目录
1、什么是反射
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并获得类信息。
举个栗子?来理解:
已知某个类A,类A中有个无参的方法print,该类A完成类名为"com.zsh.reflect.A"(字符串)
例子1:
A a = new A();
a.print();
例子2:
public static void main(String[] args) throws Exception{
Class c = Class.forName("com.zsh.reflect.A");//获得类对象
Method method = c.getMethod("print", new Class[] {});//获得无参方法
//或者Method method = c.getMethod("print");//获得无参方法
Constructor constructor = c.getConstructor();//获得构造函数
Object object = c.newInstance();//创建该类的对象实例
//object 类是所有类的父类,包括Class类
method.invoke(object, new Object[]{});//调用无参的print方法
}
上面两个例子中,例1知道要运行的类,而例2是在运行时通过字符串才得知要运行的类,即在一开始并不知道要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。反射就是像例2那样在运行时刻动态加载类,从反射中获得类对象,进而得到需要的类信息。
※ 例子2中一个要注意的点:print方法一定要定义为public,否则无法用getMethod()方法获得。若不添加上public,则用getDeclaredMethod()获得方法(这里看不懂的先往下看会有讲解的)
2、必须一定要了解的基础
在面向对象里,万事万物皆对象。
类也是对象,是 java.lang.Class 类的实例对象。
任何一个类都是Class类的实例对象,这个实例对象有三种表示方式:(设类名为Foo)【这里是重点】
- 任何一个类都有一个隐含的静态成员变量class eg:Class c1 = Foo.Class;
- 已知该类的对象,通过getClass方法 eg:Class c2 = foo1.getClass();
- 已知完整类名 eg:Class c3 = null; c3 = Class.forName("com.imooc.reflect.Foo");
c1 == c2 == c3 都代表了Foo类的类类型(即Class的对象),一个类只可能是Class类的一个实例对象
完全可以通过类的类类型创建该类的对象实例(即通过c1 / c2 / c3来创建Foo类的实例)
Foo foo = (Foo)c1.newInstance();// 前提:需要有无参构造函数
是什么类的类类型,就是什么类的对象,需强制类型转换
Class.forName("类全称") 不仅表示了类的类类型,还代表了动态加载类
- 编译时刻加载类:静态加载类。new创建对象是静态加载类,在编译时刻就需要加载所有可能使用到的类。
- 运行时刻加载类:动态加载类。通过动态加载类,在运行时加载可解决该问题,写一个接口(功能性类)。
例子:
Class c1 = int.class;// int的类类型 Class c2 = String.class;// String类的类类型
Class c3 = double.class;// double数据类型的类类型 Class c4 = Double.class;//Double类的类类型
Class c5 = void.class;
打印:
c1.getName() → int c2.getName() → java.lang.String c5.getName() → void
c2.getSimpleName() → String(得到不包含包名的类的名称)
3、怎么运用反射
要获取类的信息,首先要获取类的类类型(类信息eg:类名、类方法、类成员变量、类构造函数等等)
Class c = obj.getClass();// (Object是一切类的父类)传递的是哪个子类的对象,c就是该类的类类型
【1】获取类的名称:c.getName()
【2】获取类的方法:Method[] ms = c.getMethods;// 或 c.getDeclaredMethods()
【3】获取方法的返回值类型的类类型:Class returnType = ms[i].getReturnType(); returnType.getName();
获取方法名称:ms[i].getName()
获取参数类型(即参数列表的类型的类类型)Class[] paramTypes = ms[i].getParameterTypes();
for(Class class1:paramTypes){ class1.getName();}
(1)获取类的成员变量的信息
成员变量也是对象,是java.lang.reflect.Field类的对象,Field类封装了关于成员变量的操作
- getField():获取的是所有的public的成员变量的信息
- getDeclaredFields():获取的是该类自己声明的成员变量的信息
Field[] fs = c.getDeclaredFields();
for(Field field:fs){ //使用for循环
Class FieldType = field.getType();
String typeName = fieldType.getName(); 得成员变量的类型的类类型
String fieldName = field.getName(); 得成员变量名称
}
(2)获取类的构造函数的信息
打印对象的构造函数的信息,首先获取它的类类型,构造函数也是对象,java.lang.Constructor中封装了构造函数的信息
- getConstructors:获取所有的public的构造函数。eg:Constructor[] cs = c.getConstructors();
- getDeclaredConstructors:得到所有自己声明的构造函数。eg:constructor.getName();
获取构造函数的参数列表(得参数列表的类类型)
Class[] paramTypes = constructor.getParameterTypes();for(Class class1:paramTypes){ class1.getName();}
(3)获取类的某个方法的信息
方法的名称和方法的参数列表才能唯一决定某个方法。要获取方法,就是获取类的信息,首先要获取类类型。方法也是对象。
- getMethods:返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口的公共 member 方法。获得所有的public方法(包括继承的类或接口的方法)。
- getDeclaredMethods:返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。获得所有不包括继承的方法。所有该类自己声明的方法。
A a1 = new A();
Class c = a1.getClass();
Method m = c.getMethod("method_name", new Class[]{int.Class, int.Class});
Method m = c.getMethod("method_name", int.Class, int.Class); //method_name为方法名
获取方法时要try-catch,可能会抛出异常,因为方法可能不存在
上述例子中,用m对象来进行方法调用,和a1.method_name调用效果相同
method.invoke(对象,参数列表):通过调用invoke方法来执行对象的某个方法
- 有参:Object o = m.invoke(a1, new Object[]{x1,x2});//x1和x2为参数
- 无参:Method m2 = c.getMethod("method_name2", new Class[]{}); m2.invoke(a1, new Object[]{});
※ 反射的操作都是编译之后的操作,运行时才执行的
举个栗子?:
ArrayList list1 = new ArrayList();
Class c1 = list1.getClass();
ArrayList<String> list2 = new ArrayList<String>();
Class c2 = list2.getClass();
c1 == c2的结果为true,说明编译之后集合的泛型是去泛型化的
Java中的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了(验证:可以通过方法的反射来操作,绕过编译)
若用list1.add(20);//无法加入,提示错误,因为泛型为String类型
若用反射:Method m = c2.getMethod("method_name", Object.class); m.invoke(list2, 20);//这时可以加入,因为反射是在运行时执行的,可以绕过编译阶段的检查,即绕过泛型,但不可用foreach遍历,因为会类型转换错误,可用System.out.println(list2);
推荐一个讲反射的慕课:https://www.imooc.com/learn/199