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; } }