需用getdeclaredconstructor()获取私有构造器,再调用setaccessible(true)绕过访问检查,最后用newinstance()实例化;注意参数类型严格匹配、捕获invocationtargetexception并解包getcause()。

怎么用 getDeclaredConstructor() 获取私有构造器
Java 反射中,getConstructor() 只能拿到 public 构造器,想实例化带 private 修饰的构造器(比如单例、工具类、含校验逻辑的类),必须用 getDeclaredConstructor()。它不关心访问权限,但默认被 JVM 拦住 —— 所以紧接着得调用 setAccessible(true) 才能绕过检查。
常见错误是只调用了 getDeclaredConstructor() 却忘了 setAccessible(true),结果抛出 java.lang.IllegalAccessException。
- 参数类型必须严格匹配:比如构造器是
MyClass(String, int),就得传String.class, int.class,不能写Integer.class - 基本类型要用对应 Class 对象:
int.class,不是Integer.class;否则会报NoSuchMethodException - 获取后立即
setAccessible(true),别等到newInstance()时才设 —— 否则异常堆栈里看不到明确提示
用 constructor.newInstance() 创建实例要注意什么
newInstance() 是 Java 9 之前最常用的实例化方式,但它在 Java 9+ 已标记为 @Deprecated,虽然还能用,但底层实际委托给 Unsafe.allocateInstance() 的替代逻辑,稳定性不如显式调用 Constructor::newInstance。
更关键的是:如果目标类的构造器抛出受检异常(比如 IOException),反射调用会把它包装成 InvocationTargetException,原异常藏在 .getCause() 里 —— 很多人直接打印异常却没解包,导致排查困难。
立即学习“Java免费学习笔记(深入)”;
- 推荐统一用
constructor.newInstance(args...),而非已废弃的无参clazz.newInstance() - 捕获
InvocationTargetException并检查e.getCause(),才能看到真实报错 - 若构造器本身抛
RuntimeException(如空指针、NPE),它会原样抛出,不被包装
调用私有方法前为什么必须先 setAccessible(true)
和构造器一样,getDeclaredMethod() 能拿到 private 方法,但不设 setAccessible(true) 就调用 invoke(),一定会触发 java.lang.IllegalAccessException。这不是 JVM 的“安全警告”,而是硬性拦截 —— 它发生在 native 层,无法通过 try-catch 绕过。
容易忽略的一点:即使方法是 static,也得走这套流程。有人误以为 static 方法不用实例就能调,所以可以跳过 setAccessible,这是错的。
-
setAccessible(true)必须在invoke()前调用,且对每个Method对象单独设置 - 调用 static 方法时,
invoke()第一个参数传null即可,但不能传任意对象 - 某些 JDK 版本(如 JDK 17+)在安全管理器启用时会彻底禁止
setAccessible(true),需确认运行环境是否允许
反射调用性能差,但有些场景真没法绕开
反射慢是事实:方法查找、访问检查、参数封装、异常包装都会带来开销。JIT 编译器对反射调用优化有限,尤其是首次调用。不过,在框架层(如 Spring Bean 初始化、Jackson 反序列化)或测试辅助(Mockito 操作私有字段)中,它仍是不可替代的底层能力。
真正该警惕的不是“用了反射”,而是“高频、重复、未缓存地用”。比如每次 HTTP 请求都重新 getDeclaredMethod() + setAccessible(true),而不是把 Method 实例缓存起来复用。
- 把
Constructor、Method对象作为静态 final 字段缓存,避免重复查找 - 注意
setAccessible(true)在 JDK 12+ 后会触发警告(可通过--illegal-access=permit临时压制,但非长久之计) - 如果只是读写字段,考虑
Unsafe或 VarHandle(JDK 9+),它们更快也更底层,但门槛更高、兼容性更敏感










