1.枚举类型
JDk1.5中新增了枚举类型,可以使用该功能取代以往定义常量的方式,同时枚举类型还赋予程序在编译时进行检查的功能。
1.1 使用枚举类型设置常量
以往设置常量,通常将常量放在接口中(final static 数据类型 常量名 = 常量值;),这样在程序中就可以直接使用,并且该常量不能被修改,因为在接口中定义常量时,该常量的修饰符为final与static,常规定义如下:
package enumeration.test; public interface RuleConstant { //接口中声明常量 final static int Constants_A = 1; final static int Constants_B = 12; }
在JDK1.5版本新增枚举类型后就逐渐取代了这种常量声明方式,使用枚举类型定义常量的语法如下:
package enumeration.test; public enum Constants { Constants_A, Constants_B, Constants_C }
下面我们通过一个例子来看一下常规类型和枚举类型的使用:
package enumeration.test; public class EnumTest { //使用接口定义常量 public static void doit(int c){ switch (c) { case RuleConstant.Constants_A: System.out.println("接口常量Constants_A : doit()"); break; case RuleConstant.Constants_B: System.out.println("接口常量Constants_B : doit()"); break; default: System.out.println("default!"); break; } } //使用枚举类型定义常量--枚举类型参数约束了只能传入枚举类中的常量 public static void doit2(Constants c){ switch (c) { case Constants_A: System.out.println("枚举类型_Constants_A"); break; case Constants_B: System.out.println("枚举类型_Constants_B"); break; case Constants_C: System.out.println("枚举类型_Constants_C"); break; default: System.out.println("default!"); break; } } public static void main(String[] args) { EnumTest.doit(RuleConstant.Constants_A);//接口常量 EnumTest.doit2(Constants.Constants_B);//枚举类型 EnumTest.doit(3);//参数类型不是枚举类型,那么传入参数就没有严格限制,不可以是枚举,可以是接口常量,也可以是变量 // EnumTest.doit(Constants.Constants_C);如果一个函数参数类型没有定义为枚举,那么传入枚举,编译不容过 } }
上面代码doit()函数,即便编译器不接受在接口中定义的常量参数,也不会报错,仍然正常运行;但调用doit2()方法,任意传递参数,编译器就会报错,这个函数定义了只接受枚举常量作为参数;
枚举类型除了可以在类的外部单独进行定义,还可以在类中进行定义,如同内部类一样:
package enumeration.test; public class EnumDemo{ enum Constants{ Constants_A, Constants_B, Constatns_C } }
1.2 深入理解枚举类型
1.2.1 操作枚举类型成员的方法
package enumeration.test; import org.junit.Test; public class EnumMethodsTest { //values()该方法可以将枚举类型成员实例以数组方式返回; public static void valuesMethod(){ for (int i = 0; i < Constants.values().length; i++) { System.out.println("Constants枚举成员:"+Constants.values()[i]); } } @Test public void testValuesMethod(){ valuesMethod(); } //valueOf()将普通字符串转换为枚举实例 public static void valueOfMethod(String str){ //将字符串转换为枚举实例,不代表可以把一个字符串变成枚举类型,只是意味着,参数被设置成了枚举类型,等同于与ccalueOfMethod(Constants c) Constants constants = Constants.valueOf(str); switch (constants) { case Constants_A: System.out.println("Constants.Constants_A"); break; case Constants_B: System.out.println("Constants.Constants_B"); break; case Constants_C: System.out.println("Constants.Constants_C"); break; default: System.out.println("default"); break; } } @Test public void testValueOfMethod(){ valueOfMethod("Constants_B"); } //campareTo(),正数代表之前,负数代表后,0代表位置相同 public static void compareToMethod(){ System.out.println("Constants_A与Constants_B相比较:"+Constants.Constants_A.compareTo(Constants.Constants_B)); System.out.println("Constants_B与Constants_A相比较:"+Constants.Constants_B.compareTo(Constants.Constants_A)); System.out.println("Constants_A与Constants_A相比较:"+Constants.Constants_A.compareTo(Constants.Constants_A)); } @Test public void tesCompareToMethod(){ compareToMethod(); } //ordinal()用于获取某个枚举对象的索引位置 public void ordinalMethod(){ System.out.println("Constants_A的索引位置:"+Constants.Constants_A.ordinal()); System.out.println("Constants_B的索引位置:"+Constants.Constants_B.ordinal()); } @Test public void testordinalMethod(){ ordinalMethod(); } }
1.2.2 枚举类型中的构造函数
在枚举类型中,可以添加构造方法,但是规定构造函数必须为private修饰符所修饰,枚举类型的构造函数的定义如下:
package enumeration.constructor; import org.junit.Test; public class EnumConstructor { enum Constants{ Constants_A("我是枚举类型A"), Constants_B("我是枚举类型B"), Constants_C("我是枚举类型C"), Constants_D(3); private String description; private int i = 4; //无参构造函数 private Constants(){ } //有参构造 private Constants(String description){ this.description = description; } //有参构造 private Constants(int i){ this.i = this.i+i; } //get(),set() public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public int getI() { return i; } public void setI(int i) { this.i = i; } } //构造函数方法 public void constructorMethod(){ for (int i = 0; i < Constants.values().length; i++) { System.out.println(Constants.values()[i]+"调用getDescription()"+Constants.values()[i].getDescription()); } System.out.println(Constants.valueOf("Constants_D")+"调用getI()"+Constants.valueOf("Constants_D").getI()); } @Test public void testConstructorMethod(){ constructorMethod(); } }
package enumeration.constructor; public interface Test { public String getDescription(); public int getI(); }
package enumeration.constructor; public enum AnyEnum implements Test{ 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 constructorMethod(){ for (int i = 0; i < AnyEnum.values().length; i++) { System.out.println(AnyEnum.values()[i]+"调用getDescription()"+AnyEnum.values()[i].getDescription()); System.out.println(AnyEnum.values()[i]+"调用getI()"+AnyEnum.values()[i].getI()); } } public static void main(String[] args) { constructorMethod(); } }
1.3 使用枚举类型的优势
泛型
使用泛型避免了ClassCastException异常;
2.1 回顾"向上转型"和"向下转型"
例如:我们在数组中只能存储同一类型元素,如果存储了其他类型元素,那么编译器会报错,而集合中则可以存储多种类型元素,我们遍历集合时,会把集合中的元素进行转型(众所周知迭代器遍历集合元素返回Object),此时可能会出现ClassCastException异常,此时是运行时报错,那么我们可不可以让集合中也只存储一种类型元素,当存储其他类型元素时编译器报错,将运行时异常转变为编译时异常呢?此时就引出来了泛型,泛型是JDK1.5版本中出现的, 在类或者接口的后面有一对<> , 用来约束元素的数据类型的泛型的表现形式:
* <E>
* <T>
* <QQ>
2.2 定义泛型类
其中T代表一个数据类型的名称;
利用泛型类将上面的例17.9改写:
package genericity.test; import org.junit.Test; //改写例17.9 public class OverClass<T> { private T over; public T getOver() { return over; } public void setOver(T over) { this.over = over; } public static void main(String[] args) { //实例一个Boolean型的对象 OverClass<Boolean> overClass1 = new OverClass<Boolean>(); //实例化一个Float型的对象 OverClass<Float> overClass2 = new OverClass<Float>(); overClass1.setOver(true);//无需进行类型转换 //泛型会自动检验传入的参数是否符合规矩 overClass2.setOver(12.3f); System.out.println(overClass1.getOver()); System.out.println(overClass2.getOver()); } }
泛型类中可以设置非泛型的成员变量;
泛型将运行时的异常转换为了编译时的异常;
2.3 泛型的常规用法
2.3.1 定义泛型类时声明多个类型
2.3.2 定义泛型类时声明数组类性
定义泛型类时也可以声明数组类型:
package genericity.test; public class ArrayClass<T> { private T[] array; public T[] getArray() { return array; } public void setArray(T[] array) { this.array = array; } public static void main(String[] args) { ArrayClass<String> a = new ArrayClass<String>(); String[] array = {"1","2","3","4","5"}; a.setArray(array); for (int i = 0; i < array.length; i++) { System.out.println(a.getArray()[i]); } } }
1
2
3
4
5
2.3.3 集合类声明容器的元素
2.4 泛型的高级用法
泛型的高级用法包括限制泛型可用类型、使用类型通配符等。
2.4.1 限制泛型可用的类型
2.4.2 使用类型通配符
2.4.3 继承泛型类和实现泛型接口
2.5 泛型总结
* 泛型的好处:
* a: 避免 操作隐患ClassCastException
* b: 把运行时异常 转换为 编译时异常
* c: 避免了 强制转换的操作
2.6 泛型的练习
package collection.List.genericity; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; /* * 用ArrayList 存储 3个字符串,并遍历 * * 发现当集合中存储多种数据类型的元素的时候, 可能会有操作隐患ClassCastException(转换异常) * 怎么解决该问题呢? * 回想以前学习的数组, 数组中存储的都是同一种数据类型的元素,如果存储了 其他类型的数据,会编译错误 * String[] arr = new String[4]; * arr[0] = "JavaSE"; * arr[1] = "JavaEE"; * arr[2] = "Android"; * arr[3] = 20; * * 我们也希望,我们可以将集合也只存储同一种数据类型的数据,如果存储了其他类型的数据,给出编译错误, * 这种技术如何来实现呢? java提供了一个技术, 泛型。 * * 泛型: 在类或者接口的后面有一对<> , 用来约束元素的数据类型的 * * 泛型的表现形式: * <E> * <T> * <QQ> * * 泛型怎么使用呢? * 查看API,发现类或者接口有<>, 如果有泛型,那么必须要使用,可以避免 操作隐患ClassCastException * * <>里面 用来指定元素的数据类型 * * 泛型的好处: * a: 避免 操作隐患ClassCastException * b: 把运行时异常 转换为 编译时异常 * c: 避免了 强制转换的操作 */ public class ArrayListDemo { public static void main(String[] args) { // 创建ArrayList集合,使用泛型约束集合元素类型为String ArrayList<String> arr = new ArrayList<>(); arr.add("成不成1!"); arr.add("成不成2!"); arr.add("成不成3!"); //arr.add(20);当我们使用泛型后,集合中就只能出现允许的元素,其他类型元素编译时会报错 //通过所以直接获取指定索引元素 /*ListIterator<String> liter = arr.listIterator(0); Object object = liter.next(); System.out.println(object.toString());*/ //遍历 ListIterator<String> liter = arr.listIterator(); while (liter.hasNext()) { String string = liter.next(); System.out.println(string); } //遍历 // Iterator<String> litera = arr.iterator(); // while (litera.hasNext()) { // //类型转换 // String str = (String) litera.next(); // System.out.println(str); // } } }
package collection.List.genericity; import java.util.Enumeration; import java.util.Vector; /* * 使用Vector集合 存储字符串对象,并遍历 (使用泛型) */ public class VectorDemo { public static void main(String[] args) { // 创建Vector集合并遍历集合 Vector<String> vec = new Vector<>(); vec.add("伤不起,"); vec.add("啊"); vec.add("真的真的伤不起"); //将元素添加到指定位置 vec.add(2, "伤不起"); Enumeration<String> enu = vec.elements(); while (enu.hasMoreElements()) { String string = (String) enu.nextElement(); System.out.println(string); } } }
2.7 泛型类与泛型方法
在上面我们看到了jdk提供的一些泛型,那么在现实开发中,我们可不可以进一步对泛型加以利用呢?答案是肯定的
package collection.List.genericity; /* * 泛型类: 类上有泛型修饰, 就是泛型类 */ public class Tool<QQ> { public void show1(){ System.out.println("haha"); } public void show2(String str){ System.out.println(str); } public void show3(Integer i){ System.out.println(i); } public void show4(QQ qq){ System.out.println(qq); } }
package collection.List.genericity; /* * 泛型方法: 把泛型定义在方法上,在Tool类上,我们发现可以把类定义成泛型类,那么可不可以定义泛型方法呢 */ //public class Tool2<QQ> { // public void show (QQ qq) { // System.out.println(qq); // } // // //不想是QQ类型 // public void method(TT tt){ // // } //} public class ToolMethod { //TT类型 public<TT> void Method(TT tt){ System.out.println(tt.toString()); } }
package collection.List.genericity; public interface ITool<QQ> { //接口的方法都是抽象方法,抽象方法没有实现体 public abstract void method(QQ qq); }
package collection.List.genericity; //实现 implements /* * 泛型接口 * * 创建实现类的时候,明确泛型的数据类型 * 创建实现类的时候,没有明确泛型的数据类型的时候,需要在类上也给出泛型 */ //public class Tool3Imlp implements Tool3<String>{ // // @Override // public void show(String qq) { // System.out.println(qq); // } //} public class ToolImple<QQ> implements ITool<QQ>{ @Override public void method(QQ qq) { System.out.println(qq.toString()); } }
package collection.List.genericity; public class ToolTest { public static void main(String[] args) { //泛型类 Tool<String> tool = new Tool<>(); String str = "左宗棠"; tool.show1(); tool.show2(str); tool.show3(7); tool.show4(str); //tool.show4(7);泛型类,约束了参数类型只能是String类型 //tool.show4(tool); //泛型方法 ToolMethod tm = new ToolMethod(); tm.<String>Method(str); //tm.<String>Method(7);泛型方法决定了实参类型 //泛型接口 ITool<String> it = new ToolImple<>(); it.method(str); //it.method(7);泛型决定了传入参数类型 } }
package cn.itcast.demo1; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class Demo1 { /* * 泛型方法有自己的类型变量 * 泛型方法中的类型变量声明,必须在返回值之前! * * 通常泛型方法的返回值和参数中都会使用类型变量 * * 泛型方法中声明的类型变量只能在当前方法内使用,其他方法不能使用! * * 通过调用泛型方法时,不用显式的传递类型变量,而是通过参数类型隐式传递 */ public static <T> T get(T[] array) { return array[array.length / 2]; } @Test public void fun3() { String[] strs = {"hello", "world", "java", "zhangSan", "liSi"}; // 显式为泛型方法的类型变量赋值 String s1 = Demo1.<String>get(strs); System.out.println(s1); // 隐式为泛型方法的类型变量赋值 String s = get(strs);//等同与给T赋值了,赋的是String System.out.println(s); } @Test public void fun1() { List<String> arr = new ArrayList<String>(); arr.add("hello"); String s = arr.get(0); System.out.println(s); } @Test public void fun2() { A<String> a = new A<String>(); A<Integer> a2 = new A<Integer>(); } } /* * 泛型类,又叫参数化类型(A<T>) */ class A<T> { private T t; /* * 它是泛型类的方法,不是泛型方法 */ public T getT() { return t; } public void setT(T t) { T bean = t; this.t = t; } // 泛型类中,类型变量只有static方法中不能使用 // public static void fun() { // T bean1; // } }
package cn.itcast.demo2; import java.util.Comparator; /* * 泛型的继承 */ public class Demo1 { public void fun1() { AA1 aa1 = new AA1(); AA2<String> aa2 = new AA2<String>(); AA3<Long> aa3 = new AA3<Long>(); } } class A<T> { private T bean; public T getBean() { return bean; } public void setBean(T bean) { this.bean = bean; } } /* * 如果父类是泛型类,那么子类需要在继承时给父类传递变量类型! */ class AA1 extends A<String> { } class AA2<T> extends A<Integer> { } class AA3<T> extends A<T> { } class MyComparator implements Comparator<String> { @Override public int compare(String o1, String o2) { // TODO Auto-generated method stub return 0; } }