在 Java 编程领域,反射机制如同赋予开发者一把 “万能钥匙”,能在运行时动态操作类与对象。本文将结合实际代码示例,深入剖析反射的核心概念、操作方法及其在代理模式中的应用,帮助读者全面掌握这一高级特性。
一、反射核心概念与作用
1. 什么是反射?
反射(Reflection)是 Java 的一项强大特性,允许程序在运行时动态获取类的信息(如类的属性、方法、构造器等),并能操作类或对象的内部属性及方法。
- 核心类:位于
java.lang.reflect
包下,包括Class
、Field
、Method
、Constructor
等。 - 本质:通过 JVM 在运行时暴露的类元数据(
Class
对象),突破编译期限制,实现动态操作。
2. 反射的主要作用
- 动态创建对象:无需在编译期知道具体类名,通过类名即可创建实例。
- 访问私有成员:通过暴力反射(
setAccessible(true)
)突破封装性,操作私有字段或方法。 - 框架开发:Spring 的依赖注入(DI)、MyBatis 的 ORM 映射等框架均基于反射实现。
-
单元测试:动态调用被测类的私有方法,提升测试覆盖率。
3. 示例代码:Animal类定义
// 定义Animal类,包含注解、接口实现、构造器、方法 package com.kai.demo; import java.lang.annotation.*; // 自定义类注解(运行时保留) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface DomesticAnimal { String category() default "宠物"; // 默认属性值 boolean endangered() default false; } // 定义接口 public interface Feedable { void feed(); } public interface Trainable { void train(); } // Animal类实现接口并应用注解 @DomesticAnimal(category = "哺乳动物") // 显式设置属性值 public class Animal implements Feedable, Trainable { public String name; // 公开字段 private int age; // 私有字段 // 构造器重载 public Animal() { this.name = "--"; } public Animal(String name) { this.name = name; } public Animal(String name, int age) { this.name = name; this.age = age; } // 公开方法 public void eating() { System.out.println("Hi my name is " + name + " I'm eating"); } // 私有方法(演示暴力反射) private void run(String speed) { System.out.println(name + " is running " + speed); } // 接口实现方法 @Override public void feed() { System.out.println(name + "正在进食..."); } @Override public void train() { System.out.println(name + "正在接受训练..."); } }
上述代码构建了一个Animal类,涵盖注解、接口、字段、构造器和方法,后续将基于此进行反射操作演示。
二、反射基础操作实战
1. 获取Class对象的三种方式
public class ReflectionDemo { @Test public void crateClassObj() throws Exception { // 简化异常抛出 // 方式1:类字面量(编译期安全) Class
animalClass = Animal.class; // 获取Animal类的Class对象 Animal animal = animalClass.getConstructor().newInstance(); // 调用无参构造器 animal.eating(); // 输出:Hi my name is -- I'm eating // 方式2:Class.forName(动态加载) Class aClass = (Class ) Class.forName("com.kai.demo.Animal"); System.out.println("aClass == animalClass = " + (aClass == animalClass)); // 输出:true // 方式3:对象.getClass()(运行时获取) Animal instance = new Animal("Tom", 2); Class extends Animal> aClass1 = instance.getClass(); System.out.println("aClass1 == animalClass = " + (aClass1 == animalClass)); // 输出:true } } 关键点:无论采用哪种方式,获取到的Class对象在 JVM 中都是唯一实例,因为每个类仅对应一个Class元数据。
2. 动态创建对象
@Test public void getClassConstructor() throws Exception { Class
animalClass = Animal.class; // 获取公开无参构造器(public Animal()) Constructor paramConstructor = animalClass.getConstructor(String.class); Animal yoyo = paramConstructor.newInstance("Yoyo"); // 等价于 new Animal("Yoyo") yoyo.eating(); // 输出:Hi my name is Yoyo I'm eating // 注意:获取私有构造器时,getConstructor会报错 // 因为该方法只能获取public构造器,私有构造器需用getDeclaredConstructor // 例如:获取private void run(String) 需用getDeclaredMethod } 3. 操作私有成员
(1)访问私有字段age
@Test public void getClassFields() throws Exception { Class
animalClass = Animal.class; // 公开字段:直接获取 Field nameField = animalClass.getField("name"); // 对应public String name; // 私有字段:需声明为可访问 Field ageField = animalClass.getDeclaredField("age"); // 对应private int age; ageField.setAccessible(true); // 暴力反射,允许访问私有字段 // 创建实例并修改字段值 Animal tom = new Animal("Tom", 2); nameField.set(tom, "Tim"); // 等价于 tom.name = "Tim" ageField.set(tom, 3); // 直接修改私有字段age System.out.println("修改后:" + tom.name + ", 年龄:" + ageField.get(tom)); // 输出:修改后:Tim, 年龄:3 } (2)调用私有方法run(String)
@Test public void getClassMethod() throws Exception { Class
animalClass = Animal.class; Animal tom = animalClass.getConstructor(String.class, int.class).newInstance("Tom", 2); // 调用公开方法eating() Method eatingMethod = animalClass.getMethod("eating"); eatingMethod.invoke(tom); // 输出:Hi my name is Tom I'm eating // 调用私有方法run(String)(需暴力反射) Method runMethod = animalClass.getDeclaredMethod("run", String.class); runMethod.setAccessible(true); // 允许调用私有方法 runMethod.invoke(tom, "slowly"); // 输出:Tom is running slowly } 三、反射在代理模式中的应用
1. 静态代理:AnimalStaticProxy类
// 静态代理类,实现与目标类相同的接口 public class AnimalStaticProxy implements Feedable, Trainable { private final Animal target; // 持有目标对象引用 public AnimalStaticProxy(Animal target) { this.target = target; } // 代理方法:增强eating()逻辑 public void eating() { System.out.println("[静态代理] 准备动作..."); target.eating(); // 调用目标方法 System.out.println("[静态代理] 动作完成..."); } // 接口实现方法 @Override public void feed() { System.out.println("准备食物..."); target.feed(); // 调用目标方法 System.out.println("清理餐具..."); } }
使用示例:
public class ProxyDemo { public static void main(String[] args) { Animal cat = new Animal("Tom", 3); AnimalStaticProxy proxy = new AnimalStaticProxy(cat); proxy.feed(); // 通过代理调用接口方法 // 输出: // 准备食物... // Tom正在进食... // 清理餐具... } }
2. JDK 动态代理:AnimalInvocationHandler类
public class AnimalInvocationHandler implements InvocationHandler { private final Object target; // 目标对象(支持多态) public AnimalInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置增强:打印方法名 System.out.println("[动态代理] 调用方法:" + method.getName()); // 调用目标方法(核心逻辑) Object result = method.invoke(target, args); // 反射调用目标对象的方法 // 后置增强:无操作 return result; } // 生成代理对象的工厂方法 public Object getProxyInstance() { return Proxy.newProxyInstance( target.getClass().getClassLoader(), // 类加载器与目标类一致 target.getClass().getInterfaces(), // 代理目标类实现的所有接口 this // 当前InvocationHandler实例 ); } }
使用示例:
public class DynamicProxyDemo { public static void main(String[] args) { Animal dog = new Animal("Lucy", 2); // 生成代理对象(实现Feedable接口) Feedable proxy = (Feedable) new AnimalInvocationHandler(dog).getProxyInstance(); proxy.feed(); // 调用代理方法 // 输出: // [动态代理] 调用方法:feed // 准备食物...(假设目标类feed()方法的逻辑) // Lucy正在进食... } }
四、反射的性能测试
@Test public void performanceTest() throws Exception { Animal tom = new Animal("Tom", 2); Method eatingMethod = Animal.class.getMethod("eating"); // 直接调用100万次 long start = System.currentTimeMillis(); for (int i = 0; i < 1_000_000; i++) { tom.eating(); // 直接调用 } System.out.println("直接调用耗时:" + (System.currentTimeMillis() - start) + "ms"); // 反射调用100万次 start = System.currentTimeMillis(); for (int i = 0; i < 1_000_000; i++) { eatingMethod.invoke(tom); // 反射调用 } System.out.println("反射调用耗时:" + (System.currentTimeMillis() - start) + "ms"); }
典型输出:
直接调用耗时:123ms 反射调用耗时:4567ms (约慢30倍)
测试结果直观展现了反射调用在性能上与直接调用的差距,在实际开发中应谨慎使用反射,避免频繁调用影响程序效率。
五、总结
反射核心操作 | 关键代码示例 | 原始文档对应方法 |
---|---|---|
获取Class对象 | Class<Animal clazz = Animal.class; | crateClassObj |
创建实例 | Constructor.newInstance(); | getClassConstructor |
访问字段 | Field.get(obj); Field.set(obj, value); | getClassFields |
调用方法 | Method.invoke(obj, args); | getClassMethod |
静态代理 | AnimalStaticProxy implements Feedable | AnimalStaticProxy类 |
动态代理 | Proxy.newProxyInstance() + InvocationHandler | AnimalInvocationHandler |
通过上述代码示例与解析,读者能够系统掌握 Java 反射机制从基础操作到实际应用的全流程。反射虽功能强大,但也伴随着性能和安全风险,开发者需根据具体场景权衡使用,让这把 “万能钥匙” 在合适的场景中发挥最大价值。
// Kai@CodeHubble | Observing: Java/2025-06-29