Java反射原理以及使用
Contents
💠
-
- 3.1. Inflation
-
- 4.1. AccessibleObject
- 4.2. Annotation
- 4.3. Class
- 4.4. Field
- 4.5. Method
- 4.6. Constructor
- 4.7. Modifier
💠 2024-11-07 19:58:31
反射
Reflection is powerful, but should not be used indiscriminately.
If it is possible to perform an operation without using reflection, then it is preferable to avoid using it.
参考: java8–类加载机制与反射(java疯狂讲义3复习笔记) 参考: Java8替代传统反射动态获取成员变量值的一个示例
概念
在运行时 反射使程序能够在运行时探知类的结构信息:构造器,方法,字段… 并且依赖这些结构信息完成相应的操作,比如创建对象,方法调用,字段赋值…
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
实现原理
Inflation
考虑到许多反射调用仅会执行一次,Java 虚拟机设置了一个阈值 15(可以通过 -Dsun.reflect.inflationThreshold= 来调整),当某个反射调用的调用次数在 15 之下时,采用本地实现;
当达到 15 时,便开始动态生成字节码,并将委派实现的委派对象切换至动态实现,这个过程称之为 Inflation。
反射代理类加载器的潜在内存使用问题 - 简书
Inflation 引起的 MetaSpace Full GC 问题排查 - 腾讯云开发者社区-腾讯云
基础类
Java 反射的实现类
AccessibleObject
The AccessibleObject class is the base class for Field, Method and Constructor objects. It provides the ability to flag a reflected object as suppressing default Java language access control checks when it is used.
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为 具有在使用时禁止Java语言的
默认访问控制检查
的能力。
对于公共成员、默认(包级别)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。
将此对象的 accessible 标志设置为指示的布尔值。
true 表示反射的对象在使用时应该取消 Java 语言访问检查, 反之则要进行。
此标志不会告诉您java访问修饰符是否可以访问该字段,它会告诉您当前是否忽略这些修饰符。
实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问,为false就不能访问,一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,
但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject
上的setAccessible(true)
方法来允许这种访问,
而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。
但有的时候这将会成为一个安全隐患,为此,我们可以启用java.security.manager
来判断程序是否具有调用setAccessible()的权限。
默认情况下,
内核API
和扩展目录
的代码具有该权限,而类路径
或通过URLClassLoader加载
的应用程序不拥有此权限。
- 仍然存疑, 什么情况下才是 默认可访问的。什么情况下 true 不能访问
Annotation
Class
Field
Method
Constructor
Modifier
The Modifier class provides static methods and constants to decode class and member access modifiers.
API: modifier
Java的访问权限信息是以2的N次幂也就是bitmap的方式进行存储 一共有12个常用修饰符 也就使用了12位来标记
|
|
- 判断属性是否被 final 修饰
Modifier.isFinal(field.getModifiers())
- 移除 final 修饰符
field.getModifiers() & ~Modifier.FINAL
|
|
使用
具有的功能
- 在运行时获取任意一个类所具有的成员变量和方法以及泛型类型;
- 在运行时构造任意一个类的对象;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
泛型擦除的存在, 但是泛型如果被用来进行声明, 类上,字段上,方法参数和方法返回值上,这些属于类的结构信息其实是会被编译进Class文件中的;
而泛型如果被用来使用,常见的方法体中带泛型的局部变量,其类型信息不会被编译进Class文件中。
前者因为存在于Class文件中,所以运行时通过反射还是能够获得其类型信息的;
获取Class对象的方式
所有的反射操作的入口都是从Class对象开始的, 获取Class对象有多种方式
- 通过类加载器加载class文件
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass("com.takumiCX.reflect.ClassTest");
- 通过静态方法 Class.forName() 获取,需要传入类的全限定名字符串作参数
Class<?> clazz = Class.forName("com.takumiCX.reflect.ClassTest");
- 通过 类.class 获得类的Class对象
Class<ClassTest> clazz = ClassTest.class;
除了获得的Class对象的泛型类型信息不一样外,还有一个不同点值得注意。只有 forName() 方式 在获得class对象的同时会引起类的初始化
反射的基本使用
操作构造方法
使用newInstance()操作无参构造方法
使用Class类中的newInstance()方法进行实例化操作, 但该方法必须要求类有无参构造方法
使用Class类中的getConstructors()获取所有构造方法
|
|
使用Class类中的getConstructor获取指定参数类型的构造方法
|
|
操作类中方法
getDeclaredMethods()
获取类本身定义的所有方法, 不包含由继承获取到的方法
|
|
获取指定的方法
|
|
getMethods()
获取所有方法, 包含由继承获取到的方法, 但无法取得自身私有方法
|
|
获取指定的方法
|
|
调用方法
|
|
操作类的成员属性
取得所有成员
|
|
获取单个成员
|
|
取得所有成员, 包含由继承获取的成员, 但无法取得自身私有成员
|
|
set 和 get 属性的值
|
|
操作注解
获取类的注解
|
|
获取指定的Annotation
|
|
正常情况下 final修饰的类,变量,方法, 表示不可继承,不可修改,不可重写(override), 但是使用反射能在一定程度上进行修改
被final修饰过的变量,只是说栈存储的地址不能再改变,但是却没有说地址指向的内容不能改变,所以反射可以破final,因为它修改该了以前地址的具体内容,但是没有改地址的信息。
参考 JavaDoc: Java8
Field.set()
的文档说明
反射的性能问题
Spring 中的 IOC 主要是依据反射来实现的, 只在启动阶段性能有所损耗, 关注性能以及热点代码最好避免使用反射 例如常见的BeanCopy
从一起GC血案谈到反射原理总结: Method等Accessor对象每次get时会复制构造出新的对象,所以一般需要缓存; 反射数据是软引用
优化方案
Author Kuangcp
LastMod 2018-11-21