11.1 枚举
Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一个年的 12 个月份,一个星期的 7 天,方向有东南西北等。Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。只有枚举中包含的值可以使用,其他的值都不可使用。
1.使用枚举类型设置常量
过去设置常量,通常将常量放置在接口中,这这样在程序中就可以直接使用,并且该常量不能被修改,因为在接口中定义常量时,该常量的修饰符为final与static。例如,在项目中创建 Constants接口,在接口中定义常量的常规规方式。
在JDK 1.5版本中新增枚举类型后就逐渐取代了这种常量定义方式,因为通过使用枚举类型,可以赋予程序在编译时进行检查的功能。使用枚举类型定义常量的语法如下:
package 第十一章;
interface Constants { // 将常量放置在接口中
public static final int Constants_A = 1;
public static final int Constants_B = 12;
}
public class ConstantsTest {
enum Constants2 { // 将常量放置在枚举类型中
Constants_A, Constants_B
}
// 使用接口定义常量
public static void doit(int c) { // 定义一个方法,这里的参数为int型
switch (c) { // 根据常量的值做不同操作
case Constants.Constants_A:
System.out.println("doit() Constants_A");
break;
case Constants.Constants_B:
System.out.println("doit() Constants_B");
break;
}
}
public static void doit2(Constants2 c) { // 定义一个参数对象是枚举类型的方法
switch (c) { // 根据枚举类型对象做不同操作
case Constants_A:
System.out.println("doit2() Constants_A");
break;
case Constants_B:
System.out.println("doit2() Constants_B");
break;
}
}
public static void main(String[] args) {
ConstantsTest.doit(Constants.Constants_A); // 使用接口中定义的常量
ConstantsTest.doit2(Constants2.Constants_A); // 使用枚举类型中的常量
ConstantsTest.doit2(Constants2.Constants_B); // 使用枚举类型中的常量
ConstantsTest.doit(3);
// ConstantsTest.doit2(3);
}
}
2.深入了解枚举类型
操作枚举类型成员的方法
枚举类型较传统定义常量的方式,除了具有参数类型检测的优势之外,还具有其他方面的优势。用户可以将一个枚举类型看作是一个类,它继承于java.lang.Enum类,当定义一个枚举类型时,每一个枚举类型成员都可以看作是枚举类型的一个 实例,这些枚举类型成员都默认被final、public、static 修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。
由于枚举类型对象继承于java.langEnum 类, 所以该类中一些操作枚举类型的方法都可以应用到枚举类型中。
枚举类类型的常用方法
values():枚举类型实例包含一个values()方法,该方法可以将枚举类型成员以数组的形式返回。
import static java.lang.System.out;
public class ShowEnum {
enum Constants2 { // 将常量放置在枚举类型中
Constants_A, Constants_B
}
// 循环由values()方法返回的数组
public static void main(String[] args) {
for (int i = 0; i < Constants2.values().length; i++) {
// 将枚举成员变量打印
out.println("枚举类型成员变量:" + Constants2.values()[i]);
}
}
}
valueOf() 与 compareTo():枚举类型中静态方法valueOf()可以将普通字符串转换为枚举类型,而compareTo()方法用于比较两个枚举类型成员定义时的顺序。调用 comparo eTo()方法时,如果方法中参数在调用该方法的枚举对象位置之前,则返回正整数;如果两个互相比较的枚举成员的位置相同,则返回 0;如果方法中参数在调用该方法的枚举对象位置之后,则返回负整数。
package 第十一章;
import static java.lang.System.out;
public class EnumMethodTest {
enum Constants2 { // 将常量放置在枚举类型中
Constants_A, Constants_B, Constants_C, Constants_D
}
// 定义比较枚举类型方法,参数类型为枚举类型
public static void compare(Constants2 c) {
// 根据values()方法返回的数组做循环操作
for (int i = 0; i < Constants2.values().length; i++) {
// 将比较结果返回
out.println(c + "与" + Constants2.values()[i] + "的比较结果为:" + c.compareTo(Constants2.values()[i]));
}
}
// 在主方法中调用compare()方法
public static void main(String[] args) {
compare(Constants2.valueOf("Constants_B"));
}
}
ordinal():枚举类型中的ordinal()方法用于获取某个枚举对象的位置索引值。
package 第十一章;
public class EnumIndexTest {
enum Constants2 { // 将常量放置在枚举类型中
Constants_A, Constants_B, Constants_C
}
public static void main(String[] args) {
for (int i = 0; i < Constants2.values().length; i++) {
// 在循环中获取枚举类型成员的索引位置
System.out.println(Constants2.values()[i] + "在枚举类型中位置索引值" + Constants2.values()[i].ordinal());
}
}
}
枚举类型中的构造方法
在枚举类型中,可以添加构造方法,但是规定这个构造方法(无论是有参还是无参构造方法)必须是private 修饰符或者默认修饰符所修饰的。枚举类型定义的构造方法语法如下:
public enum Constants2{
Constants_A("我是枚举成员A"),
Constants_B("我是枚举成员B"),
Constants_C("我是枚举成员C"),
Constants_D(3);
String description;
int i;
private Constants2(){ //定义默认构造方法
}
//定义带参数的构造方法,参数类型为字符串型
private Constants2(String description){
this.description=description;
private Constants2(int i){//定义带参数的构造方法,参数类型为整型
this.i=this.i+i;
}
}
package 第十一章;
import static java.lang.System.out;
public class EnumConTest {
enum Constants2 { // 将常量放置在枚举类型中
Constants_A("我是枚举成员A"), // 定义带参数的枚举类型成员
Constants_B("我是枚举成员B"),
Constants_C("我是枚举成员C"),
Constants_D(3);
private String description;
private int i = 4;
private Constants2() {
}
// 定义参数为String型的构造方法
private Constants2(String description) {
this.description = description;
}
// 定义参数为int型的构造方法
private Constants2(int i) {
this.i = this.i + i;
}
// 获取description的值
public String getDescription() {
return description;
}
// 获取i的值
public int getI() {
return i;
}
}
public static void main(String[] args) {
for (int i = 0; i < Constants2.values().length; i++) {
out.println(Constants2.values()[i] + "调用getDescription()方法为:" + Constants2.values()[i].getDescription());
}
out.println(Constants2.valueOf("Constants_D") + "调用getI()方法为:" + Constants2.valueOf("Constants_D").getI());
}
}
package eleven06;
import static java.lang.System.out;
public enum AnyEnum implements EnumInterface {
Constants_A { // 可以在枚举类型成员内部设置方法
public String getDescription() {
return ("我是枚举成员A");
}
public int getI() {
return i;
}
},
Constants_B {
public String getDescription() {
return ("我是枚举成员B");
}
public int getI() {
return i;
}
},
Constants_C {
public String getDescription() {
return ("我是枚举成员C");
}
public int getI() {
return i;
}
},
Constants_D {
public String getDescription() {
return ("我是枚举成员D");
}
public int getI() {
return i;
}
};
private static int i = 5;
public static void main(String[] args) {
for (int i = 0; i < AnyEnum.values().length; i++) {
out.println(AnyEnum.values()[i] + "调用getDescription()方法为:" + AnyEnum.values()[i].getDescription());
out.println(AnyEnum.values()[i] + "调用getI()方法为:" + AnyEnum.values()[i].getI());
}
}
}
3.使用枚举类型的优势
枚举类型声明提供了一种用户友好的变量定义方法,枚举了某种数据类型所有可能出现的值。总结枚举类型,它具有以下特点:
(1)类型安全。
(2)紧凑有效的数据定义。
(3)可以和程序其他部分完美交互。(4)运行效率高
11.2 泛型
1.回顾“向上转型”与“向下转型”
package 第十一章;
public class Test {
private Object b; // 定义Object类型成员变量
public Object getB() { // 设置相应的getXXX()方法
return b;
}
public void setB(Object b) { // 设置相应的setXXX()方法
this.b = b;
}
public static void main(String[] args) {
Test t = new Test();
t.setB(new Boolean(true)); // 向上转型操作
System.out.println(t.getB());
t.setB(new Float(12.3));
Float f = (Float) (t.getB()); // 向下转型操作
System.out.println(f);
}
}
2.定义泛型类
Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与返回的值都以Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生ClassCastException异常。
package 第十一章;
public class OverClass<T> { // 定义泛型类
private T over; // 定义泛型成员变量
public T getOver() { // 设置相应的getXXX()方法
return over;
}
public void setOver(T over) { // 设置相应的setXXX()方法
this.over = over;
}
public static void main(String[] args) {
//实例化一个Boolean型的对象
OverClass<Boolean> over1 = new OverClass<Boolean>();
//实例化一个Float型的对象
OverClass<Float> over2 = new OverClass<Float>();
over1.setOver(true);
over2.setOver(12.3f);
Boolean b = over1.getOver();
Float f = over2.getOver();
System.out.println(b);
System.out.println(f);
}
}
3.泛型的常规用法
定义泛型类时声明多个类型
在定义泛型类时,可以声明多个类型。语法如下:
MutiOverClass<T1,T2>
MutiOverClass:泛型类名称
其中,T1 和 T2 为可能被定义的类型。这样在实例化指定类型的对象时就可以指定多个类型。
MutiOverClass<Boolean,Float>=new Mutid OverClass<Boolean,Float>();
定义泛型类时也可以声明数组类型,下面的实例中定义泛型时便声明了数组类型。
package 第十一章;
public class ArrayClass<T> {
private T[] array; // 定义泛型数组
public void SetT(T[] array) { // 设置SetXXX()方法为成员数组赋值
this.array = array;
}
public T[] getT() { // 获取成员数组
return array;
}
public static void main(String[] args) {
ArrayClass<String> a = new ArrayClass<String>();
String[] array = { "成员1", "成员2", "成员3", "成员4", "成员5" };
a.SetT(array); // 调用SetT()方法
for (int i = 0; i < a.getT().length; i++) {
System.out.println(a.getT()[i]); // 调用getT()方法返回数组中的值
}
}
}
集合类声明容器的元素
实际应用中,通过在集合类中应用泛型可以使集合类中的元素类型保证唯一性,这样在运行时就不会产生ClassCastException异常,提高了代码的安全性和可维护性。可以使用K和V两个字符代表容器中的键值和与键值相对应的具体值。
package 第十一章;
import java.util.*;
public class MutiOverClass<K, V> {
public Map<K, V> m = new HashMap<K, V>(); // 定义一个集合HashMap实例
// 设置put()方法,将对应的键值与键名存入集合对象中
public void put(K k, V v) {
m.put(k, v);
}
public V get(K k) { // 根据键名获取键值
return m.get(k);
}
public static void main(String[] args) {
// 实例化泛型类对象
MutiOverClass<Integer, String> mu = new MutiOverClass<Integer, String>();
for (int i = 0; i < 5; i++) {
// 根据集合的长度循环将键名与具体值放入集合中
mu.put(i, "我是集合成员" + i);
}
for (int i = 0; i < mu.m.size(); i++) {
// 调用get()方法获取集合中的值
System.out.println(mu.get(i));
}
}
}
package 第十一章;
import java.util.*;
public class ListClass {
public static void main(String[] args) {
// 定义ArrayList容器,设置容器内的值类型为Integer
List<Integer> a = new ArrayList<Integer>();
a.add(1); // 为容器添加新值
for (int i = 0; i < a.size(); i++) {
// 根据容器的长度循环显示容器内的值
System.out.println("获取ArrayList容器的值:" + a.get(i));
}
// 定义Map容器,设置容器的键名与键值类型分别为Integer与String型
Map<Integer, String> m = new HashMap<Integer, String>();
for (int i = 0; i < 5; i++) {
m.put(i, "成员" + i); // 为容器填充键名与键值
}
for (int i = 0; i < m.size(); i++) {
// 根据键名获取键值
System.out.println("获取Map容器的值" + m.get(i));
}
// 定义set容器,使容器中的内容为Character型
Set<Character> set = new HashSet<Character>();
//为容器添加新值
set.add('一');
set.add('二');
//使用foreach循环,将set中元素按照Character类型进行循环遍历
for (Character c: set) {
// 显示容器中的内容
System.out.println("获取set容器的值:" + c);
}
}
}
4.泛型的高级用法
泛型的高级用法主要包括通过类型参数T的继承和通过类型通配符的继承来限制泛型类型,另
外,开发人员还可以继承泛型类或者实现泛型接 口,本节将对泛型的一些高级用法进行讲解。
通过类型参数T的继承限制泛型类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类型作了限制,这主要通过对类型参数T实现继承来体现,语法如下:
class 类名称<T extends anyClass>
package 第十一章;
import java.util.*;
public class LimitClass<T extends List> { // 限制泛型的类型
public static void main(String[] args) {
// 可以实例化已经实现List接口的类
LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
LimitClass<LinkedList> l2 = new LimitClass<LinkedList>();
// 这句是错误的,因为HashMap没有实现List()接口
LimitClass<HashMap> l3 = new LimitClass<HashMap>();
}
}
继承泛型类与实现泛型接口
定义为泛型的类和接口也可以被继承与实现。
在项目中创建一个类文件,在该类中继承泛型类。
5.泛型总结
使用泛型需遵循以下原则:
(1)泛型的类型参数只能是类类型,不可以是简单类型,如A<int>这种泛型定义就是错误的。
(2)泛型的类型个数可以是多个。
(3)可以使用extends关键字限制泛型的类类型。(4)可以使用通配符限制泛型的类型。