Java 反射现代实践速查表(JDK 11+/17+)

Java 反射现代实践速查表(JDK 11+/17+)

适用人群:想快速上手/查阅反射写法的 Java 工程师。\ 运行环境:建议 JDK 11+,最佳 JDK 17/21。\ 关键词:ClassFieldMethodConstructorMethodHandleVarHandleProxyType


目录

    1. 反射是什么
    1. 获取 Class 的三种方式
    1. 现代 实例化对象(替代 Class.newInstance()
    1. 访问字段 Field
    1. 调用方法 Method
    1. 构造器 Constructor
    1. 注解与参数信息
    1. 泛型反射:Type 家族
    1. JDK 9+ 模块化与反射访问(--add-opens/opens
    2. 动态代理 Proxy
    3. MethodHandle / VarHandle(高性能反射)
    4. 性能优化与缓存
    5. 安全要点与限制
    6. 常见异常与排错速览
    7. 实用片段(可直接复用)
    8. 最佳实践清单
    9. 延伸阅读

1. 反射是什么

  • 运行时检查 类型结构(类/字段/方法/构造器/注解),并可读写字段调用方法创建对象
  • 常用于:依赖注入、ORM、序列化/反序列化、AOP、测试工具、插件系统等。

2. 获取 Class 的三种方式

plain 复制代码
Class<?> c1 = String.class;                  // 字面量
Class<?> c2 = "Hello".getClass();            // 实例 -> Class
Class<?> c3 = Class.forName("java.lang.String"); // 全限定名加载

小贴士:基本类型与数组也有 Classint.classString[].class


3. 现代 实例化对象(替代 Class.newInstance()

不要再用 clazz.newInstance()(JDK 9+ 已弃用)。\ 推荐使用无参构造器的标准替代:

plain 复制代码
MyType obj = MyType.class.getDeclaredConstructor().newInstance();
  • 有参构造:
plain 复制代码
Constructor<MyType> ctor = MyType.class.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true);           // 若非 public
MyType obj = ctor.newInstance("Ocean", 42);

说明:getDeclaredConstructor 能拿到非 public 构造;遇到访问限制需结合模块化策略(见 §9)。


4. 访问字段 Field

plain 复制代码
Field f = MyType.class.getDeclaredField("name"); // 含私有
f.setAccessible(true);                           // 破私有(受模块限制)
f.set(obj, "Ocean");
Object val = f.get(obj);
  • 列出所有声明字段(不含父类):
plain 复制代码
for (Field fd : MyType.class.getDeclaredFields()) {
    fd.setAccessible(true);
    System.out.println(fd.getName() + " : " + fd.getType());
}

5. 调用方法 Method

plain 复制代码
Method m = MyType.class.getDeclaredMethod("sayHello", String.class);
m.setAccessible(true);
Object ret = m.invoke(obj, "World");
  • 列出 public 方法(含继承):
plain 复制代码
for (Method pm : MyType.class.getMethods()) {
    System.out.println(pm);
}

6. 构造器 Constructor

  • 获取并调用:
plain 复制代码
Constructor<MyType> ctor = MyType.class.getDeclaredConstructor();
ctor.setAccessible(true);
MyType instance = ctor.newInstance();
  • 列出所有声明构造器:
plain 复制代码
for (Constructor<?> c : MyType.class.getDeclaredConstructors()) {
    System.out.println(c);
}

7. 注解与参数信息

plain 复制代码
// 类、字段、方法上的注解
MyAnno anno = MyType.class.getAnnotation(MyAnno.class);

// 方法参数名(需编译参数 -parameters)
Method m = MyType.class.getDeclaredMethod("foo", String.class, int.class);
for (Parameter p : m.getParameters()) {
    System.out.println(p.getName() + " : " + p.getType());
}

8. 泛型反射:Type 家族

plain 复制代码
Field f = Repo.class.getDeclaredField("data"); // e.g. Map<String, List<User>>
Type t = f.getGenericType();
if (t instanceof ParameterizedType pt) {
    Type raw = pt.getRawType();          // Map
    Type[] args = pt.getActualTypeArguments(); // [String, List<User>]
}
  • 常见 Type 子接口:
    • Class<?>:普通类型
    • ParameterizedType:带泛型参数的类型,如 List<String>
    • TypeVariable:类型变量,如 T
    • WildcardType:通配符,如 ? extends Number
    • GenericArrayType:泛型数组,如 T[]

9. JDK 9+ 模块化与反射访问(--add-opens/opens

  • 现象 :对非导出包 的私有成员做反射(哪怕 setAccessible(true)),会抛 IllegalAccessException 或非法反射访问警告/失败。
  • 解决
    1. 运行时打开包(适合应用层调试/临时兼容)
plain 复制代码
java --add-opens my.module/com.example.internal=ALL-UNNAMED -jar app.jar
markdown 复制代码
2. **模块描述符中显式开放**(长期方案) 
plain 复制代码
// module-info.java
open module my.module {
    // 或者有选择地 opens 某些包
    // opens com.example.internal to other.module;
}
  • 注意 :JDK 17+ 对 JDK 内部 API 强封装更严格;尽量依赖标准 API,不要反射 JDK 内部包。

10. 动态代理 Proxy

  • 在运行期为接口生成代理对象,常用于 AOP、拦截器、RPC Stub。
plain 复制代码
interface Hello { String hi(String name); }

InvocationHandler h = (proxy, method, args) -&gt; {
    if (method.getName().equals("hi")) {
        return "[LOG] " + args[0];
    }
    return method.invoke(proxy, args);
};

Hello hello = (Hello) Proxy.newProxyInstance(
        Hello.class.getClassLoader(),
        new Class&lt;?&gt;[]{Hello.class},
        h
);

System.out.println(hello.hi("Ocean")); // [LOG] Ocean

如果需要代理(非接口),可使用 ByteBuddy、CGLIB、Javassist 等字节码库。


11. MethodHandle / VarHandle(高性能反射)

  • **MethodHandle**:更接近直接调用的可链接句柄,类型安全,常比 Method.invoke 更快。
plain 复制代码
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(MyType.class, "sayHello",
        MethodType.methodType(String.class, String.class));
String r = (String) mh.invoke(obj, "World");
  • **VarHandle**(JDK 9+):字段/数组/字节缓冲区的"变量句柄",支持原子/内存栅栏等操作:
plain 复制代码
VarHandle NAME;
static {
    try {
        NAME = MethodHandles.lookup()
               .in(MyType.class)
               .findVarHandle(MyType.class, "name", String.class);
    } catch (Exception e) { throw new RuntimeException(e); }
}
NAME.set(obj, "Ocean");
String v = (String) NAME.get(obj);

一般规则:频繁调用/性能敏感 → 优先考虑 MethodHandle/VarHandle;一次性工具/低频 → 传统反射 API 足够。


12. 性能优化与缓存

  • 缓存反射元数据Field/Method/Constructor 查找与 setAccessible 代价不小,建议按类型做缓存。
  • 避免热路径反射 :把反射调用移出循环,或转换为 MethodHandle 并缓存句柄。
  • JIT 热身:基准测试前做预热,避免冷启动干扰。

13. 安全要点与限制

  • 反射破坏封装,需谨慎对外暴露;对不可信输入(类名/成员名)务必校验白名单。
  • 受限环境(安全管理器已废弃但仍有沙箱/容器策略)可能禁止反射访问。
  • JDK 内部包的访问在现代 JDK 中被强封装,不要依赖非标准内部 API。

14. 常见异常与排错速览

  • ClassNotFoundException:类名或类路径错误。
  • NoSuchMethodException / NoSuchFieldException:签名或名称不匹配(包含参数类型/顺序)。
  • IllegalAccessException:访问权限/模块未 opens
  • InvocationTargetException:被调用方法抛出的异常被包装在此处。
  • InaccessibleObjectException(JDK 16+ 常见):模块强封装导致;用 --add-opensopens 解决。

15. 实用片段(可直接复用)

15.1 安全获取无参构造实例(现代写法)

plain 复制代码
public static &lt;T&gt; T newInstance(Class&lt;T&gt; type) {
    try {
        Constructor&lt;T&gt; c = type.getDeclaredConstructor();
        c.setAccessible(true);
        return c.newInstance();
    } catch (ReflectiveOperationException e) {
        throw new IllegalStateException("Cannot instantiate: " + type.getName(), e);
    }
}

15.2 读/写私有字段(带模块检查提示)

plain 复制代码
public static Object readField(Object target, String name) {
    try {
        Field f = target.getClass().getDeclaredField(name);
        f.setAccessible(true); // 若报 InaccessibleObjectException,参见 §9
        return f.get(target);
    } catch (ReflectiveOperationException e) {
        throw new IllegalStateException(e);
    }
}

15.3 遍历声明方法(含注解)

plain 复制代码
for (Method m : clazz.getDeclaredMethods()) {
    m.setAccessible(true);
    System.out.println(m.getName());
    for (Annotation a : m.getAnnotations()) {
        System.out.println("  @" + a.annotationType().getSimpleName());
    }
}

15.4 解析字段的泛型参数

plain 复制代码
public static List&lt;Class&lt;?&gt;&gt; genericArgsOfField(Field f) {
    Type t = f.getGenericType();
    if (t instanceof ParameterizedType pt) {
        List&lt;Class&lt;?&gt;&gt; list = new ArrayList&lt;&gt;();
        for (Type arg : pt.getActualTypeArguments()) {
            if (arg instanceof Class&lt;?&gt; cls) list.add(cls);
            // 可扩展处理 WildcardType/TypeVariable 等
        }
        return list;
    }
    return List.of();
}

15.5 缓存 MethodHandle

plain 复制代码
final class Handles {
    private static final Map&lt;String, MethodHandle&gt; CACHE = new ConcurrentHashMap&lt;&gt;();
    static MethodHandle vh(Class&lt;?&gt; c, String name, Class&lt;?&gt; ret, Class&lt;?&gt;... params) {
        return CACHE.computeIfAbsent(c.getName() + "#" + name, k -&gt; {
            try {
                return MethodHandles.lookup().findVirtual(c, name, MethodType.methodType(ret, params));
            } catch (Throwable e) { throw new RuntimeException(e); }
        });
    }
}

16. 最佳实践清单

  • 实例化getDeclaredConstructor().newInstance() 取代 Class.newInstance()
  • 模块化 :跨模块反射请使用 --add-opens 或在 module-info.javaopens
  • 性能 :高频反射改用 MethodHandle/VarHandle,并缓存句柄/成员。
  • 健壮性 :明确捕获 ReflectiveOperationException 分支,封装为业务异常。
  • 安全:对外暴露的"类名/成员名"必须白名单校验;不要反射 JDK 内部包。
  • 可维护性 :优先接口与多态,反射只用于必要环节。

17. 延伸阅读


相关推荐
玉衡子2 小时前
九、MySQL配置参数优化总结
java·mysql
叽哥2 小时前
Kotlin学习第 8 课:Kotlin 进阶特性:简化代码与提升效率
android·java·kotlin
麦兜*2 小时前
MongoDB Atlas 云数据库实战:从零搭建全球多节点集群
java·数据库·spring boot·mongodb·spring·spring cloud
带刺的坐椅2 小时前
DamiBus v1.1.0 发布(给单体多模块解耦)
java·事件总线·damibus
葡萄城技术团队2 小时前
用 Java 构建健壮 REST API 的 4 个关键技巧
java
杨杨杨大侠2 小时前
解密 atlas-mapper 框架 (9/10):故障排查与调试技巧
java·开源·github
Slaughter信仰2 小时前
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)第十章知识点问答(10题)
java·jvm·数据库
麦兜*2 小时前
MongoDB 在物联网(IoT)中的应用:海量时序数据处理方案
java·数据库·spring boot·物联网·mongodb·spring
SimonKing3 小时前
【工具库推荐】Java开发者必备:6款HTTP客户端神器,从经典到未来
java·后端·程序员