前言
相信每一个做Android开发的同学都用到过反射;同样我也经常用到;但是我从来没有好好的总结过这个经常用到的技术。很幸运在读包建强老师的书的时候看到了他专门有一章关于反射的总结。我决定在此基础上总结记录一下。
反射包括一下技术:
- 根据一个字符串得到一个类对象
- 获取一个类的所有公有或者私有、静态或者实例的字段、方法、属性
- 对于泛型类的反射
得到Class对象
- getClass方法
String s = "Hello";
s.getClass();
- Class.forName
try {
Class c = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
- class属性
Class c = String.class;
- TYPE属性
Class c = Boolean.TYPE;
创建实例对象
我们以一个TestClass用来测试
public class TestClass {
private static int age;
private String name;
private int a;
private float b;
private double c;
private TestClass() {
}
public TestClass(int a, float b) {
this.a = a;
this.b = b;
}
public TestClass(int a, double c) {
this.a = a;
this.c = c;
}
private void printABC(String name){
this.name = name;
System.out.println("a:"+a+" b:"+b+" c:"+c);
}
private void printABC(){
System.out.println("a:"+a+" b:"+b+" c:"+c);
}
private static void work(){
System.out.println("你好!打工人!");
}
}
获取构造函数
- 通过getDeclaredConstructors;获取所有构造函数,包括私有构造函数
try {
Class c = Class.forName("TestClass");
for(Constructor constructor:c.getDeclaredConstructors()){
System.out.println(Modifier.toString(constructor.getModifiers()) + " " + constructor.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
输出
private TestClass
public TestClass
public TestClass
- 通过getConstructors;获取public构造函数
try {
Class c = Class.forName("TestClass");
for(Constructor constructor:c.getConstructors()){
System.out.println(Modifier.toString(constructor.getModifiers()) + " " + constructor.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
输出
public TestClass
public TestClass
- 获取具体参数形式的构造函数getDeclaredConstructor()
try {
Class c = Class.forName("TestClass");
//获取无参构造函数
Constructor constructor = c.getDeclaredConstructor();
//获取对应参数的构造函数
Class[] p = {Integer.TYPE , Float.TYPE };
Constructor constructor2 = c.getDeclaredConstructor(p);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
}
通过构造函数创建对象
- 有参数的构造函数
通过有参数构造函数创建对象,通过Constructor的newInstance函数传入参数值
try {
Class c = Class.forName("TestClass");
Class[] p = {Integer.TYPE , Float.TYPE };
Constructor constructor2 = c.getDeclaredConstructor(p);
constructor2.newInstance(1,1.0f);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
- 无参数的构造方法
通过Constructor对象的newInstance()函数
try {
Class c = Class.forName("TestClass");
//获取无参构造函数
Constructor constructor = c.getDeclaredConstructor();
constructor.newInstance();
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
通过Class对象的newInstance()
try {
Class c = Class.forName("TestClass");
c.newInstance();
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
通过反射执行私有方法
执行私有实例方法
使用私有实例方法,前提条件是必须有实例对象;前面已经介绍了如何通过反射创建实例对象;通常我们使用的时候也不需要自己创建实例,只是为了使用实例中的私有方法。
Class c = Class.forName("TestClass");
//获取对应参数的构造函数
Class[] p = {Integer.TYPE , Float.TYPE };
Constructor constructor2 = c.getDeclaredConstructor(p);
Object object =constructor2.newInstance(1,1.0f);
//获取相应的方法
Class[] mp = {String.class};
Method method = c.getDeclaredMethod("printABC",mp);
method.setAccessible(true);
//调用函数
Object[] objects = {"Hello"};
method.invoke(object,objects);
强调一下一定要对Method对象调用
method.setAccessible(true);
不然会报错
java.lang.IllegalAccessException: Class Main can not access a member of class TestClass with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Method.invoke(Method.java:491)
at Main.main(Main.java:22)
执行私有静态方法
和执行私有实例方法,唯一区别是调用方法时不需要依赖实例对象
Class c = Class.forName("TestClass");
//获取方法对象
Method method = c.getDeclaredMethod("work");
method.setAccessible(true);
//调用函数
method.invoke(null);
读写私有字段
读写私有实例字段
其实很简单;流程和执行实例私有方法类似;先获取字段对象;然后通过实例对象和字段对象的set和get方式操作字段对象
需要注意Android中对final字段的限制
Class c = Class.forName("TestClass");
//获取对应参数的构造函数
Class[] p = {Integer.TYPE , Float.TYPE };
Constructor constructor2 = c.getDeclaredConstructor(p);
Object object =constructor2.newInstance(1,1.0f);
//获取字段
Field field = c.getDeclaredField("name");
field.setAccessible(true);
//读取字段
System.out.println("字段值:"+field.get(object));
//修改字段值
field.set(object,"打工人");
System.out.println("字段值:"+field.get(object));
读写静态字段
很简单,和实例字段读写非常类似,只是实例对象传null。
Class c = Class.forName("TestClass");
//获取字段
Field field = c.getDeclaredField("age");
field.setAccessible(true);
//读取字段
System.out.println("字段值:"+field.get(null));
//修改字段值
field.set(null,10);
System.out.println("字段值:"+field.get(null));