一.什么是反射机制
java的反射机制是在运行状态中: 对于任意一个类,都能够知道这个类中的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。
这里的运行状态,个人理解是相对于编译时的,因为java的类在创建后,以使用new来创建为例,jvm会把代码编译成.class文件然后被类加载器加载到内存中,其中new的类会放入方法区,而类的class对象也就是类型对象会放入堆中,这个class对象每个类只有一个,也是提供方法区实例化的类的数据结构的接口。这是类的加载过程详细的后面我再了解说明下,这样如果我们在jvm正在运行时加载一些之前可能用不到所以没有加载到jvm内存中的类的话,就需要反射来动态的实例化类或者调用类的一些方法。
那反射有哪些具体的功能呢?
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 生成动态代理。
反射的使用场景应该都看到过很多次了,就是在逆向代码(如反编译); 与注解相结合的框架(如Retrofit); 单纯的反射机制应用框架(如EventBus); 动态生成类框架(如Gson)
二.获取类的信息
- java中获得class对象通常有三种方式: 一是使用Class类的static Class<?> forName(String className)静态方法,传入的字符串参数值是某个类的全限定名(必须添加完整包名);二是调用某个类的class属性来获取该类对应的Class对象;三是调用某个对象的getClass()方法
public static void main(String[] args) {
//通过forName()静态方法实现
try {
Class<?> class1 = Class.forName("com.dyh.demo.Book");
System.out.println("通过forName静态方法获取的类: " + class1);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//通过类的class的属性
Class<?> class2 = Book.class;
System.out.println("通过类的class的属性获取的类: " + class2);
//通过对象的getClass方法
Book book = new Book();
Class<?> class3 = book.getClass();
System.out.println("通过类的class的属性获取的类: " + class3);
}
- 获取class对象的属性,方法成员变量等
/**
* 获取class对象的成员变量
*/
//获取class对象的所有属性
Field[] allFields = class3.getDeclaredFields();
System.out.println("class对象的所有属性: " + Arrays.toString(allFields));
//获取class对象的public属性
Field[] publicFields = class3.getFields();
System.out.println("class对象的public属性: " + Arrays.toString(publicFields));
//获取class对象的指定属性
Field authorField = class3.getDeclaredField("author");
System.out.println("class对象的指定属性: " + authorField);
//获取class对象的指定public属性
Field versionField = class3.getField("version");
System.out.println("class对象的指定public属性: " + versionField);
/**
* 获取class对象的方法
*/
//获取class对象的所有声明方法
Method[] methods = class3.getDeclaredMethods();
System.out.println("class对象的所有声明方法: " + Arrays.toString(methods));
//获取class对象的public方法包括父类的方法
Method[] allMethods = class3.getMethods();
System.out.println("class对象的public方法包括父类的方法: " + Arrays.toString(allMethods));
//获取class对象对应类的,带指定形参列表的public方法
Method method = class3.getMethod("author", String.class);
System.out.println("class对象的对应类的,带指定形参列表的public方法: " + method);
//获取class对象的对应类的,带指定形参列表的方法
Method declareMethod = class3.getDeclaredMethod("author", String.class);
System.out.println("class对象的对应类的,带指定形参列表的方法: " + declareMethod);
/**
* 获取class对象的构造方法
*/
//获取class对象的所有声明构造函数
Constructor<?>[] allConstructors = class3.getDeclaredConstructors();
System.out.println("class对象的所有声明构造函数: " + Arrays.toString(allConstructors));
//获取class对象的public构造函数
Constructor<?>[] publicConstructors = class3.getConstructors();
System.out.println("class对象的public构造函数: " + Arrays.toString(publicConstructors));
//获取class指定声明的构造方法
Constructor<?> constructor = class3.getDeclaredConstructor(String.class);
System.out.println("class指定声明的构造方法: " + constructor);
//获取class指定声明的public构造方法
Constructor<?> publicConstructor = class3.getConstructor(String.class);
System.out.println("class指定声明的public构造方法: " + publicConstructor);
/**
* 获取class对象的其他方法
*/
//获取class对象的所有注解
Annotation[] annotations = (Annotation[]) class3.getAnnotations();
System.out.println("class对象的所有注解: " + Arrays.toString(annotations));
//获取class对象的指定注解
Annotation annotation = (Annotation) class3.getAnnotation(Deprecated.class);
System.out.println("class对象的指定注解: " + annotation);
//获取class对象的直接超类的Type
Type superClass = class3.getGenericSuperclass();
System.out.println("class对象的直接超类的Type: " + superClass);
//获取class对象的所有接口的type集合
Type[] interfaceTypes = class3.getGenericInterfaces();
System.out.println("class对象的所有接口的type集合: " + Arrays.toString(interfaceTypes));
3.获取class对象的信息
//判断是否是基础类型
boolean isPrimitive = class2.isPrimitive();
//判断是否是集合类
boolean isArray = class2.isArray();
//判断是否是注解类
boolean isAnnotation = class2.isAnnotation();
//判断是否是接口类
boolean isInterface = class2.isInterface();
//判断是否是枚举类
boolean isEnum = class2.isEnum();
//判断是否是匿名内部类
boolean isAnonymousClass = class2.isAnonymousClass();
//判断是否被某个注解类修饰
boolean isAnnotationPresent = class2.isAnnotationPresent(Documented.class);
//获取class名字包括包名
String className = class2.getName();
//获取包信息
Package aPackage = class2.getPackage();
//获取类名
String simpleName = class2.getSimpleName();
//获取class访问权限
int modifiers = class2.getModifiers();
//内部类
Class<?>[] declaredClasses = class2.getDeclaredClasses();
//外部类
Class<?> declaringClass = class2.getDeclaringClass();
三.通过反射操作对象
-
生成类的实例对象: 一种方法是使用newInstance()方法创建Class对象对应类的实例(这种方式的条件是Class对象的对应类有默认的构造函数,使用newInstance()方法实际上就是调用默认的构造函数来new一个实例。)使用方法是: Object obj = class.newInstance(); 还有一种方法是先获取到指定的构造函数(上面已经给出了获取class对象各个构造方法的函数),在通过调用获取到的构造方法的newInstance()方法来创建该class对象对应类的实例。
-
调用类的方法: 上面提到了获取class对象的各个方法的函数,获取到了想要动态使用的方法后,调用Method的Object invoke(Object obj, Object... args) 方法来使用。(第一个参数对应调用该方法的实例对象,第二个参数对应该方法的参数)具体使用方式:
//生成一个实例对象
Object obj = class2.newInstance();
//获取需要的方法
Method method = class2.getDeclaredMethod("setAuthor", String.class);
//调用函数并传参
method.invoke(obj, "dyh");
当然如果想要调用class对象对应类的方法,必须要有对应调用该方法的权限,如果某个方法是private的,可以使用method.setAccessible(boolean flag)来指定是否取消权限检查,设置true时,则取消java的访问权限检查。
- 使用成员变量: 在之前获取到成员变量的基础上,可以通过Field的getT(Object obj)方法来获取obj对象对应的成员变量的值,通过setT(Object obj, T value) 来设置obj对象对应的成员变量的值。T 是泛型,这里替代java的8种基本类型。