目录
定义
- Java 的反射(reflection)机制是一种强大功能,它可以让我们在运行时动态地获取和操作 类 或 对象 的信息
实例理解
- 我们可以通过反射机制来创建一个类的对象,而不需要使用 new 关键字
- 我们也可以通过反射机制来访问或修改一个对象的私有属性或方法,而不需要遵循封装原则
- 我们还可以通过反射机制来调用一个对象的任意方法,而不需要知道它的参数类型或返回值类型
用途
典型用途一:
- 在开发第三方应用时,我们可能会遇到一些类的成员变量、方法或属性是私有的,或者只对系统应用开放,这就意味着我们不能直接访问这些成员或方法
- 这时我们便可以在运行时 通过 Java 的反射机制 来动态地访问和操作类的内部成员,包括私有成员和方法
典型用途二:
- 反射在开发通用框架 Spring 中起着重要的作用
- 在Spring 框架中,所有类(Bean)都由 Spring 容器进行管理,这些 Bean 可以通过 XML 配置或注解来配置
- 当我们从容器中获取Bean 以进行依赖注入时,容器会读取配置信息,这些配置信息包含了类的信息,比如类的名称、属性、方法等
- Spring根据这些信息 动态地创建这些类的实例,这个过程就是所谓的 依赖注入,该过程中,反射起到了关键作用
- Spring 使用反射来动态地创建类的实例,调用方法,以及设置属性值
反射基本原理
- Java 的反射机制是基于 java.lang.Class 类实现的
- 当我们编译一个 Java 文件时,会生成一个 .class 文件
- 当 JVM 加载这个 .class 文件时,会将其解析为一个 java.lang.Class 类的对象
- 在程序运行时,每个 Java 文件都会被 JVM 解析为一个 Class 类的实例
- 这个 Class 类的实例包含了该 Java 文件中所定义类的所有信息,包括类的名称、属性、方法等
- 我们可以通过 Java 的反射机制来操作这个 Class 类的实例
- 具体来说,我们可以使用反射来获取类的属性和方法,甚至可以添加或修改类的属性和方法
- 这使得我们可以在运行时动态地操作类,使其成为一个 动态的类
类名 用途 Class 类 代表类的实体,在运行的 Java 应用程序中表示类和接口 Field 类 代表类的成员变量、类的属性 Method 类 代表类的方法 Constructor 类 代表类的构造方法
Class 类中的相关方法
常用获得类相关的方法
方法 用途 getClassLoader() 获得类的加载器 getDeclaredClasses() 返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的) forName(String className) 根据类名返回类的对象 newInstance() 创建类的实例 getName() 获得类的完整路径名字 常用获得类中属性相关的方法
方法 用途 getField(String name) 获得某个公有的属性对象 getFields() 获得所有公有的属性对象 getDeclaredField(String name) 获得某个属性对象 getDeclaredFields() 获得所有属性对象 常用获得类中构造器相关的方法
方法 用途 getConstructor(Class <?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法 getConstructors() 获得该类的所有公有构造方法 getDeclaredConstructor(Class <?> parameterTypes) 获得该类中与参数类型匹配的构造方法 getDeclaredConstructors() 获得该类所有构造方法 常用获得类中方法相关的方法
方法 用途 getMethod(String name, Class <?> parameterTypes) 获得该类某个公有的方法 getMethods() 获得该类所有公有的方法 getDeclaredMethod(String name, Class <?> parameterTypes) 获得该类某个方法 getDeclaredMethods() 获得该类所有方法
实例理解
- 此处我们先创建一个 Student 类
class Student{ //私有属性name private String name = "master"; //公有属性age public int age = 18; //不带参数的构造方法 public Student(){ System.out.println("Student()"); } private Student(String name,int age) { this.name = name; this.age = age; System.out.println("Student(String,name)"); } private void eat(){ System.out.println("make hamburger!"); } public void sleep(){ System.out.println("go to bed!"); } private void function(String str) { System.out.println(str); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
- 此处介绍三种方式来获取 Student 的 Class 对象
public class Demo1 { public static void main(String[] args) { // 有三种方式可以获取 Class 对象 Student student1 = new Student(); // 1、通过对象的 getClass() 方法 Class<?> c1 = student1.getClass(); // 2、通过类名 .class 获取 Class<?> c2 = Student.class; // 3、通过调用 Class.forName() 方法获取了 Student 类的 Class 对象 // Class.forName() 方法需要一个类的全限定名(包括 包名和类名) 作为参数 // 此处的 ? 是一个通配符,用于表示未知类型 // 当我们声明一个泛型变量时,如果我们不确定或不关心实际的类型参数,我们可以使用 ? 来表示 Class<?> c3 = null; try { c3 = Class.forName("Student"); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } // 此处证明通过上述三种方式所获取的 Class 对象 都是同一个 System.out.println((c1.equals(c2) && c1.equals(c3) && c2.equals(c3)) ? "true" : "false"); } }
- 此处我们通过反射机制创建一个对象
import java.lang.reflect.InvocationTargetException; public class ReflectClassDemo { // 通过反射创建一个对象 public static void reflectNewInstance() throws InstantiationException, IllegalAccessException, ClassNotFoundException { Class<?> c3 = Class.forName("Student"); // 通过调用 Class 类的 newInstance() 方法来创建一个 c3 对应类的新实例 // newInstance() 方法调用的是这个类的无参构造函数 // 如果这个类没有无参构造函数,或者无参构造函数是私有的,那么 newInstance 会抛出一个异常 // 因为 newInstance() 方法返回的类型为 Object 类 所以需要类型转换,此处转换为 Student 类 Student student = (Student) c3.newInstance(); System.out.println(student); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException { reflectNewInstance(); } }
运行结果:
- 此处我们通过反射机制获取私有的构造方法
import java.lang.reflect.Constructor; public class ReflectClassDemo { // 通过反射获取私有的构造方法 public static void reflectPrivateConstructor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class<?> c3 = Class.forName("Student"); Constructor<?> constructor = c3.getDeclaredConstructor(String.class,int.class); // 注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的构造方法为私有的 constructor.setAccessible(true); // 此处利用构造方法 修改年龄和性别 Student student = (Student)constructor.newInstance("xiaolin",20); System.out.println(student); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException { reflectPrivateConstructor(); } }
运行结果:
- 此处我们通过反射机制获取私有属性
import java.lang.reflect.Field; public class ReflectClassDemo { // 通过反射获取私有属性 public static void reflectPrivateField() throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException { Class<?> c3 = Class.forName("Student"); Field field = c3.getDeclaredField("name"); // 注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的 name 属性是私有的 field.setAccessible(true); Student student = (Student) c3.newInstance(); // 此处修改私有属性 field.set(student,"haoran"); System.out.println(student); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException { reflectPrivateField(); } }
运行结果:
- 此处我们通过反射机制获取私有方法
import java.lang.reflect.Method; public class ReflectClassDemo { // 通过反射获取私有方法 public static void reflectPrivateMethod() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Class<?> c3 = Class.forName("Student"); Method method1 = c3.getDeclaredMethod("function", String.class); Method method2 = c3.getDeclaredMethod("sleep"); // 注意只要是涉及到 private 都要使用 setAccessible(true) 来打开权限,此处的 function 方法是私有的 method1.setAccessible(true); Student student = (Student) c3.newInstance(); // 此处给 function 方法传参 method1.invoke(student,"此处利用反射机制给 function 方法传个字符串参数"); // 此处调用 sleep 方法,该方法为 public 无需额外打开权限,直接调用即可 method2.invoke(student); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException { reflectPrivateMethod(); } }
运行结果:
优点
- 对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个类都能调用它的任意一个方法
- 增加程序的灵活性和扩展性,降低耦合性,提高自适应性
- 反射已经运用在很多流行框架,典型代表为 Spring
缺点
- 使用反射会有效率问题,会导致程序效率降低
- 反射技术绕过了源代码的技术,因而会带来维护问题
- 反射代码比相应的直接代码更复杂
总结:
- 虽然反射非常强大,但也需要谨慎使用
- 我们需要在反射带来的灵活性和可扩展性与其带来的性能开销、维护问题和代码复杂性之间找到一个平衡
- 某些情况下,比如开发通用的框架,使用反射式非常有价值的
- 但在其他情况下,我们可能更倾向于使用更简单、更直接的代码