​​Java 反射教程 JDK 25 实战​ - 实践

​​​一 核心概念与适用场景​

  • ​反射(Reflection)​​ 是 ​​JVM​​ 在运行时获取类结构并操作对象的能力,核心入口是 ​​java.lang.Class​​,配合 ​​java.lang.reflect​​ 包中的 ​​Method、Field、Constructor​​ 等类完成元数据读取与动态调用。典型能力包括:动态创建实例、访问/修改字段(含私有)、调用方法(含私有)、获取注解、动态处理数组与泛型签名等。
  • ​适用场景​​:框架与库的底层能力(如 ​​Spring IOC/AOP、JUnit 测试发现、Jackson/Gson 序列化、IDE 调试与展示​​)、​​动态代理​​(JDK 动态代理基于反射)、插件化与热加载、序列化/反序列化与对象映射等。
  • ​代价与风险​​:反射会绕过部分编译期优化,通常比直接调用慢;通过 ​​setAccessible(true)​​ 可突破访问控制,可能破坏封装与安全策略;反射代码更难静态分析与维护。自 ​​Java 9 模块系统​​ 起,对反射访问非公开成员施加了更严格的限制,需要在模块描述符显式开放。
  • ​JDK 25 提示​​:语言与平台特性保持稳定,反射 API 基本用法与 ​​JDK 8+​​ 一致;但在模块化项目(module-info.java)中,跨模块访问非公开成员必须声明 ​​opens/exports​​,否则会抛出 ​​InaccessibleObjectException​​。

​二 环境准备与模块化要点​

  • ​JDK 25 安装与运行​​:确保 java -version 输出包含 ​​25​​;编译与运行使用 javac --release 25java --release 25(或省略 --release 使用默认版本)。
  • ​模块化项目结构​​(推荐):
    src/
    ├── module-info.java
    └── com.example.reflection/
        ├── model/Person.java
        └── demo/ReflectionDemo.java
  • ​module-info.java 示例​​(允许反射访问包内成员):
    module com.example.reflection {
        requires java.base; // 默认依赖,显式书写更清晰
        // 如需被其他模块反射访问本模块的非公开成员:
        // opens com.example.reflection.model to java.base;
    }
  • ​未使用模块(classical classpath)​​:不受模块边界限制,但应避免在生产中滥用反射访问私有成员。
  • ​跨模块反射要点​​:目标类所在模块需对调用者模块 ​​opens 包​​;若仅访问公共 API,通常无需 opens,但涉及字段/方法/构造器的私有访问仍需 ​​setAccessible(true)​​ 且在模块环境下可能受限。

