跳到主要内容

前言

Java反射机制对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的功能叫做Java反射

Java反射可以用来:

  • 在运行时分析类的能力
  • 在运行时检查对象
  • 实现泛型数组操作代码

反射十分复杂但也十分强大,一般我们写web后端的程序员并不需要考虑反射机制,而在开发工具领域Java反射十分有用

在Java反射中,Class类是必不可少的,而Class类是在JDK包中真实存在的类,我们说Java反射之前,需要先明白Class类和Java中的类加载机制

Class类

我们点到Class的源码可以看到它的构造方法是私有的,说明我们不能手动去创建一个Class类对象

private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}

在Java中,每一个使用class标识的类,在内存中都有一个Class对象用来保存他的信息,而这个Class对象就是我们使用反射机制的基础,我们使用反射其实就是通过操作Class对象来实现反射的

类加载

在启动时,包含main方法的类会优先被加载,它会加载所有需要的类,这些加载的类又要加载他们所需要的类

下图是一个类的生命周期

image.png

加载

加载阶段准确的来说是获取类的二进制字节流的过程,我们可以使用系统提供了ClassLoader,也可以自定义ClassLoader,对于Java中的类加载机制,十分强大,这里我就不再赘述,简单提一嘴(JVM中使用双亲委派机制来进行类加载)

连接

在连接阶段主要分为三个阶段,分别是验证,准备,解析

  • 验证

验证阶段主要是为了验证文件是否正确,也就是.class文件是否符合要求

  • 准备

准备阶段主要是为了将各个类的静态变量分配内存,并初始化为默认值

  • 解析

解析阶段主要是为了将文件中的符号引用转换为直接引用

什么是符号引用呢?

比如我们有一个User类,User类里面有一个方法getName(),我们有一个User类的实例user 然后我们user.getName()这个就叫做符号引用

什么是直接引用?

直接引用就是指向方法区的内存位置

初始化

初始化阶段主要是为了给类的静态变量赋予正确的初始值,我们前面看到连接阶段的准备阶段是给各个类的静态变量分配内存并初始化为默认值,要注意是默认值,而这里赋予正确的值

使用

类访问方法区内的数据结构的接口, 对象是Heap区的数据

卸载

卸载阶段也就是程序结束了,在JVM中有好几种方式会导致程序结束

  • 程序正常结束
  • 调用了System.exit()方法
  • 程序遇到异常或者错误导致结束
  • 操作系统层面导致程序结束

Java反射该怎么使用?

在Java中使用反射,也就是操作Class对象,对于每一个Class类,JVM都会帮我们创建一个Class对象,我们该如何获取这些class呢?

三种方式

  1. 根据类名

我们如果知道类名,比如我们要获取User.class,我们直接用就可以了

  1. 根据对象调用getClass()方法

如果我们有一个User类的实例user,可以通过调用user.getClass(),就可以返回一个User的Class对象

  1. 根据类全路径

使用Class.forName("类全路径"),例如Class.forName("com.yl.User")

我们获取完Class类对象之后就少不了其他操作,那么就可以引出下面这三个类了

  • Constructor(构造)
  • Field(字段)
  • Method(方法)

这三个类是使用Java反射最核心的类,看见名字我们就可以知道分别代表着构造,字段,方法

我们可以使用Constructor类来使用Class的构造,使用Field来使用Class的成员属性,使用Method来使用Class类的方法

Constructor

  • 我们可以使用Class的newInstance()方法来获取一个类的实例,前提是这个类必须要有无参构造方法,调用这个方法其实就是调用类的无参构造,如果无参构造为private那么将报错
  • getConstructor()这个方法就是获取访问权限为public的构造方法,当然我们可以指定一些参数,参数指的是指定类型参数的构造方法
  • getDeclaredConstructor()这个方法获取所有的构造,包括private,而参数和上面那个方法保持一致

Field

我们可以通过这个类来知道某个类的一些字段,这里我就不再一一列举,同样我们可以获得public或者全部的成员属性的字段,以及成员属性的类型

Method

和前面两个类一致,我们可以通过这个类来获得类的方法,同样的我们可以获得public或者全部的方法

总结

对于Java的反射本质上就是通过JVM事先加载好的class类对象,我们可以通过这个类对象拿到这个类的各种信息,例如方法,属性,构造等。

  • 优点

增加代码的灵活性,避免程序写死到代码里

代码简洁,提高代码复用率,外部调用方便

对于任何一个类,我们通过反射能知道他的所有方法,并且调用他任何一个方法

  • 缺点

使用反射会降低程序的性能

使用反射会让代码变得不易读难以维护

使用反射必须运行在一个没有安全限制的环境中