Java 详细说明反射机制 | JavaGuide(Java面试 + 学习指南)
Java反射的优缺点【Java面试】 (qq.com)
Java中的反射是什么? (qq.com)
口语回答反射是Java语言的一个重要特征。它可以在程序运行过程中构建任何类对象,获得任何类对象的变量、方法和属性,并调用任何对象。
成员变量与属性的区别:
Person类中定义了三个字段 age、name、gender是类成员的变量,但它们并不都是属性;属性是什么?
属性的定义规则如下:set/get方法名,去掉set/get后,将首字母小写剩余部分获得的字符串就是这一类的属性。
Java语言通过反射能力支持动态获取程序信息和动态调用方法的能力。
Java中有一个专门的Java.lang.reflect用于实现与反射相关的类库,包括construct、Field、Method等类分别用于获取类别的结构方法、成员变量和方法信息。
反射的使用场景相当多,比如在动态代理场景中,使用动态生成的代理来提高代码的重用性。在Spring框架中,反射被广泛使用,例如反射被用来实例Bean对象。
Java反射的优点:
- 增加程序的灵活性。在操作过程中可以动态修改和操作类别
- 例如,动态代理使用反射来提高代码的重用率
- 在运行过程中,可以轻松获得任何类的方法和属性,也可以通过反射动态调用
Java反射的缺点:
- 反射可能涉及动态类型的分析,因此JVM无法优化这些代码,导致性能低于非反射调用
- 使用反射后,代码的可读性会下降
- 反射可以绕过一些限制访问的属性或方法,可能会破坏代码本身的抽象性。
1 如果你知道具体的类别,你可以使用它
Class alunbarClass = TargetObject.class;
但是,我们一般不知道具体的类别,基本上是通过遍历包下面的类别获得的 Class 通过这种方式获得对象 Class 对象不会初始化
2 通过Class.forName()获取传入类的全路径
Class alunbarclass1 = Class.forName("cn.javaguide.TargetObject");
3 instance通过对象实例.getClass()获取
TargetObject o = new TargetObject();Class alunbarclass2 = o.getClass();
4 xxclassloder通过类加载器.loadClass()获取传入类路径:
ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
Class对象不会通过类加载器初始化,这意味着不会执行一系列步骤,包括初始化,静态代码块和静态对象不会执行。
一些基本的反射操作1 创建一个我们需要使用反射操作的类别 TargetObject
。
package cn.javaguide;public class TargetObject { private String value; public TargetObject() { value = "JavaGuide"; } public void publicMethod(String s) { System.out.println("I love " + s); } private void privateMethod() { System.out.println("value is " + value); }}
2 使用反射操作和参数的方法
package cn.javaguide;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Main { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException { /** * 获取 TargetObject 类的 Class 并创建对象 TargetObject 类实例 */ Class<?> targetClass = Class.forName("cn.javaguide.TargetObject"); TargetObject targetObject = (TargetObject) targetClass.newInstance(); /** * 获取 TargetObject 所有定义类别的方法 */ Method[] methods = targetClass.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getName()); } /** * 获取指定的方法并调用 */ Method publicMethod = targetClass.getDeclaredMethod("publicMethod", String.class); publicMethod.invoke(targetObject, "JavaGuide"); /** * 获取指定参数并修改参数 */ Field field = targetClass.getDeclaredField("value"); ///为了修改类中的参数,我们取消了安全检查 field.setAccessible(true); field.set(targetObject, "JavaGuide"); /** * 调用 private 方法 */ Method privateMethod = targetClass.getDeclaredMethod("privateMethod"); ///为了调用private,我们取消了安全检查 privateMethod.setAccessible(true); privateMethod.invoke(targetObject); }}
原理?通过类加载器将类字节码文件加载到内存中,并生成相应的Class对象。通过Class对象,可以获取和操作各种信息,实现动态调用和操作。
Java反射的运行机制如下:
类加载:Java程序执行时,类加载器根据类名查找并加载相应的类文件,将字节码文件加载到内存中,生成相应的Class对象。
获取Class对象:通过类加载器加载类别后,可以通过Class.forName()方法或对象的getclass()方法获取相应的class对象。
获取类结构信息:类结构函数、方法、字段等信息可以通过Class对象获取。通过Class获取Constructor()、getMethod()和getfield()等方法,可获得指定的结构函数、方法和字段。
动态调用:通过Method对象的invoke(),可以动态调用类的方法。通过Field对象的get()和set(),可以访问和修改类的字段。
框架和库:Springg等许多开源框架和库、Hibernate、Junit等,都使用反射来实现自动化配置、动态代理、对象关系映射等功能。
注释处理器:注释处理器可以通过反射进行分析和处理,生成相应的代码或实现特定的功能。例如,Java编译器的注释处理器可以通过反射进行扫描和处理。
序列化和反序列化:Java的序列化和反序列化机制可以通过反射读取和写入对象的属性,实现对象的序列化和反序列化。
配置文件的分析和加载:许多应用程序使用配置文件来配置参数和设置,可以动态地读取和分析配置文件,并根据配置文件的内容加载相应的类别和执行相应的操作。
动态代理:动态代理是通过反射实现的,可以在运行过程中动态生成代理类,并在代理类中调用代理对象的方法。
测试工具和单元测试:测试工具和单元测试框架可通过反射动态调用测试方法,实现自动测试和测试结果的收集。
动态加载类和资源:通过反射,可以在运行过程中动态加载类和资源文件,实现类的动态加载和更新。