constructor.newinstance() 抛 illegalaccessexception 或 instantiationexception 是因 java 9+ 默认禁止反射访问非公开构造器,需用 setaccessible(true) 或 --add-opens;抽象类/接口无法实例化属正常;应优先使用 constructor.newinstance(args...) 而非 constructor.newinstance()。

Constructor.newInstance() 为什么总抛 IllegalAccessException 或 InstantiationException
Java 9+ 默认禁止反射访问非公开构造器,哪怕在同一个模块里;newInstance() 已被标记为废弃,底层会强制检查封装性。不是代码写错了,是 JVM 主动拦住了。
- 用
setAccessible(true)绕过检查——但仅对非模块化项目或显式开放的包有效;模块化环境下必须配--add-opens - 优先改用
constructor.newInstance(args...)(注意不是Constructor.newInstance()),它不走旧版反射入口,兼容性更好 - 如果目标类是抽象类或接口,
InstantiationException是预期行为,别试图绕过,该换设计就换设计
Field.set() 失败却没报错?可能是 final 字段或值类型自动装箱问题
反射修改 final 字段在 Java 14+ 被彻底禁用(即使 setAccessible(true) 也无效);而给 int 字段赋 Integer 值时,JVM 不会自动拆箱,直接抛 IllegalArgumentException。
- 检查字段是否带
final修饰符——真要改,得用 Unsafe(不推荐)或换运行时字节码增强方案 - 用
field.getType()对比实际值类型,int.class字段不能传Integer.valueOf(42) - 基本类型字段建议统一用
setInt()/setBoolean()等专用方法,避免类型擦除带来的隐式转换陷阱
Method.invoke() 在 Lambda 表达式里调用失败,常见于 SerializedLambda 场景
通过 MethodHandles.lookup().unreflect() 或序列化获取的 SerializedLambda,其 implMethod 指向的是合成的私有方法(如 lambda$xxx<p>通过 <code>MethodHandles.lookup().unreflect() 或序列化获取的 SerializedLambda,其 implMethod 指向的是合成的私有方法(如 lambda$xxx$0),不是原始源码里的 public 方法。
- 不要硬编码方法名去
getDeclaredMethod("lambda$xxx$0", ...)——名字不稳定,编译器可能重命名 - 从
SerializedLambda解析出implClass和implMethodName后,仍需用getDeclaredMethod()+setAccessible(true) - 若目标方法是默认接口方法,Java 8 反射能调,但 Java 16+ 需确保接口所在模块已导出且未被封印
反射性能差是事实,但热点路径上真正拖慢的往往不是 invoke() 本身
实测显示,单次 Method.invoke() 开销约 100–300ns,而反复调用 getMethod() 或 getDeclaredField() 查找成员,每次都要遍历类结构、做安全检查,开销高一个数量级。
立即学习“Java免费学习笔记(深入)”;
- 把
Method/Field实例缓存起来(比如用ConcurrentHashMap),别每次临时查 - 避免在循环里调
clazz.getDeclaredMethods()——这会触发完整反射元数据加载 - Android 上还要额外注意:ART 运行时对反射有额外校验,
setAccessible(true)成功率比 JVM 低,建议预热或 fallback 到注解处理器生成代理
反射的边界其实很清晰:它不是通用工具,而是给框架层留的逃生舱口。一旦你发现自己在业务逻辑里频繁判断 method.getParameterCount() == 2,说明该收手了。










