[笔记]反射的概念和应用

在 Java 编程领域,反射机制如同赋予开发者一把 “万能钥匙”,能在运行时动态操作类与对象。本文将结合实际代码示例,深入剖析反射的核心概念、操作方法及其在代理模式中的应用,帮助读者全面掌握这一高级特性。

一、反射核心概念与作用

1. 什么是反射?

反射(Reflection)是 Java 的一项强大特性,允许程序在运行时动态获取类的信息(如类的属性、方法、构造器等),并能操作类或对象的内部属性及方法。

  • 核心类:位于java.lang.reflect包下,包括ClassFieldMethodConstructor等。
  • 本质:通过 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 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

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