一.什么是反射机制
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种基本类型。