Reflection (反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Java 反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在 运行时处理注解
- 生成动态代理
反射相关的主要API
java lang Class
代表一 个类java lang reflect Method
代表类的方法java lang reflect Field
代表类的成员变量java lang reflect Constructor
代表类的构造器
反射的例子
通过反射创建对象、调用公有的属性和方法
1 | Class clazz = User.class; // 获取Class对象 |
通过反射还可以调用私有的属性和方法
1 | // 调用私有的构造器 |
关于java.lang.Class类
类的加载过程:
使用java命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,这个过程就是类的加载。
对于加载到内存中的类,称为运行时类,这个运行时类就作为java.lang.Class
的一个实例。
对于每个类而言, JRE都为其保留一个不变的Class
类型的对象。一个Class
对象包含了特定某个结构class/interface/enum/annotation/primitive type/void/[]
的有关信息 。
- Class本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一 个Class 对象对应的是一个加载到 JVM中的一个class文件
- 每个 类的实例都会记得自己是由哪个Class 实例所生成,通过Class可以完整地得到一个类中的所有被加载的结构
- Class 类是 Reflection 的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class 对象
Class类的常用方法:
1 | static Class forName(String name); // 返回指定类名name的Class对象 |
获取Class 类的实例(四种方法):
- 若已知具体的类,通过类的
class
属性获取, 该方法最为安全可靠,程序性能最高1
Class clazz = String.class;
- 若已知某个类的实例,调用该实例的
getClass
方法获取 Class 对象1
Class clazz = "test".getClass();
- 已知一个类的全类名,且该类在类路径下,可通过Class 类的静态方法
forName
获取,可能抛出ClassNotFoundException
1
Class clazz = Class.forName(java.lang.String);
- 使用
classLoader
来获取1
2ClassLoader classloader = this.getClass().getClassLoader();
Class clazz = classloader.loadClass("java.lang.String");
类的加载与ClassLoader
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化
- 类的加载(Load)
将类的class 文件读入内存,并为之创建一个java.lang.Class
对象。此过程由类加载器完成 - 类的链接(Link)
将类的二进制数据合并到 JRE 中 - 类的初始化(Initialize)
JVM负责对类进行初始化
类加载器的作用:
- 类加载的作用: 将 class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的
java.lang.Class
对象作为方法区中类数据的访问入口 。 - 类缓存: 标准的 JavaSE 类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间 。 不过 JVM 垃圾回收机制可以回收这些Class对象 。
JVM 规范 定义 了如下类型的类的加载器。
- 引导类加载器(Boostap Classloader)
用 C++编写的,是 JVM 自带的类加载器, 负责 Java 平台核心库,用来装载核心类库。该加载器无法直接获取 - 扩展类加载器(Extension Classloader)
负责jre/lib/ext
目录下的 jar包或-D java.ext.dirs
指定目录下的 jar 包装入工作库 - 系统类加载器(System Classloader)
负责java classpath
或-D java.class.path
所指的目录下的类与 jar 包装入工作,是最常用的加载器
1 | // 获取一个系统类加载器 |
获取运行时类的完整结构
获取类的属性及其结构
1 | Class clazz = String.class; |
获取类的方法及其结构
1 | Class clazz = String.class; |
获取类的构造器结构
1 | Class clazz = String.class; |
创建运行时类的对象
获得了类的Class对象之后,可以使用这个Class对象来创建对象。
使用Class的静态方法newInstance
可以创建一个对象,其内部调用了类的空参构造器。
1 | Class clazz = String.class; |
除此之外可以先获取类的构造器,再使用类的构造器来创建对象
1 | Class clazz = String.class; |
调用运行时类的指定结构
调用指定的属性
1 | Class clazz = User.class; |
调用指定的方法
1 | Class clazz = User.class; |
参考
P神的《Java安全漫谈》——反射篇
https://javasec.org/javase/Reflection/Reflection.html
尚硅谷Java入门视频教程——反射部分