大纲:
Class类的使用:
三种获得Class类的实例的方法;
基本数据类型对应的类类型;
Class类的基本API:获取类的常用(全部)信息;
Java类加载机制:
编译时加载(静态加载);
运行时加载(动态加载);
方法的反射:
成员变量的反射:
构造函数的反射:
通过反射了解集合泛型的本质:
在面向对象的世界里,万事万物皆对象;但其实静态的成员、普通的数据类型不是对象,但我们最好看做万事万物皆对象;类也是对象,类是java.lang.Class类的实例对象;任何一个类都是Clss类的实例对象;
三种获得Class类的实例的方法:
1 /** 2 * Class类的使用:Class类的实例对象的三种表示方法 3 */ 4 package reflect; 5 6 public class ClassTest01 7 { 8 public static void main(String[] args) 9 { 10 // Foo类的实例对象:foo 11 Foo foo = new Foo(); 12 13 // Foo这个类也是一个实例对象,是java.lang.Class类的实例对象 14 // 任何一个类都是Class类的实例对象,这个实例对象有三种表示方法 15 16 // 第一种表示方法: 17 // 实际告诉我们,任何一个类都有一个隐含的静态成员变量:class 18 Class c1 = Foo.class; 19 20 // 第二种表示方法:已知该类的实例对象,通过getClass()方法获取 21 Class c2 = foo.getClass(); 22 23 // 官网描述:c1、c2表示Foo类的类类型(class type) 24 // 如何解释:Foo类本身就是一个实例对象,是Class类的实例对象 25 // 以此区分:Foo类的实例对象为foo,Foo类的类类型为c1、c2 26 27 // c1、c2都是同一个类(Foo)的类类型,一个类只可能是Class类的一个实例对象 28 // 所以c1与c2完全相等 29 System.out.println(c1 == c2); 30 31 // 第三种表示方法: 32 Class c3 = null; 33 try 34 { 35 // 写出类的全量名 36 c3 = Class.forName("reflect.Foo"); 37 } 38 catch (ClassNotFoundException e) 39 { 40 e.printStackTrace(); 41 } 42 // c2与c3完全相等 43 System.out.println(c2 == c3); 44 45 // 可以通过一个类的类类型创建该类的实例对象 46 try 47 { 48 // 前提:要有无参的构造方法 49 Foo f1 = (Foo) c1.newInstance(); 50 // 此时可以调用类的方法了 51 f1.display(); 52 } 53 catch (InstantiationException e) 54 { 55 e.printStackTrace(); 56 } 57 catch (IllegalAccessException e) 58 { 59 e.printStackTrace(); 60 } 61 } 62 } 63 64 class Foo 65 { 66 void display() 67 { 68 System.out.println("abc"); 69 } 70 }
Class类动态加载类的用法:
Class.forName(“类的全称”); 这种写法不仅表示了类的类类型,还表示了动态加载类;
要区分编译和运行:
编译时刻加载类是静态加载类、运行时刻加载类是动态加载类;
new 创建对象:是静态加载类,在编译时刻就需要加载所有可能用到的类,不管这些类在后面是否真用到了;(通过动态加载类可以解决该问题);
静态加载类的方式:
1 /** 2 * 静态加载类的方式:通过new创建对象 3 */ 4 package reflect.static_load; 5 6 public class Office 7 { 8 public static void main(String[] args) 9 { 10 // Word类,Excel类必须都存在才能运行 11 if("Word".equals(args[0])) 12 { 13 // 通过new创建对象,是静态加载类,在编译时刻就需要加载所有可能使用到的类,不管你需不需要用;所以下面报错了,因为没有定义Word和Execl类; 14 // 现在的需求是:你想用哪个就加载哪个,哪个不用就不加载它,这就涉及到动态加载; 15 // 通过动态加载类可以解决该问题 16 Word word = new Word(); // 这里编译报错,因为没有Word类 17 word.start(); 18 } 19 20 if("Excel".equals(args[0])) 21 { 22 Excel excel = new Excel(); // 这里编译报错,因为没有Excel类 23 excel.start(); 24 } 25 } 26 }
动态加载类的方式:
1 /** 2 * 动态加载类 3 */ 4 package reflect.dynamic_load; 5 6 public class OfficeBetter 7 { 8 public static void main(String[] args) 9 { 10 // 实例一 11 try 12 { 13 // 动态加载类,在运行时刻加载;就算此时没有定义Execl类,编译也没有报错;只有在运行时才会报错 14 Class c1 = Class.forName("reflect.dynamic_load.Excel"); 15 } 16 catch (ClassNotFoundException e) 17 { 18 e.printStackTrace(); 19 } 20 21 // 实例二 22 // 动态加载类,在运行时刻加载 23 try 24 { 25 Class c2 = Class.forName("reflect.dynamic_load.Excel"); 26 // 如下直接创建具体类的类对象不合适,如果你想要的是Office类对象,但加载的可能是Excel类对象 27 Word word = (Word) c2.newInstance(); // 运行时报错,因为c2是Excel类的类类型 28 word.show(); 29 Excel excel = (Excel) c2.newInstance(); 30 excel.show(); 31 } 32 catch (ClassNotFoundException e) 33 { 34 e.printStackTrace(); 35 } 36 catch (InstantiationException e) 37 { 38 e.printStackTrace(); 39 } 40 catch (IllegalAccessException e) 41 { 42 e.printStackTrace(); 43 } 44 45 // 实例三 46 try 47 { 48 // 动态加载类,在运行时刻加载 49 Class c3 = Class.forName("reflect.dynamic_load.Excel"); 50 // 通过类类型创建具体类的实例对象 51 // 统一标准,优化实例二 52 OfficeAble oa = (OfficeAble)c3.newInstance(); 53 oa.show(); 54 } 55 catch (ClassNotFoundException e) 56 { 57 e.printStackTrace(); 58 } 59 catch (InstantiationException e) 60 { 61 e.printStackTrace(); 62 } 63 catch (IllegalAccessException e) 64 { 65 e.printStackTrace(); 66 } 67 } 68 }
1 package reflect.dynamic_load; 2 3 public interface OfficeAble 4 { 5 public void show(); 6 }
1 package reflect.dynamic_load; 2 3 public class Word implements OfficeAble 4 { 5 public void show() 6 { 7 System.out.println("word...work"); 8 } 9 }
1 package reflect.dynamic_load; 2 3 public class Excel implements OfficeAble 4 { 5 public void show() 6 { 7 System.out.println("excel...work"); 8 } 9 }
基本数据类型对应的类类型:
1 /** 2 * 基本数据类型和一些关键字都存在对应的类类型 3 */ 4 package reflect; 5 6 public class ClassTest02 7 { 8 public static void main(String[] args) 9 { 10 Class c1 = int.class; // int基本类型对应的类类型 11 Class c2 = double.class; 12 // 打印出类类型的名称 13 System.out.println(c1.getName()); // 结果:int 14 System.out.println(c2.getName()); // 结果:double 15 16 Class c3 = Double.class; // Double类的类类型 17 Class c4 = String.class; 18 System.out.println(c3.getName()); // 结果:java.lang.Double 19 System.out.println(c4.getName()); // 结果:java.lang.String 20 System.out.println(c4.getSimpleName()); // 结果:String,不带包名的类名称 21 22 // 只要是在类里面声明的返回值啊什么的基本都有对应的类类型 23 Class c5 = void.class; // void关键字对应的类类型 24 System.out.println(c5.getName()); 25 } 26 }
Class类的基本API:获取类的常用(全部)信息:
1 /** 2 * Class类的基本API:获取类的常用信息; 3 * 获取类的信息,要先获取类的类类型 4 */ 5 package reflect; 6 7 import java.lang.reflect.Constructor; 8 import java.lang.reflect.Field; 9 import java.lang.reflect.Method; 10 11 public class ClassTest03 12 { 13 public static void main(String[] args) 14 { 15 16 } 17 18 // 获取方法的信息 19 public static void getMethodsMessage(Object obj) 20 { 21 // 先获取类的类类型 22 // 因为我们有了类的对象,所以通过这种方法获取类的类类型 23 // 传递的是哪个子类的对象,获取的就是那个子类的类类型 24 Class c = obj.getClass(); 25 26 // 获取类的完整名称 27 System.out.println(c.getName()); 28 29 // 获取类的名称(不是完整的名称) 30 System.out.println(c.getSimpleName()); 31 32 // 获取类的方法 33 // 万事万物都是对象,方法也是对象 34 // 一个成员方法就是一个java.lang.reflect.Method类的对象,Method类里面封装了关于成员方法的操作 35 // getMethods():获取所有的public修饰的函数,包括父类继承而来的 36 Method[] ms = c.getMethods(); 37 // getDeclaredMethods():获取所有该类自己声明的方法,不问访问权限,不包括继承来的方法 38 Method[] ms2 = c.getDeclaredMethods(); 39 40 // 下面获取方法的信息 41 for(int i=0;i<ms.length;i++) 42 { 43 // 获取方法的返回值类型的类类型 44 Class returnType = ms[i].getReturnType(); 45 // 获得方法的返回值类型的类类型的名字 46 System.out.println(returnType.getName()); 47 48 // 获取方法的名称 49 System.out.println(ms[i].getName()); 50 51 // 获取方法的参数列表的类型的类类型 52 Class[] parameterTypes = ms[i].getParameterTypes(); 53 for(int j=0;j<parameterTypes.length;j++) 54 { 55 // 获取方法的参数类型的类类型的名字 56 System.out.println(parameterTypes[j].getName()); 57 } 58 } 59 } 60 61 // 获取成员变量的信息 62 private static void getFieldsMessage(Object obj) 63 { 64 // 先获取类的类类型 65 Class c = obj.getClass(); 66 67 // 成员变量也是对象,是java.lang.reflect.Field类的对象,Field类里面封装了关于成员变量的操作 68 // getFields():获取所有的public的成员变量的信息 69 Field[] fs = c.getFields(); 70 // getDeclaredFields():获取该类自己声明的成员变量的信息 71 Field[] fs2 = c.getDeclaredFields(); 72 for(Field f:fs) 73 { 74 // 获取成员变量的类型的类类型 75 Class fieldTypes = f.getType(); 76 // 获取成员变量的类型的类类型的名字 77 String typeName = fieldTypes.getName(); 78 System.out.println(typeName); 79 // 获取成员变量的名字 80 String fieldName = f.getName(); 81 System.out.println(fieldName); 82 } 83 } 84 85 // 获取对象的构造函数的信息 86 public static void getConstructorMessage(Object obj) 87 { 88 // 先获取类的类类型 89 Class c = obj.getClass(); 90 91 // 构造函数也是对象,是java.lang.reflect.Constructor类的对象,Constructor类中封装了构造函数的信息 92 // c.getConstructors():获得所有的public的构造函数 93 Constructor[] con = c.getConstructors(); 94 // getDeclaredConstructors():获得所有的构造函数;也可以说获得所有自己声明的构造方法,因为构造方法都是必须要自己声明的; 95 Constructor[] con2 = c.getDeclaredConstructors(); 96 for(Constructor constr:con2) 97 { 98 // 获取构造函数的参数列表的类型的类类型 99 Class[] parametersTypes = constr.getParameterTypes(); 100 for(Class parametersType:parametersTypes) 101 { 102 // 获取构造函数的参数列表的类型的类类型的名称 103 System.out.println(parametersType.getName()); 104 } 105 106 // 获取构造函数的名字 107 System.out.println(constr.getName()); 108 } 109 } 110 }
方法的反射的基本操作:
1、如何获取某个方法:
方法的名称和方法的参数列表才能唯一决定某个方法;
2、方法反射的操作:
method.invoke(对象,参数列表);
1 /** 2 * 方法的反射操作 3 * 要获取一个方法,就是获取类的信息,获取类的信息首先要获取类的类类型 4 */ 5 package reflect; 6 7 import java.lang.reflect.InvocationTargetException; 8 import java.lang.reflect.Method; 9 10 public class MethodTest01 11 { 12 public static void main(String[] args) 13 { 14 // 获取需要的方法:方法名+参数列表唯一决定一个方法 15 16 // 先获取类类型 17 A a = new A(); 18 Class c = a.getClass(); 19 20 // 再获取方法:方法名+参数列表 21 try 22 { 23 // 获取方法 24 // getMethod():获取public方法 25 // getDeclaredMethod():获取自己声明的方法 26 Method m = c.getMethod("show", new Class[]{int.class,int.class}); 27 // 也可以这样写 28 Method m2 = c.getMethod("show", int.class,int.class); 29 30 // 获取方法后,进行方法的反射操作 31 // 以前调用方法是这样写的,而方法的反射操作是用n对象来进行方法调用,但两者的效果是一样的 32 // a.show(1,2); 33 // 方法的反射操作 34 // 方法如果没有返回值,返回null,有具体的返回值就返回具体的返回值 35 Object o = m.invoke(a, new Object[]{1,2}); 36 // 也可以这样写 37 Object o2 = m2.invoke(a, 1,2); 38 39 Method m3 = c.getMethod("show", new Class[]{String.class,String.class}); 40 Method m4 = c.getMethod("show", String.class,String.class); 41 Object o3 = m3.invoke(a, new Object[]{"hello",",world"}); 42 Object o4 = m4.invoke(a, "hello",",world"); 43 44 // 方法的形参为空场景 45 Method m5 = c.getMethod("show", new Class[]{}); 46 // 或者没有就不传 47 Method m6 = c.getMethod("show"); 48 Object o5 = m5.invoke(a, new Object[]{}); 49 Object o6 = m6.invoke(a); 50 } 51 catch (NoSuchMethodException e) 52 { 53 e.printStackTrace(); 54 } 55 catch (SecurityException e) 56 { 57 e.printStackTrace(); 58 } 59 catch (IllegalAccessException e) 60 { 61 e.printStackTrace(); 62 } 63 catch (IllegalArgumentException e) 64 { 65 e.printStackTrace(); 66 } 67 catch (InvocationTargetException e) 68 { 69 e.printStackTrace(); 70 } 71 } 72 } 73 74 class A 75 { 76 public void show(int i,int j) 77 { 78 System.out.println(i + j); 79 } 80 81 public void show(String i,String j) 82 { 83 System.out.println(i.toUpperCase() + "," + j.toUpperCase()); 84 } 85 86 public void show() 87 { 88 89 } 90 }
通过反射了解集合泛型的本质:
1 /** 2 * 通过反射了解集合泛型的本质 3 */ 4 package reflect; 5 6 import java.lang.reflect.InvocationTargetException; 7 import java.lang.reflect.Method; 8 import java.util.ArrayList; 9 10 public class MethodTest02 11 { 12 public static void main(String[] args) 13 { 14 ArrayList list = new ArrayList(); 15 ArrayList<String> list1 = new ArrayList<String>(); 16 17 list1.add("123"); 18 // 下面这样写是错误的,因为泛型限制的加入的对象类型 19 // list1.add(23); 20 21 Class c1 = list.getClass(); 22 Class c2 = list1.getClass(); 23 24 // 反射的操作都是编译之后(运行时)的操作 25 // 结果为true,说明编译之后的集合的泛型是去泛型化的,就是说编译之后集合就没有泛型了 26 // java中集合的泛型是防止错误输入的,只在编译时有效,绕过编译就无效了 27 // 验证:通过方法的反射绕过编译 28 System.out.println(c1 == c2); // true 29 30 // 验证:通过方法的反射操作绕过编译 31 try 32 { 33 Method m = c2.getMethod("add", Object.class); 34 m.invoke(list1, 1); // 绕过编译操作就绕过了泛型,能添加int类型数据 35 System.out.println(list1.size()); 36 System.out.println(list1); 37 38 // 现在就不能用String类型遍历了,因为有了int类型 39 } 40 catch (NoSuchMethodException e) 41 { 42 e.printStackTrace(); 43 } 44 catch (SecurityException e) 45 { 46 e.printStackTrace(); 47 } 48 catch (IllegalArgumentException e) 49 { 50 e.printStackTrace(); 51 } 52 catch (InvocationTargetException e) 53 { 54 e.printStackTrace(); 55 } 56 catch (IllegalAccessException e) 57 { 58 e.printStackTrace(); 59 } 60 } 61 }