加载中

反射

文章分类

浏览该分类下的所有文章

8 篇文章 1

反射机制:原理、应用与最佳实践

Java反射允许在运行时获取类的元信息并操作对象,核心API包括Class、Field、Method、Constructor,可通过类名、对象getClass()或Class.forName()获取Class实例。常用于动态代理、框架(如Spring、Hibernate)和单元测试,支持创建实例、访问私有成员和调用方法。反射虽提升灵活性,但因运行时解析导致性能下降并破坏封装,存在安全风险。最佳实践是限制使用范围、缓存Class对象以提升效率、在敏感操作时使用AccessController.doPrivileged()。合理使用可平衡灵活性与性能安全。

反射的基本概念

反射是一种在运行时动态获取、创建、检查和调用类及其成员(包括 private)的机制,核心类为 Class、Constructor、Method、Field。它可用于突破访问限制、实现自定义注解、动态加载第三方 jar、按需加载以缩短编译和初始化时间。实现原理是 ClassLoader 将 .class 文件加载后生成 Class 对象,反射通过上述四个类操作这些对象。优点是灵活自由,缺点包括性能下降、破坏封装导致安全风险以及 API 变动时的兼容性问题。适度、按需使用可将影响降至可接受范围。

每日一题(2022/1/19)

本文给出一个面试题:在保持 `String s` 引用不变的前提下,使其输出由 `"abc"` 变为 `"abcd"`。常规做法如 `StringBuilder.append` 或 `String.replace` 均无效,因为它们不会修改原对象。正确做法是利用反射访问并修改 `String` 的私有 final 字段 `value`:先通过 `getDeclaredField("value")` 设置可访问,再用 `set(s, "abcd".toCharArray())`。但在 JDK 11 及以上运行时会抛出 IllegalArgumentException,提示非法的反射访问并禁止对 final 字段赋值,这是因为 JVM 在编译期已将 `String` 常量化并强化了访问限制。

CGLIB与JDK动态代理

CGLIB和JDK动态代理都是为了解耦而产生的代理技术。JDK代理只能为实现接口的类生成基于接口的代理,需要实现InvocationHandler并使用反射;CGLIB则利用ASM字节码框架为目标类生成子类,覆盖非final方法,可对没有接口的类进行代理。Spring默认:bean实现接口时使用JDK代理,未实现接口时使用CGLIB,亦可强制指定。性能上,JDK在JDK6+及调用次数少时更快,CGLIB在大量调用且JDK8以前可能占优,但对final类/方法无能为力。CGLIB的实现核心是Enhancer.create生成子类并通过MethodInterceptor拦截方法,适用于AOP等场景。

isAssignableFrom()方法与instanceof关键字

isAssignableFrom()和instanceof都用于类型判断,但侧重点不同。isAssignableFrom()在类层面比较,调用者是父类Class,参数是子类或自身Class,用于判断一个类是否可以赋值给另一个类;instanceof在实例层面比较,左侧是对象实例,右侧是其类或父类类型,用于判断对象是否是某类或其子类的实例。文章通过接口、父类、子类的代码示例展示两者的调用方式和输出结果,说明isAssignableFrom()判断“是否为父类”,instanceof判断“是否为子类”。

JVM之RTTI与反射

RTTI(运行时类型识别)通过获取 Class 对象来在运行时获取已知类的完整信息,获取方式包括 Class.forName、.class 和对象的 getClass(),前者会立即初始化类,后者在首次使用静态成员时才初始化。反射则用于编译时未知的类:在运行时加载对应的 .class 文件后,可通过 Class 的 getMethods、getConstructors 等获取 Method、Constructor 对象并实例化对象。核心区别在于 RTTI 需要编译期已知类名并在编译时检查 .class,而反射在运行时才打开并检查 .class,实现对未知类型的动态操作。

getField和getDeclaredField的区别

getField只能获取public字段且包括父类继承的,而getDeclaredField可以获取本类所有字段(包括private),但不返回继承的字段。示例中通过反射分别访问Person的private age(需setAccessible(true))和public money,演示读取和赋值。反射优点是运行时动态获取类信息,提高系统灵活性;缺点是性能较低、安全性差且破坏封装。

Class对象的创建方式

本文演示了在 Java 中获取 Class 对象的五种常用方式:①通过实例调用 getClass();②使用 Class.forName(全限定名);③直接使用类字面量 .class;④通过基本类型包装类的 TYPE 字段获取原始类型 Class;⑤利用已有 Class 对象的 getSuperclass() 获取父类的 Class。代码示例分别展示了每种方法的使用及输出结果,帮助读者理解反射机制中 Class 对象的获取途径。