​三 核心 API 与 JDK 25 实战代码​

  • ​获取 Class 对象的三种方式​​:类.class对象实例.getClass()Class.forName("全限定名")

  • ​构造实例​​:优先使用 ​​Constructor.newInstance()​​(支持私有构造器);旧式 Class.newInstance() 仅能调用无参公有构造器且已不推荐。

  • ​字段操作​​:getDeclaredField/Field[] 获取字段,setAccessible(true) 后用 set/get 读写(含私有)。

  • ​方法调用​​:getDeclaredMethod/Method[] 获取方法,setAccessible(true) 后用 invoke 调用(含私有)。

  • ​数组操作​​:使用 ​​java.lang.reflect.Array​​ 动态创建与访问。

  • ​泛型信息​​:通过 ​​getGenericType()​​ 与 ​​ParameterizedType​​ 获取字段/方法的泛型签名(类型擦除后仍保留签名信息)。

  • 示例代码(放在 src/com/example/reflection/demo/ReflectionDemo.java):

    package com.example.reflection.demo;
    import com.example.reflection.model.Person;
    import java.lang.reflect.*;
    import java.util.Arrays;
    public class ReflectionDemo {
        public static void main(String[] args) {
            try {
                // 1) 获取 Class 对象
                Class clazz = Person.class; // 也可 Class.forName("com.example.reflection.model.Person")
                // 2) 构造实例(公有构造器)
                Constructor noArgsConstructor = clazz.getDeclaredConstructor();
                noArgsConstructor.setAccessible(true); // 若构造器为 private 才需要
                Person p1 = (Person) noArgsConstructor.newInstance();
                // 3) 带参构造器
                Constructor paramConstructor = clazz.getDeclaredConstructor(String.class, int.class);
                Person p2 = (Person) paramConstructor.newInstance("Alice", 25);
                System.out.println("p2 = " + p2);
                // 4) 访问与修改字段(含私有)
                Field nameField = clazz.getDeclaredField("name");
                nameField.setAccessible(true);
                nameField.set(p1, "Bob");
                System.out.println("p1.name = " + nameField.get(p1));
                // 5) 调用方法(含私有)
                Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);
                setNameMethod.setAccessible(true);
                setNameMethod.invoke(p1, "Charlie");
                System.out.println("p1 after setName = " + p1);
                // 6) 私有方法调用
                Method privateMethod = clazz.getDeclaredMethod("greet", String.class);
                privateMethod.setAccessible(true);
                String greeting = (String) privateMethod.invoke(p1, "Reflection");
                System.out.println("greet result = " + greeting);
                // 7) 数组操作
                Class intArrayClass = int[].class;
                Object arr = Array.newInstance(int.class, 3);
                Array.set(arr, 0, 100);
                Array.set(arr, 1, 200);
                Array.set(arr, 2, 300);
                System.out.println("array[1] = " + Array.get(arr, 1));
                // 8) 泛型字段类型读取(示例:List)
                Field listField = clazz.getDeclaredField("tags");
                Type genericType = listField.getGenericType();
                if (genericType instanceof ParameterizedType pt) {
                    Type[] args = pt.getActualTypeArguments();
                    System.out.println("tags 泛型参数: " + Arrays.toString(args)); // [class java.lang.String]
                }
            } catch (NoSuchMethodException | IllegalAccessException |
                     InstantiationException | InvocationTargetException |
                     NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }
  • 对应的模型类(放在 src/com/example/reflection/model/Person.java):

    package com.example.reflection.model;
    import java.util.List;
    public class Person {
        private String name;
        private int age;
        private List tags;
        public Person() {}
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person{name='" + name + "', age=" + age + ", tags=" + tags + '}';
        }
        private void setName(String name) {
            this.name = "[private-set]" + name;
        }
        private String greet(String prefix) {
            return prefix + ", I'm " + name;
        }
    }
  • 运行方式(模块化):

    # 编译
    javac --release 25 -d out src/module-info.java src/com/example/reflection/demo/ReflectionDemo.java src/com/example/reflection/model/Person.java
    # 运行
    java --release 25 -p out -m com.example.reflection/com.example.reflection.demo.ReflectionDemo
  • 预期输出(顺序可能因实现细节略有差异):

    p2 = Person{name='Alice', age=25, tags=null}
    p1.name = Bob
    p1 after setName = Person{name='Charlie', age=25, tags=null}
    greet result = Reflection, I'm Charlie
    array[1] = 200
    tags 泛型参数: [class java.lang.String]
  • 常见异常与处理要点:

    • ​ClassNotFoundException / NoClassDefFoundError​​:类路径或模块路径配置问题。
    • ​NoSuchMethod/Field/ConstructorException​​:签名不匹配(参数类型、参数个数、是否为静态等)。
    • ​IllegalAccessException​​:访问受限;对非公开成员先 setAccessible(true),在模块环境下需 opens
    • ​InvocationTargetException​​:被调用方法内部抛出的实际异常(通过 getCause() 获取)。
    • ​InaccessibleObjectException(JDK 9+)​​:跨模块反射访问非公开成员未开放;在 module-info.java 中添加 opens 包 to 调用者模块

​四 性能优化与最佳实践​

  • ​缓存反射元数据​​:对高频使用的 ​​Method/Field/Constructor​​ 做 static final 缓存,避免重复查找;必要时配合 ClassValue 做按类缓存。
  • ​减少安全检查开销​​:对需要反复调用的非公开成员,调用一次 setAccessible(true) 后复用;在模块环境下,优先考虑设计上的公共 API,减少对私有成员的反射依赖。
  • ​优先公共 API 与标准机制​​:能用接口/继承/注解处理器/MethodHandle(JDK 7+)/LambdaMetafactory(JDK 8+)实现的逻辑,优先选择;它们通常比反射更高效、类型更安全。
  • ​防御性编程​​:对反射调用进行参数校验、空值与异常处理;对 invoke 返回值进行类型检查与转换保护。
  • ​模块化合规​​:跨模块反射访问非公开成员时,在 module-info.java 中显式 opens;对外暴露最小必要包,避免全包开放。
  • ​安全策略​​:在受管环境(如启用安全管理器)下,限制反射权限,避免通过反射破坏单例、绕过访问控制或执行不可信代码。

​五 常见问题与排查清单​

  • ​找不到类或方法​​:核对全限定名、方法名与参数类型;注意 ​​基本类型/包装类型​​ 的差异(如 int.classInteger.class 不同)。
  • ​非法访问异常​​:字段/方法/构造器为非公开时,需 setAccessible(true);在 ​​JDK 9+ 模块​​ 环境下,为目标包添加 opens 到调用者模块。
  • ​模块未读/未导出​​:若目标类不在 java.base,需在 module-info.javarequires 目标模块,并对需要反射访问的包 opens
  • ​泛型擦除导致类型丢失​​:运行时通过 ​​getGenericType() / ParameterizedType​​ 获取字段/方法的泛型签名,避免直接使用 getClass() 获取泛型类型。
  • ​性能瓶颈​​:热点路径避免反射;缓存 ​​Method/Field​​,减少重复查找;必要时用 ​​MethodHandle​​ 或代码生成替代。
posted on 2025-12-04 16:05  ljbguanli  阅读(0)  评论(0)    收藏  举报