29 反射机制

目录

  • [🟠 29 反射机制](#🟠 29 反射机制)
    • 一、什么是反射
      • [1.1 反射的核心能力](#1.1 反射的核心能力)
      • [1.2 反射的优缺点](#1.2 反射的优缺点)
    • 二、Class类详解
      • [2.1 获取Class对象的三种方式](#2.1 获取Class对象的三种方式)
      • [2.2 Class类的常用方法](#2.2 Class类的常用方法)
      • [2.3 基本类型与数组的Class对象](#2.3 基本类型与数组的Class对象)
    • 三、获取构造方法
      • [3.1 获取和使用构造方法](#3.1 获取和使用构造方法)
      • [3.2 构造方法API速查](#3.2 构造方法API速查)
    • 四、获取并调用方法
      • [4.1 获取和调用普通方法](#4.1 获取和调用普通方法)
      • [4.2 获取方法列表的对比](#4.2 获取方法列表的对比)
    • 五、获取并操作字段
      • [5.1 读写对象字段](#5.1 读写对象字段)
      • [5.2 通过反射修改final字段(高级技巧)](#5.2 通过反射修改final字段(高级技巧))
    • 六、动态代理
      • [6.1 JDK动态代理](#6.1 JDK动态代理)
      • [6.2 CGLIB动态代理](#6.2 CGLIB动态代理)
      • [6.3 JDK代理 vs CGLIB代理](#6.3 JDK代理 vs CGLIB代理)
    • 七、反射的性能与最佳实践
      • [7.1 性能对比测试](#7.1 性能对比测试)
      • [7.2 性能优化建议](#7.2 性能优化建议)
    • 八、反射的应用场景
      • [8.1 框架中的反射应用](#8.1 框架中的反射应用)
      • [8.2 自定义简易IoC容器](#8.2 自定义简易IoC容器)
    • 九、总结与互动

🟠 29 反射机制

更新日期:2026年6月 | Java入门到精通系列 第四阶段

© 版权声明:本文为原创技术文章,转载请联系作者并注明出处。



一、什么是反射

反射(Reflection) 是 Java 语言的一项强大特性,允许程序在运行时获取类的信息并操作类的属性和方法。简单来说,反射让我们能够"透视"一个类的内部结构。

1.1 反射的核心能力

能力 说明 示例
获取类信息 获取类名、父类、接口等 Class.forName("java.lang.String")
创建对象 动态实例化对象 clazz.getDeclaredConstructor().newInstance()
调用方法 动态调用任意方法 method.invoke(obj, args)
访问字段 读写私有字段 field.set(obj, value)
操作数组 动态创建和操作数组 Array.newInstance(componentType, length)

1.2 反射的优缺点

复制代码
✅ 优点:                          ❌ 缺点:
├── 灵活性极高,运行时动态操作        ├── 性能开销较大
├── 框架基石(Spring、MyBatis等)    ├── 破坏封装性
├── 支持泛型擦除后的类型恢复          ├── 编译期无法类型检查
└── 实现通用工具和插件机制            └── 代码可读性降低

二、Class类详解

java.lang.Class 是反射的入口,每个类在JVM中都有且仅有一个对应的 Class 对象。

2.1 获取Class对象的三种方式

java 复制代码
public class ClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1:通过类名.class(编译时确定)
        Class<String> clazz1 = String.class;
        System.out.println(clazz1); // class java.lang.String

        // 方式2:通过对象.getClass()(运行时获取)
        String str = "Hello";
        Class<? extends String> clazz2 = str.getClass();
        System.out.println(clazz2); // class java.lang.String

        // 方式3:通过全限定类名(最灵活,常用于配置文件)
        Class<?> clazz3 = Class.forName("java.lang.String");
        System.out.println(clazz3); // class java.lang.String

        // 三种方式获取的是同一个Class对象
        System.out.println(clazz1 == clazz2); // true
        System.out.println(clazz2 == clazz3); // true
    }
}

2.2 Class类的常用方法

java 复制代码
public class ClassInfoDemo {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("java.util.ArrayList");

        // 基本信息
        System.out.println("类名: " + clazz.getName());           // java.util.ArrayList
        System.out.println("简单类名: " + clazz.getSimpleName()); // ArrayList
        System.out.println("包名: " + clazz.getPackageName());    // java.util
        System.out.println("修饰符: " + Modifier.toString(clazz.getModifiers())); // public

        // 继承关系
        Class<?> superClass = clazz.getSuperclass();
        System.out.println("父类: " + superClass.getName()); // java.util.AbstractList

        // 实现的接口
        Class<?>[] interfaces = clazz.getInterfaces();
        System.out.println("实现接口:");
        for (Class<?> iface : interfaces) {
            System.out.println("  - " + iface.getName());
        }

        // 判断类型
        System.out.println("是否是接口: " + clazz.isInterface());       // false
        System.out.println("是否是数组: " + clazz.isArray());           // false
        System.out.println("是否是枚举: " + clazz.isEnum());            // false
        System.out.println("是否是注解: " + clazz.isAnnotation());      // false
        System.out.println("是否是抽象类: " + Modifier.isAbstract(clazz.getModifiers())); // false
    }
}

2.3 基本类型与数组的Class对象

java 复制代码
public class PrimitiveClassDemo {
    public static void main(String[] args) {
        // 基本类型也有Class对象
        Class<Integer> intClass = int.class;
        Class<Void> voidClass = void.class;
        System.out.println(intClass.isPrimitive()); // true

        // 包装类
        Class<Integer> wrapperClass = Integer.class;
        System.out.println(wrapperClass.isPrimitive()); // false

        // 数组的Class对象
        Class<String[]> arrayClass = String[].class;
        System.out.println(arrayClass.isArray());       // true
        System.out.println(arrayClass.getComponentType()); // java.lang.String

        // 动态创建数组
        Object array = Array.newInstance(String.class, 5);
        Array.set(array, 0, "Hello");
        System.out.println(Array.get(array, 0)); // Hello
    }
}

三、获取构造方法

3.1 获取和使用构造方法

java 复制代码
import java.lang.reflect.Constructor;

// 用于演示的类
class Student {
    private String name;
    private int age;

    public Student() { this.name = "Unknown"; this.age = 0; }
    public Student(String name) { this.name = name; this.age = 0; }
    private Student(String name, int age) { this.name = name; this.age = age; }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}

public class ConstructorDemo {
    public static void main(String[] args) throws Exception {
        Class<Student> clazz = Student.class;

        // 获取所有public构造方法
        Constructor<?>[] publicConstructors = clazz.getConstructors();
        System.out.println("public构造方法数量: " + publicConstructors.length);

        // 获取所有构造方法(包括private)
        Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
        System.out.println("所有构造方法数量: " + allConstructors.length);
        for (Constructor<?> c : allConstructors) {
            System.out.println("  " + Modifier.toString(c.getModifiers()) + " " + c);
        }

        // 使用无参构造创建对象
        Constructor<Student> noArgConstructor = clazz.getConstructor();
        Student s1 = noArgConstructor.newInstance();
        System.out.println(s1); // Student{name='Unknown', age=0}

        // 使用带参构造创建对象
        Constructor<Student> oneArgConstructor = clazz.getConstructor(String.class);
        Student s2 = oneArgConstructor.newInstance("张三");
        System.out.println(s2); // Student{name='张三', age=0}

        // 使用private构造方法(需要setAccessible)
        Constructor<Student> privateConstructor = clazz.getDeclaredConstructor(String.class, int.class);
        privateConstructor.setAccessible(true); // 暴力反射
        Student s3 = privateConstructor.newInstance("李四", 25);
        System.out.println(s3); // Student{name='李四', age=25}
    }
}

3.2 构造方法API速查

方法 说明
getConstructor(Class<?>... params) 获取指定参数的public构造方法
getConstructors() 获取所有public构造方法
getDeclaredConstructor(Class<?>... params) 获取指定参数的任意构造方法
getDeclaredConstructors() 获取所有构造方法(含private)
newInstance(Object... args) 使用该构造方法创建实例
setAccessible(true) 突破private访问限制

四、获取并调用方法

4.1 获取和调用普通方法

java 复制代码
import java.lang.reflect.Method;

class Calculator {
    public int add(int a, int b) { return a + b; }
    public int multiply(int a, int b) { return a * b; }
    private String secret() { return "这是私有方法"; }
    public static void greet(String name) { System.out.println("Hello, " + name + "!"); }
}

public class MethodDemo {
    public static void main(String[] args) throws Exception {
        Class<Calculator> clazz = Calculator.class;
        Calculator calc = clazz.getDeclaredConstructor().newInstance();

        // 获取并调用add方法
        Method addMethod = clazz.getMethod("add", int.class, int.class);
        Object result = addMethod.invoke(calc, 10, 20);
        System.out.println("10 + 20 = " + result); // 10 + 20 = 30

        // 获取并调用multiply方法
        Method multiplyMethod = clazz.getMethod("multiply", int.class, int.class);
        System.out.println("5 * 6 = " + multiplyMethod.invoke(calc, 5, 6)); // 5 * 6 = 30

        // 调用私有方法
        Method secretMethod = clazz.getDeclaredMethod("secret");
        secretMethod.setAccessible(true);
        String secret = (String) secretMethod.invoke(calc);
        System.out.println(secret); // 这是私有方法

        // 调用静态方法(第一个参数传null)
        Method greetMethod = clazz.getMethod("greet", String.class);
        greetMethod.invoke(null, "World"); // Hello, World!

        // 获取方法信息
        System.out.println("返回类型: " + addMethod.getReturnType().getName()); // int
        System.out.println("参数个数: " + addMethod.getParameterCount());       // 2
        Class<?>[] paramTypes = addMethod.getParameterTypes();
        for (Class<?> type : paramTypes) {
            System.out.println("参数类型: " + type.getName());
        }
    }
}

4.2 获取方法列表的对比

方法 区别
getMethod(name, params) 获取public方法(含继承的)
getMethods() 获取所有public方法(含继承的)
getDeclaredMethod(name, params) 获取本类声明的方法(不含继承)
getDeclaredMethods() 获取本类声明的所有方法(含private)

五、获取并操作字段

5.1 读写对象字段

java 复制代码
import java.lang.reflect.Field;

class Person {
    public String name;
    private int age;
    protected String address;
    private static String country = "China";

    public Person(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", address='" + address + "'}";
    }
}

public class FieldDemo {
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;
        Person person = new Person("张三", 25, "北京");

        // 读取public字段
        Field nameField = clazz.getField("name");
        System.out.println("name: " + nameField.get(person)); // 张三

        // 修改public字段
        nameField.set(person, "李四");
        System.out.println("修改后: " + person);

        // 读写private字段
        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);
        System.out.println("age: " + ageField.getInt(person)); // 25
        ageField.set(person, 30);
        System.out.println("修改后age: " + ageField.getInt(person)); // 30

        // 读写静态字段
        Field countryField = clazz.getDeclaredField("country");
        countryField.setAccessible(true);
        System.out.println("country: " + countryField.get(null)); // China
        countryField.set(null, "中国");
        System.out.println("修改后country: " + countryField.get(null)); // 中国

        // 遍历所有字段
        System.out.println("\n所有字段信息:");
        Field[] allFields = clazz.getDeclaredFields();
        for (Field f : allFields) {
            f.setAccessible(true);
            System.out.printf("  %s %s = %s%n",
                Modifier.toString(f.getModifiers()),
                f.getType().getSimpleName(),
                f.getName());
        }
    }
}

5.2 通过反射修改final字段(高级技巧)

java 复制代码
public class FinalFieldDemo {
    public static void main(String[] args) throws Exception {
        // 修改static final字段(Java 12+需要特殊处理)
        Field field = Person.class.getDeclaredField("country");
        field.setAccessible(true);

        // Java 12+ 中需要移除final修饰符
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

        field.set(null, "日本");
        System.out.println(field.get(null)); // 日本

        System.out.println("⚠️ 注意:修改final字段是危险操作,仅用于了解机制");
    }
}

六、动态代理

动态代理是反射最经典的应用之一,Spring AOP的核心实现原理。

6.1 JDK动态代理

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 接口
interface UserService {
    void addUser(String name);
    void deleteUser(String name);
    String findUser(String name);
}

// 实现类
class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) { System.out.println("添加用户: " + name); }
    @Override
    public void deleteUser(String name) { System.out.println("删除用户: " + name); }
    @Override
    public String findUser(String name) { return "用户[" + name + "]"; }
}

// 通用的InvocationHandler实现
class LoggingHandler implements InvocationHandler {
    private final Object target;

    public LoggingHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("📋 [日志] 调用方法: " + method.getName());
        System.out.println("📋 [日志] 参数: " + (args != null ? Arrays.toString(args) : "无"));

        long start = System.currentTimeMillis();
        Object result = method.invoke(target, args); // 调用真实方法
        long cost = System.currentTimeMillis() - start;

        System.out.println("📋 [日志] 返回值: " + result);
        System.out.println("📋 [日志] 耗时: " + cost + "ms");
        return result;
    }
}

public class JdkProxyDemo {
    public static void main(String[] args) {
        // 创建代理对象
        UserService realService = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(),
            new Class[]{UserService.class},
            new LoggingHandler(realService)
        );

        // 通过代理调用方法
        proxy.addUser("张三");
        System.out.println("---");
        String user = proxy.findUser("李四");
        System.out.println("查询结果: " + user);
    }
}

输出结果:

复制代码
📋 [日志] 调用方法: addUser
📋 [日志] 参数: [张三]
添加用户: 张三
📋 [日志] 返回值: null
📋 [日志] 耗时: 0ms
---
📋 [日志] 调用方法: findUser
📋 [日志] 参数: [李四]
📋 [日志] 返回值: 用户[李四]
📋 [日志] 耗时: 0ms
查询结果: 用户[李四]

6.2 CGLIB动态代理

java 复制代码
// CGLIB代理不需要接口,可以代理普通类
// 需要引入cglib依赖

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class OrderService {
    public String createOrder(String product) {
        return "订单已创建: " + product;
    }
}

class CglibInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        System.out.println("[CGLIB] 前置处理");
        Object result = methodProxy.invokeSuper(obj, args);
        System.out.println("[CGLIB] 后置处理");
        return result;
    }
}

public class CglibProxyDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class);
        enhancer.setCallback(new CglibInterceptor());

        OrderService proxy = (OrderService) enhancer.create();
        System.out.println(proxy.createOrder("iPhone"));
    }
}

6.3 JDK代理 vs CGLIB代理

特性 JDK动态代理 CGLIB代理
实现方式 基于接口 基于继承
要求 目标类必须实现接口 目标类不能是final
性能 创建快,调用稍慢 创建慢,调用快
Spring默认 有接口时使用 无接口时使用
代理原理 Proxy.newProxyInstance() Enhancer.create()

七、反射的性能与最佳实践

7.1 性能对比测试

java 复制代码
public class ReflectionBenchmark {
    public static void main(String[] args) throws Exception {
        Student student = new Student("测试", 20);
        Class<Student> clazz = Student.class;

        // 直接调用
        long start = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            student.toString();
        }
        long directTime = System.nanoTime() - start;

        // 反射调用(每次获取Method)
        Method method = clazz.getMethod("toString");
        start = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            method.invoke(student);
        }
        long reflectTime = System.nanoTime() - start;

        // 反射调用(关闭安全检查)
        method.setAccessible(true);
        start = System.nanoTime();
        for (int i = 0; i < 1000000; i++) {
            method.invoke(student);
        }
        long fastReflectTime = System.nanoTime() - start;

        System.out.println("直接调用: " + directTime / 1_000_000 + "ms");
        System.out.println("反射调用: " + reflectTime / 1_000_000 + "ms");
        System.out.println("优化反射: " + fastReflectTime / 1_000_000 + "ms");
    }
}

7.2 性能优化建议

策略 说明
缓存Method/Field对象 避免重复查找
调用setAccessible(true) 跳过安全检查,提升性能
使用MethodHandle Java 7+,接近直接调用的性能
考虑代码生成 如ASM、ByteBuddy

八、反射的应用场景

8.1 框架中的反射应用

框架/场景 反射用途
Spring IoC 根据配置文件创建Bean实例
Spring AOP 动态代理实现切面编程
MyBatis 将ResultSet映射到POJO
JUnit 发现并调用@Test标注的方法
序列化框架 Jackson/Gson通过反射读写字段
IDE 代码提示、自动补全

8.2 自定义简易IoC容器

java 复制代码
import java.util.HashMap;
import java.util.Map;

// 简易IoC容器
class SimpleContainer {
    private Map<Class<?>, Object> beans = new HashMap<>();

    // 注册Bean
    public <T> void register(Class<T> clazz) throws Exception {
        Constructor<T> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        T instance = constructor.newInstance();

        // 自动注入:遍历字段,查找@Autowired注解
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(MyAutowired.class)) {
                Object dependency = beans.get(field.getType());
                if (dependency != null) {
                    field.setAccessible(true);
                    field.set(instance, dependency);
                }
            }
        }
        beans.put(clazz, instance);
    }

    // 获取Bean
    @SuppressWarnings("unchecked")
    public <T> T getBean(Class<T> clazz) {
        return (T) beans.get(clazz);
    }
}

// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface MyAutowired {}

// 使用示例
class UserRepository {
    public String findUser() { return "张三"; }
}

class UserService {
    @MyAutowired
    private UserRepository userRepository;

    public String getUser() {
        return userRepository.findUser();
    }
}

public class IoCDemo {
    public static void main(String[] args) throws Exception {
        SimpleContainer container = new SimpleContainer();
        container.register(UserRepository.class);
        container.register(UserService.class);

        UserService service = container.getBean(UserService.class);
        System.out.println(service.getUser()); // 张三
    }
}

九、总结与互动

核心知识点回顾

复制代码
📌 反射机制核心要点:
├── Class类是反射的入口,有三种获取方式
├── getDeclaredXxx() 获取本类声明的(含private)
├── getXxx() 获取public的(含继承的)
├── setAccessible(true) 突破private限制
├── 动态代理是反射最重要的应用之一
└── 反射性能较低,应缓存Method/Field对象

思考题 🤔

  1. 为什么说反射是框架的基石? 没有反射,Spring会变成什么样?
  2. JDK动态代理为什么只能代理接口? 如果要代理类该怎么做?
  3. Class.forName()ClassLoader.loadClass() 有什么区别?

下篇预告 👉

下一篇:30-注解与元编程 ------ 深入学习Java注解的定义、处理和元编程技巧,理解Spring框架是如何通过注解驱动的!

参考资料

相关推荐
San813_LDD1 小时前
[数据结构]共享栈与双端队列:算法思想分析及C语言实现
java·开发语言·数据结构
우리帅杰1 小时前
【AI测试】Python AI大模型介绍
开发语言·人工智能·python·ai编程
我是一颗柠檬1 小时前
【Java项目技术亮点】全链路分层限流:从网关到数据库的多层防护体系
java·开发语言·数据库
The Sheep 20231 小时前
C#多线程学习
开发语言·学习·c#
Shadow(⊙o⊙)1 小时前
QT常用控件1.0,enabled() geometry() QIcon的.qrc文件导入
开发语言·c++·qt
geovindu1 小时前
python: Generators Pattern
开发语言·python·设计模式·生成器模式
wuminyu1 小时前
Java锁膨胀机制之偏向锁到轻量级锁源码剖析
java·linux·c语言·jvm·c++
没有不重的名么1 小时前
spyder使用教程
开发语言·python
阿正的梦工坊1 小时前
【Rust】06-函数、控制流与模块组织
开发语言·算法·rust