一、泛型-续
1.泛型通配符
1.1 通配符基本使用
1.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是 什么,它包含的都是Object。
2.写入list中的元素时,不行。因为我们不知道的元素类型,我们不能向其中添加对象。
3.唯一的例外是null,它是所有类型的成员。调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object
1.2 通配符例子的基础练习
package com.*; import java.util.ArrayList; import java.util.List; public class Test1 { public static void main(String[] args) { // ?表示未知类型,允许在new类型时给一个确定的类型 List<?> list = new ArrayList<String>(); List<?> list1 = new ArrayList<Integer>(); // 添加数据,因为?表示未知类型,添加具体数据的时候不允许添加 // 如果允许添加具体数据,那么字符串添加“ABC”是否允许 //报错 // 如果允许,list集合类型是Integer还是String // 如果不允许添加“ABC”,那么为什么能添加3,就没有道理了 // list.add(3); // 唯一能添加的是null,因为null可以赋值给任何类型 list.add(null); // 获取元素 // 增强for循环 for(int i = 0;i <list1.size();i++){ // 获取list<?>是允许的,因为不管是什么类型,肯定是Object的子类 Object o = list1.get(i); } List<String> list2 = new ArrayList<String>(); list2.add("aaa"); for (int i = 0; i < list2.size() ; i++) { System.out.println(list2.get(i)); } } } ```
1.3 通配符高级使用-受限泛型
1.3.1 受限泛型的羽语法
可以指定泛型的上限和下限
// 受限泛型 泛型的上限 只接受该类型及其子类 List<? extends String> list3 = new ArrayList<String>(); // 泛型的下限 只接受该类型及其父类,直至Object List<? super String> list4 = new ArrayList<String>();
1.3.2 例子练习
1.3.2.1 受限泛型的综合例子练习
java package com.*; import java.util.ArrayList; import java.util.List; public class Test2 { public static void main(String[] args) { List<Number> list1 = new ArrayList<>(); List<Integer> list2 = new ArrayList<>(); List<String> list3 = new ArrayList<>(); List<Object> list4 = new ArrayList<>(); getElement1(list1);//允许的 getElement1(list2);//允许的,因为Integer是Number的子类 // 不允许,编译报错,因为String不是Number的子类 // getElement1(list3); // 编译报错,因为Object也不是Number的子类 // getElement1(list4); // 允许的 getElement2(list1); // 不允许的,编译报错,因为Integer不是Number的父类 // getElement2(list2); // 编译报错,因为String不是Number的父类 // getElement2(list3); // 编译成功,因为Object是Number的父类 getElement2(list4); } // lists的泛型类型是Number或Number的子类或者子孙类,泛型的上限 public static void getElement1(List<? extends Number> lists){ } // list的泛型类型是Number或Number的父类,直至Object,泛型的下限 public static void getElement2(List<? super Number> lists){ } }
1.3.2.1 泛型上限的例子练习
java package com.*; public class Person<T> { // 定义成员变量 private T val; public T getVal() { return val; } public void setVal(T val) { this.val = val; } // Person<? extends Number>的泛型的类型是Number或者Number的子孙类 public static void show1(Person<? extends Number> p){ System.out.println(p); } // 无意义,只能放String,没有String的子类 // Final虽然是String的子类,但不能被继承 // 语法规则正确,但是无意义 public static void show3(Person<? extends String> p){ System.out.println(p); } // Person<? super String>的泛型的类型是String或者String的父类 public static void show2(Person<? super String> p){ System.out.println(p); } //测试 public static void main(String[] args) { Person<Integer> p1 = new Person<>(); p1.setVal(1); Person<Double> p2 = new Person<>(); p2.setVal(3.14); Person<String> p3 = new Person<>(); p3.setVal("007"); Person.show1(p1); // 编译报错,p1的泛型类型是Integer,不是String的父类 // Person.show2(p1); Person.show1(p2); // 编译报错,p2的泛型类型是Double,不是String的父类 // Person.show2(p2); // 编译报错,p3的泛型类型是String,不是Number的子孙类 // Person.show1(p3); Person.show2(p3); Person<Object> p4 = new Person<>(); p4.setVal(new Object()); // 编译报错,p4的泛型类型是Object,不是Number的子孙类 // Person.show1(p4); // 允许,p4的泛型类型是Object,是String的父类 Person.show2(p4); } }
2.类型擦除
2.1 类型擦除概念
泛型是java1.5版本才引进的概念,在这之前没有泛型,但是,泛型代码能够很好的和之前版本的代码兼 容,原因是泛型只存在代码编译阶段,在进入JVM 之前,泛型相关的信息会被擦除掉,我们称之为–类型擦除。
原始类型就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相 应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。
2.2 类型擦除例子练习
2.2.1 类型擦除例子基础练习一
package com.*; import java.util.ArrayList; import java.util.List; public class Test3 { public static void main(String[] args) { List<Integer> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); // true,说明list1与list2的类型一致,说明Integer与String消失了 System.out.println(list1.getClass() == list2.getClass()); } }
2.2.2 类型擦除例子基础练习二
package com.*; import java.util.List; public class Erasure { // 参数列表类型或数量不同可以实现重载 // List<Integer> a和List<String> a会在成功编译之后擦除掉泛型类型,转换为基本类型Object,所以两个方法的参数列表类型是相同的,不能构成重载 // public void test(List<Integer> a){ // // } // public void test(List<String> a){ // // } public static void main(String[] args) { /* GT<Integer> gt1 = new GT<>(); // 为gt1.var赋值为1 gt1.var = 1; GT<String> gt2 = new GT<>(); System.out.println(gt1.var); System.out.println("------------"); gt2.var = 2; // gt1和gt2共享val值 System.out.println("gt2.val:"+gt2.var); System.out.println("get1.val:"+gt1.var); */ } } class GT<E>{ // 静态变量 public static int var = 8; // 方法 TODO } 对应运行结果: 1 ------------ gt2.val:2 get1.val:2
2.3 类型擦除特征
1.所有泛型类的类型参数在编译时都会被擦除,虚拟机运行时中没有泛型,只有普通类和普通方法, 从这一点上来说,Java中的泛型从某种程度上是一种语法糖。
2.Java泛型不支持基本类型 例如: short int double等。3.在泛型代码内部,无法获得任何有关泛型参数类型的信息,如果传入的类型参数为T,那么在泛型
4.代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了。5.创建泛型对象时请指明类型,让编译器尽早的做参数检查。
6.不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。
7.Java的泛型类型不能用于new构建对象(也不能用于初始化数组)。泛型不能用于显性地引用运行时类型的操作之中,例如转型,instanceof和new操作(包括new一个对象,new一个数组),因为所有关于参数的类型信息都在运行时丢失了,所以任何在运行时需要获取类型信息的操作都无法进行工作。
二、反射
2.1 反射的概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。
2.2 反射的作用
动态获取类的信息,进一步实现需要的功能。
2.3 反射的相关的类
1.Class 类型
2.Constructor 构造方法
3.Method 方法
4.Field 属性
除了Class外,其他类都位于java.lang.reflect包中
可见,反射API将类的类型、方法、属性都封装成了类
其中最重要的类是Class,可以说,反射的使用都是从Class开始
2.3 反射的使用案例
2.3.1 获取Class
2.3.1.1 方法
2.3.1.2 案例
package com.*.fanxing; public class TestPersonClass { public static void main(String[] args) throws ClassNotFoundException { // 获取class对象的方式一 // 创建Person对象 Person p = new Person(); Class<? extends Person> cla1 = p.getClass(); System.out.println(cla1); // 方式二 getsuperclass Class superClass = cla1.getSuperclass(); System.out.println(superClass); // 方式三Class.forname Class cla2 = Class.forName("com.*.fanxing.Person"); System.out.println(cla2); // 代码能编译通过,但会运行报错,因为没有AA这个类 // String ClassName ="com.*.fanxing.AA"; // Class clas3 =Class.forName(ClassName); // System.out.println(clas3); // 方式四通过类名.class Class cla = Person.class; System.out.println(cla); Class cla4 = Integer.class; System.out.println(cla4); Class cla5 = String.class; System.out.println(cla5); } }
2.3.2 获取Class的基本信息
2.3.2.1 方法
2.3.2.2 案例
java package com.*.fanxing; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class TestPerson { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException { // 获取class对象 Class cla = Class.forName("com.ysu.fanxing.Person"); System.out.println(cla); // 获取所有public的属性 System.out.println("获取所有public的属性"); Field[] fields = cla.getFields(); for (Field field:fields) { System.out.println(field); } // 获取所有定义的属性 System.out.println("获取所有定义的属性"); Field[] fielde = cla.getDeclaredFields(); for (Field field:fielde) { System.out.println(field); } // 获取指定属性 System.out.println("根据属性名称获取指定属性"); // getDeclaredField获取定义指定的属性(根据属性名称) Field f = cla.getDeclaredField("name"); System.out.println(f); // 获取共有方法 System.out.println("获取所有的public方法,包含父类,父类的父类等等的方法"); Method[] m = cla.getMethods(); for(Method me:m){ System.out.println(me); } // 获取所有定义的方法 System.out.println("获取所有定义的方法,类本身定义的方法"); Method[] mr = cla.getDeclaredMethods(); for(Method me:mr){ System.out.println(me); } // 获取指定方法 System.out.println("根据方法名称和参数列表类型获取指定方法"); Method mt = cla.getDeclaredMethod("abc", String.class,Integer.class); System.out.println(mt); Method mb = cla.getDeclaredMethod("abc", String.class); System.out.println(mb); // 获取所有的构造方法 System.out.println("获取所有的构造方法"); Constructor[] con = cla.getConstructors(); for(Constructor c:con){ System.out.println(c); } // 获取指定的构造方法 System.out.println("获取指定的定义的构造方法"); Constructor c = cla.getConstructor(String.class,Integer.class); System.out.println(c); // 动态创建对象 System.out.println("动态创建Person对象"); // 编译期间不依赖Person,如果没有定义Person,编译能通过 // 调用Person无参构造方法 Object o = cla.newInstance(); System.out.println(o); // 静态创建Person对象,编译时依赖定义Person,如果没有定义Person,编译不能通过 // Person pe =new Person(); System.out.println("获取类的包名"); Package pa = cla.getPackage(); System.out.println(pa); } }
2.4 反射的详解
2.4.1 Constructor类的使用
2.4.1.1 使用实例
package com.*.fanxing; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class PersonConstructor { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { String classname = "com.*.fanxing.Person"; // test1(); test2(classname); } private static void test1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // 获取Person的Class对象 Class<?> cla = Class.forName("com.*.fanxing.Person"); // 通过Class对象获取指定的Constructor的构造方法 Constructor<?> cl = cla.getDeclaredConstructor(String.class,Integer.class); // 调用Constructor的newInstance方法调用Person的构造方法动态的创建Person的实例对象 // 传实参 // o指的是Person对象 Object o = cl.newInstance("cla",18); // 打印Person对象 System.out.println(o); } private static void test2(String classname) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException { // 根据ClassName获取Class对象 Class<?> cla = Class.forName(classname); // 获取定义的所有构造方法 Constructor<?>[] con = cla.getDeclaredConstructors(); // 遍历所有的构造方法,处理每一个构造方法 for(Constructor<?> c:con){ // 判断con有几个参数,不同的参数数量处理逻辑不同 // 获取构造方法参数的数量 int count = c.getParameterCount(); switch (count){ case 0:{ Object o = c.newInstance(); System.out.println("使用无参的构造方法构建对象"); System.out.println(o); break; } case 1:{ Class<?>[] pa = c.getParameterTypes(); if(pa[0].equals(String.class)){ Object o = c.newInstance("jack"); System.out.println("使用1个参数的构造方法构建对象"); System.out.println(o); }else if(pa[0].equals(Integer.class)){ Object o = c.newInstance(10); System.out.println("使用1个参数的构造方法构建对象"); System.out.println(o); } break; } case 2:{ // 获取构造方法的参数类型列表 Class<?>[] pa = c.getParameterTypes(); if(pa[0].equals(String.class)&&pa[1].equals(Integer.class)){ Object o = c.newInstance("jack",18); System.out.println("使用2个参数(第一个参数是String)的构造方法构建对象"); System.out.println(o); }else if(pa[0].equals(Integer.class)&&pa[1].equals(String.class)){ Object o = c.newInstance(10,"lock"); System.out.println("使用2个参数(第一个参数是Integer)的构造方法构建对象"); System.out.println(o); } break; } default:{ System.out.println("构造方法超过2个以上,未有处理逻辑"); } } } } }