一、概述
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括:私有的方法和属性),这种动态获取信息以及动态调用对象方法的功能,就称为 Java语言的反射机制。
(通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。)
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(包括:方法、属性、类名、父类名、实现的所有接口等),每一个类对应着一个字节码文件,也就对应着一个Class类型的对象(即:字节码文件对象)。
/**
* 获取字节码文件对象(.class)的三种方式
*/
// 1、通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时,该类还是源文件阶段,并没有变为字节码文件。
Class clazz1 = Class.forName("全限定类名");
// 2、当类被加载成.class文件时,此时,Person类变成了.class,再获取该字节码文件对象,也就是获取自己,该类处于字节码阶段。
Class clazz2 = Person.class;
// 3、通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段。
Class clazz3 = p.getClass();
有了字节码文件对象,才能获得类中所有的信息,在使用反射机制,获取信息时,也要考虑使用上面哪种方式获取字节码对象最为合理,视不同情况而定。
二、Class类的API详解
1、创建实例对象
(1)通过字节码对象
// User类还没加载,在源文件阶段就获取其字节码文件对象
Class clazz = Class.forName("com.miui.performance.User");
// 创建User实例(这里是通过User的无参构造方法,来创建的对象)
User user = (User) clazz.newInstance();
// 通过user对象,就可以获取所有信息
(2)通过构造方法(注:用于constructor没有无参构造,只有有参构造时创建实例对象的情况)
// 获取字节码文件
Class clazz = Class.forName("com.miui.performance.User");
// 获取有参构造器(参数列表中有几个参数就写几个,不写就表示调用无参构造函数)
Constructor constructor = clazz.getConstructor(int.class, String.class);
// 通过构造器来实例化对象,将实际的参数传进去
User user = (User) constructor.newInstance(10, "小米");
(3)通过Jar包和全类名
// 获取jar包
perfClassLoader = new PathClassLoader("/system/framework/MiuiBooster.jar", ClassLoader.getSystemClassLoader());
// 加载类
perfClass = perfClassLoader.loadClass("com.miui.performance.DeviceLevelUtils");
// 获取构造器
mConstructor = perfClass.getConstructor(Context.class);
// 创建实例对象
mPerf = mConstructor.newInstance();
2、获取成员变量
(1)获取指定成员变量
// 获取字节码文件对象
Class clazz = Class.forName("com.miui.performance.User");
// 创建User实例(这里是通过User的无参构造方法,来创建的对象)
User user = (User) clazz.newInstance();
// 获取私有的成员变量
Field field1 = clazz.getDeclaredField("id");
// 获取public的成员变量
Field field2 = clazz.getField("name");
// 将私有的成员变量打开可见权限
field1.setAccessible(true);
// 对成员变量进行赋值操作
field1.setInt(user, 5);
// 获取成员变量的值
int id = field1.getInt(user);
(2)获取全部成员变量
// 获取全部成员变量,包括私有的
Field[] fields = clazz.getDeclaredFields();
// 遍历所有成员变量,获取对应的值
for(int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true); // 打开可见权限
System.out.println(fields[i].getInt(user)); // 获取成员变量的值
}
3、获取成员方法
(1)获取指定成员方法
// 获取字节码文件对象
Class clazz = Class.forName("com.miui.performance.User");
// 创建User实例(这里是通过User的无参构造方法,来创建的对象)
User user = (User) clazz.newInstance();
// 获取public的方法
Class[] args = new Class[]{int.class, String.class};
Method method = clazz.getMethod("initDevice", args);
// 调用方法(参数列表:方法的对象, 实际的参数值)
method.invoke(user, 5, note);
// 获取私有的方法
args = new Class[]{int.class};
Method method2 = clazz.getDeclaredMethod("getLevel", args);
method2.setAccessible(true);
method2.invoke(user, 2);
(2)获取所有的成员方法
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods) {
method.setAccessible(true);
// 获取方法的参数
Class<?>[] parameterTypes = method.getParameterTypes();
for(int i = 0; i < parameterTypes.length; i++) {
// 获取构造函数中参数类型
System.out.println(parameterTypes[i].getName());
}
}
4、获取该类的所有接口
Class[] interfaces = clazz.getInterfaces();
5、获取指定资源的输入流
// 参数:所需资源的名称,如果以"/"开始,则绝对资源名为"/"后面的一部分。
// 返回值:一个InputStream对象,如果找不到带有该名称的资源,则返回null。
InputStream is = clazz.getResourceAsStream(String name);