Java 反射机制-Reflection
部分内容来自黑马程序员和JavaGuide,可以先了解一下类加载和类加载器的知识。
1 反射机制
1.1 【面试】 说说你对反射的了解?
来自文档的解释:
Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.
The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program.
One tangible use of reflection is in JavaBeans, where software components can be manipulated visually via a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded.
简单来说就是:
- 反射是Java编程语言中的一个特色。
- 它允许一个正在执行的Java程序来 检验 或者 “反省” 自己,并且操作程序的内部属性。
- 好比说,一个Java类可以获取它所有成员的名字并进行展示。
- 这在C、C++、Pascal其他语言里面根本做不到。
- 反射机制的一个实用例子就是JavaBeans,软件的各个组件可以通过 构建工具来可视化地操作。
- 这个工具使用反射来获取Java类的属性————当他们被动态地加载时。
- 这张图就很简单明了:
对于一个未知的对象,我们调用一下反射的API,就可以在运行时修改方法、类、接口的行为。
- 反射的工作机制:
程序运行时,java 系统会一直对所有对象进行所谓的运行时类型识别,这项信息记录了每个对象所属的类。通过专门的类可以访问这些信息。用来保存这些信息的类是Class类,Class类为编写可动态操纵的Java代码程序提供了强大功能.
- Class类:
Class类的实例代表一个运行中Java程序的类或接口。枚举是类的一种,注解是接口的一种,每个数组同样也属于一个 被反射为 Class对象的类。Java原始数据类型和void关键字也被视为Class对象。Class类没有公开构造函数,当类被装载,并且在类加载器中被 定义Class 的方法调用时,Class对象会被被JVM自动构建。
综上,总结一下,如果面试时,问你:简单说一下反射机制/反射是什么,你该怎么回答呢?
反射是Java语言中专有的特性。
在Java程序运行时,获取编译以后的类的字节码文件对象,也就是Class类。 (xxx.class字节码文件在读入内存后,类加载器会生成一个java.lang.Class对象,也就是字节码文件对象)。
对于任意一个Class类,都能直接得到这个类的全部部分,包括它的构造器对象、成员变量对象、成员方法对象。
我们把这种 运行时 获取类信息 以及 动态调用类中成分的 能力 称为 Java的反射机制。
1.2 【面试】反射能干什么?
(1) 获取一个类的Class对象 所有行为的第一步
只有我们获取了Class对象后,才能获取其他信息,因为Class对象就像一个入口。在上面类的加载中也说过。反射的第一步,就是拿到这个类对象。
https://blog.csdn.net/liangwenmail/article/details/120965770
- 通过类名.class获取,这里的class是Java的关键字。不止对引用类型,基本类型也可以使用。https://www.zhihu.com/question/47002542
-
Object类的成员方法,被所有类继承:使用对象的getClass()方法,获取对象的类。即使用对象来获取 该对象对应类的Class对象。
-
Class类的静态方法,传入类的全路径:Class.forName("xxx")
首先,Java文件编译得到字节码文件(.class文件),接着回顾我们上面说到的类加载知识。当一个程序使用某个类时,若该类还未被加载到内存中,则会将其.class文件加载到内存中,生成一个对应的java.lang.Class对象。
(2) 获取一个类的构造器对象 Constructor类
Class类提供了以上几种 获取 一个类的 构造器对象 的API。主要区别就是 拿所有还是拿指定(全部返回数组,指定通过参数类型返回单个);拿公开还是公私都拿。
现在我们获取到 一个类的 构造器对象,那肯定可以通过这个构造器来 new 一个该类的对象。
💠 使用反射来创建一个类的对象 | 暴力反射
我们获取了一个类的构造器对象,也就是Constructor,现在,我们调用这个构造器对象的方法 newInstance() 来创建这个类的实例。注意,在创建私有构造器实例时,会报错,因为没有访问权限。
这里,我们就要使用暴力反射 setAccessible(true) 来打开这个构造器的权限,但这是仅在我们这次测试中打开权限。这样一来,我们就可以使用私有构造器来创建对象。newInstance()的返回值类型是object,我们需要进行一下类型强制转换。
(3) 获取一个类的方法对象 Method类
- 步骤
- API,一模一样 不多说了
现在我们获取了这个Method对象,还是要使用它。
💠 使用反射来调用一个类的方法
很多方法是对象的方法,所以应该先创建对象。在调用方法时,我们要传入调用方法的对象。
(4) 获取一个类的成员变量对象 Field类
- 步骤,照样先是获取到Class对象
- 对应的API,照样是拿全部/指定,拿公开/声明的这种排列组合,不多说了。唯一值得注意的是,前两个获取指定类型,传入的参数是参数的类型,也就是Class,而这里传入的是String,是属性名,因为类的属性类型可能相同,但是属性名是唯一的。
注意,这里返回的是 某个类的 Field类的对象。我们继而可以使用这个Field对象,修改 这个类的对象 的 成员变量,听起来是有点拗口。
💠 使用反射 对类的变量 进行取值/赋值
很多变量是 类的变量,所以我们先创建个对象。在调用取值、赋值时,我们要指明对哪个对象进行操作。
-
API
-
总结
画了一个图,来展示一下反射的几个API,看到这里,大家都知道这些方式是怎么用的了,但是为什么要这么用,为什么这么麻烦呢?
1.3 【面试】你在什么地方接触过反射的使用?
💫 泛型和泛型擦除,给ArrayList里add String数据
泛型和泛型擦除
是面试常问的一个问题,在另外一篇中再说这个问题。我们先简述一下:泛型只是在规范你的书写,Java代码在编译阶段,所有的泛型信息会被擦除,Java的泛型基本上都是在编辑器这个层次上实现的,在生成的字节码文件中是不包含泛型信息的,使用泛型的时候加上的类型,在编译阶段会被擦除掉,这个过程称为泛型擦除。所以我们就可以在运行时,获取这个类的Class,再通过Class获取add Method,再通过method调用,调用指定类的add方法,来加入一个不符合泛型规定的变量。
public static void main(String[] args) throws Exception {
ArrayList<Integer> l1 = new ArrayList<Integer>();
l1.add(2);
// 1. 获取Class
Class c1 = l1.getClass();
// 2. 通过Class 获取 Method
Method m = c1.getMethod("add",Object.class);
// 3. 通过Method调用add方法,加入String类型变量
m.invoke(l1,"String");
System.out.println(l1);
}
💫 平时我们在学习项目、框架时,在哪里学习过反射呢?
-
JDBC驱动注册:JavaWeb,JDBC的Driver注册。最最最开始学习JDBC时,我们使用过Class.forName("com.mysql.jdbc.Driver")来注册驱动。
并且,这条语句没有返回值,我们只是需要这个类加载的动作。我们知道,在类的初始化阶段,类加载会执行静态代码块。在Driver的静态代码块中,通过DriverManager的静态方法RegisterDriver来注册驱动。
-
MyBatisMapper代理:MyBatis中,我们也使用到了反射。在最早的JavaWeb中,我们的MyBatis还是使用xml来开发的。sqlSession.getMapper(UserMapper.class),这里的Mapper代理开发就使用了反射的方法获取类的Class对象。以前使用xml配置编写时,sqlSession使用namespace+id来指定查询语句,这样的编码方式耦合度高。使用mapper代理开发,可以通过接口的方法指定查询语句,减少耦合程度。
-
SSM框架中也有反射的使用。Spring的getBean:我们一开始学习Spring时,都是从IoC和DI这两个概念接触的。至于IoC,就是在配置文件中配置好Bean,然后使用ApplicationContext来getBean,来创建实例。我们给getBean传入id,id对应着配置文件中Bean的class...class也就是类的全限定名的一个字符串。这里使用的也是反射机制。还有Spring中@Import注解,也是传入xxxConfig.class,Spring整合Junit的注解也都是传入.class
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)