Java 反射现代实践指南(JDK 11+ / 17+ 适用)

下面是一篇基于最新 JDK 实践,内容涵盖:概念 → 基本 API → JDK 9+ 变化 → 安全与性能 → 最佳实践,并附示例代码。


✅ Java 反射现代实践指南(JDK 11+ / 17+ 适用)


1. 什么是反射?

反射(Reflection) 是 Java 提供的一种机制,允许程序在运行时动态地:

  • 检查类的结构(字段、方法、构造器、注解等)
  • 访问和修改对象属性
  • 调用方法
  • 创建实例

它打破了编译期的静态类型限制,使框架(如 Spring、MyBatis)能够实现依赖注入、ORM 映射等功能。


2. 反射的核心 API

反射主要位于 java.lang.reflect 包,核心类包括:

  • Class<T>:类型元信息入口
  • Field:字段
  • Method:方法
  • Constructor<T>:构造器

2.1 获取 Class 对象

perl 复制代码
Class&lt;?&gt; clazz1 = String.class;
Class&lt;?&gt; clazz2 = "Hello".getClass();
Class&lt;?&gt; clazz3 = Class.forName("java.lang.String");

2.2 创建实例(现代写法)

不要再用 Class.newInstance() ,它在 JDK 9 起已弃用。

✅ 推荐:

ini 复制代码
MyClass obj = MyClass.class.getDeclaredConstructor().newInstance();

原因:

  • 支持异常细分(ReflectiveOperationException
  • 与模块化访问控制兼容

2.3 访问字段

csharp 复制代码
Field f = MyClass.class.getDeclaredField("name");
f.setAccessible(true); // 破私有(需注意模块限制)
f.set(obj, "Ocean");
System.out.println(f.get(obj));

2.4 调用方法

ini 复制代码
Method m = MyClass.class.getDeclaredMethod("sayHello", String.class);
m.setAccessible(true);
String result = (String) m.invoke(obj, "World");
System.out.println(result);

3. JDK 9+ 模块化带来的变化

3.1 强封装(JEP 261 & JEP 403)

  • JDK 9 引入模块系统,非导出包的反射访问会报 IllegalAccessException
  • JDK 17 起(JEP 403),JDK 内部 API 强封装,不能随意反射访问。

3.2 如何解决?

  • 启动参数添加 --add-opens
csharp 复制代码
java --add-opens java.base/java.lang=ALL-UNNAMED ...
  • 或在 module-info.java 中显式 opens

4. 性能与安全

  • 性能开销:反射调用比直接调用慢 10~20 倍(JIT 优化后仍有差距)。

  • 安全风险setAccessible(true) 绕过封装,可能破坏模块边界。

  • 最佳实践

    • 避免在高频路径使用反射。
    • 可用 MethodHandle(Java 7+)或 VarHandle(Java 9+)替代,性能更好。

5. 现代最佳实践清单

实例化

scss 复制代码
clazz.getDeclaredConstructor().newInstance();

跨模块访问

  • 使用 --add-opensmodule-info.javaopens

性能敏感场景

  • 优先 MethodHandle
ini 复制代码
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(MyClass.class, "sayHello",
    MethodType.methodType(String.class, String.class));
String result = (String) mh.invoke(obj, "World");

避免滥用反射

  • 能用接口/多态解决的,不要用反射。

6. 实战示例:动态调用方法

arduino 复制代码
public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        Class&lt;?&gt; clazz = Class.forName("com.example.MyClass");
        Object obj = clazz.getDeclaredConstructor().newInstance();

        Method m = clazz.getDeclaredMethod("sayHello", String.class);
        m.setAccessible(true);
        String result = (String) m.invoke(obj, "Ocean");
        System.out.println(result);
    }
}

7. 延伸阅读


相关推荐
就叫_这个吧4 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡4 小时前
AI 应用系统设计
java·开发语言·人工智能
城管不管4 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅4 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
AI绘画哇哒哒4 小时前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent
最强小杰5 小时前
gpt-5.6-sol 频繁报 503 怎么办?区分容量熔断和限速 429 的排查方法 + 可复用 retry wrapper
java·人工智能·gpt·ai
吠品7 小时前
Wine 在 Linux 上运行 Windows 软件完整指南
java·linux·服务器
亚历克斯神8 小时前
智能搜索系统的升级复盘——从 Elasticsearch 到混合检索的检索质量提升
java·spring·微服务
金銀銅鐵9 小时前
[Java] 一个方法最多可以有多少个入参?
java·jvm
哭哭啼9 小时前
JAVA服务问题诊断
java·开发语言·jvm