Java 反射机制
-
在程序运行时,获取类的完整结构信息,调用类对象的方法
。 -
正射:
使用某个类,都会知道这个类,以及要用它来做什么,可以直接通过 new 实例化创建对象,然后使用这个对象对类进行操作
。 -
反射:
一开始并不知道要初始化的是什么类,无法使用 new 来实例化创建对象,在运行时才知道要操作的是什么类,然后通过 JDK 提供的反射 API 来初始化对象,同样可以获取到类的完整构造以及调用对应的方法
。
反射的原理
-
Java 反射机制(Java Reflection) 是:
Java 语言中一种动态(运行时)访问、检测、修改它本身的能力
。 -
主要作用是:
动态(运行时)获取类的完整结构信息、调用对象的方法
。 -
另一种说法:
Java 程序在运行时(动态)通过先创建一个类的反射对象,再对类进行相关操作
。 -
获取一个类的反射对象的主要过程:
-
获取类的
Class
实例对象。 -
根据
Class
实例对象获取Constructor
对象。 -
再根据
Constructor
对象的newInstance()
方法获取到类的反射对象
。 -
同样,根据
Class
实例对象可以获取类的Method
对象。 -
再根据
Method
对象的invoke()
方法调用具体的类方法。
-
Java 反射 API
-
Class 类:
每个类都有的唯一对象
、反射的核心类
,可以获取类的属性,方法
等信息。 -
Field 类:表示
类的成员变量
,在 Java.lang.reflec 包中,可以用来获取和设置类之中的属性值
。 -
Method 类:表示
类的方法
,在 Java.lang.reflec 包中,可以用来获取类中的方法信息或者执行方法
。 -
Constructor 类:表示
类的构造方法
,在 Java.lang.reflec 包中。
例子
正射的例子
-
正射调用时,目标类的
静态代码块
、代码块
都调用了。 -
而,构造方法则是调用时再启动。
public class JavaReflectionTest {
public static void mainO1(String[] args) {
// 正射调用
System.out.println("=== 正射调用过程 1 ===");
ReflectionTest reflectionTest = new ReflectionTest();
reflectionTest.setId(1);
System.out.println(reflectionTest.toString());
}
public static void mainO2(String[] args) {
// 正射调用
System.out.println("=== 正射调用过程 2 ===");
ReflectionTest reflectionTest1 = new ReflectionTest("反射类", 2);
System.out.println(reflectionTest1.toString());
}
}
class ReflectionTest {
private static final String NAME = "ReflectionTest";
private static int number = 20221204;
static {
System.out.println("ReflectionTest 静态代码块");
}
private String describe;
private int id;
{
System.out.println("ReflectionTest 代码块");
}
public ReflectionTest() {
System.out.println("ReflectionTest 无参构造方法");
}
public ReflectionTest(String describe, int id) {
System.out.println("ReflectionTest 全参构造方法");
this.describe = describe;
this.id = id;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "{" +
"NAME = '" + NAME + '\'' +
", number = " + number +
", describe = '" + describe + '\'' +
", id = " + id +
'}';
}
}
反射的例子
-
其中,目标类的
静态代码块
、代码块
、无参构造函数
都调用了。 -
只有
全参构造函数
方法按需调用。 -
注意:
-
ReflectionTest.class 方式中:
静态代码块、代码块、无参构造函数 全都在 newInstance() 方法时调用
。 -
Class.forName(“”) 方式中:
静态代码块 在获取 Class 时调用
,代码块、无参构造函数 都在 newInstance() 方法时调用
。 -
new ReflectionTest().getClass() 方式中:
静态代码块、代码块、构造函数 全都在获取 Class 时调用
,而 newInstance() 方法再次调用了代码块、构造函数
。
-
public class JavaReflectionTest {
public static void main1(String[] args) {
// 反射调用 1
System.out.println("=== 反射调用过程 1 ===");
// 获取 Class 对象
Class<ReflectionTest> reflectionTestClass = ReflectionTest.class;
try {
// 获取 Constructor 对象
Constructor<ReflectionTest> classConstructor = reflectionTestClass.getConstructor();
// 获取类对象
ReflectionTest reflectionTest = classConstructor.newInstance();
// 类对象直接调用 方法(没有问题)
System.out.println(reflectionTest.toString());
// 获取 Method 对象(setId() 方法)
Method setId = reflectionTestClass.getMethod("setId", int.class);
// 调用获得的方法(setId() 方法)
setId.invoke(reflectionTest, 3);
// 获取 Field 对象(sex 属性)
Field sex = reflectionTestClass.getDeclaredField("sex");
// 设置 sex 属性的值
// 设置失败,还是用 方法吧
sex.set("男", String.class);
// 获取 Method 对象(toString() 方法)
Method toString = reflectionTestClass.getMethod("toString");
// 调用获得的方法(toString() 方法)
System.out.println(toString.invoke(reflectionTest));
} catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main2(String[] args) {
// 反射调用 2
System.out.println("=== 反射调用过程 2 ===");
try {
// 获取 Class 对象
Class<?> reflectionTestClass2 = Class.forName("com.example.reflex.ReflectionTest");
// 获取 Constructor 对象
Constructor<?> classConstructor = reflectionTestClass2.getConstructor();
// 获取类对象
Object reflectionTest = classConstructor.newInstance();
// 获取 Method 对象(setId() 方法)
Method setId = reflectionTestClass2.getMethod("setId", int.class);
// 调用获得的方法(setId() 方法)
setId.invoke(reflectionTest, 4);
// 获取 Method 对象(toString() 方法)
Method toString = reflectionTestClass2.getMethod("toString");
// 调用获得的方法(toString() 方法)
System.out.println(toString.invoke(reflectionTest));
} catch(ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main3(String[] args) {
// 反射调用 3
System.out.println("=== 反射调用过程 3 ===");
// 获取 Class 对象
Class<? extends ReflectionTest> reflectionTestClass3 = new ReflectionTest().getClass();
try {
// 获取 Constructor 对象
Constructor<? extends ReflectionTest> classConstructor = reflectionTestClass3.getConstructor();
// 获取类对象
ReflectionTest reflectionTest = classConstructor.newInstance();
// 获取 Method 对象(setId() 方法)
Method setId = reflectionTestClass3.getMethod("setId", int.class);
// 调用获得的方法(setId() 方法)
setId.invoke(reflectionTest, 5);
// 获取 Method 对象(toString() 方法)
Method toString = reflectionTestClass3.getMethod("toString");
// 调用获得的方法(toString() 方法)
System.out.println(toString.invoke(reflectionTest));
} catch(NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main4(String[] args) {
// 反射调用 4
System.out.println("=== 反射调用过程 4 ===");
// 获取 Class 对象
Class<? extends ReflectionTest> reflectionTestClass4 = new ReflectionTest("反射类", 6).getClass();
try {
// 获取 Constructor 对象
Constructor<? extends ReflectionTest> classConstructor = reflectionTestClass4.getConstructor();
// 获取类对象
ReflectionTest reflectionTest = classConstructor.newInstance();
// 获取 Method 对象(setId() 方法)
Method setId = reflectionTestClass4.getMethod("setId", int.class);
// 调用获得的方法(setId() 方法)
setId.invoke(reflectionTest, 7);
// 获取 Method 对象(toString() 方法)
Method toString = reflectionTestClass4.getMethod("toString");
// 调用获得的方法(toString() 方法)
System.out.println(toString.invoke(reflectionTest));
} catch(NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
class ReflectionTest {
private static final String NAME = "ReflectionTest";
private static int number = 20221204;
static {
System.out.println("ReflectionTest 静态代码块");
}
private String describe;
public String sex;
private int id;
{
System.out.println("ReflectionTest 代码块");
}
public ReflectionTest() {
System.out.println("ReflectionTest 无参构造方法");
}
public ReflectionTest(String describe, int id) {
System.out.println("ReflectionTest 全参构造方法");
this.describe = describe;
this.id = id;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public static String getNAME() {
return NAME;
}
public static int getNumber() {
return number;
}
public static void setNumber(int number) {
ReflectionTest.number = number;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "{" +
"NAME = '" + NAME + '\'' +
", number = " + number +
", describe = '" + describe + '\'' +
", sex = '" + sex + '\'' +
", id = " + id +
'}';
}
}
反射机制中获取 Class 的三种方式
-
以 MyClass 类为例,现在要获取 MyClass 类的 Class 对象
。 -
注意:
下面三种方式获取到的 Class 对象是同一个,内存地址是相同的
。-
都是 JVM 类加载的
,加载过程中都是使用同一个 ClassLoader 类加载器
来加载的。 -
使用
同一个 ClassLoader 类加载器的 Class 对象是属于同一个类的
。 -
如果
自定义类加载器
,破坏了 JVM 的双亲委派机制
,便可以使得同一个类被不同类加载器加载。获取到不同的 Class 对象
。
-
public class JavaReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("=== ReflectionTest.class 反射调用过程 ===");
// 获取 Class 对象
Class<ReflectionTest> reflectionTestClass = ReflectionTest.class;
System.out.println("=== Class.forName() 反射调用过程 ===");
// 获取 Class 对象
Class<?> reflectionTestClass1 = Class.forName("com.example.reflex.ReflectionTest");
System.out.println("=== new ReflectionTest().getClass() 反射调用过程 ===");
// 获取 Class 对象
Class<? extends ReflectionTest> reflectionTestClass3 = new ReflectionTest().getClass();
}
}
MyClass.class
-
通过 MyClass.class 获取
,JVM 会使用 ClassLoader 类加载器将类加载到内存
中。 -
但并
不会做任何类的初始化工作
。 -
返回
java.lang.Class
对象。
Class.forName(“MyClass”)
-
通过 Class.forName("com.example.reflex.MyClass") 获取
,同样,JVM 会使用 ClassLoader 类加载器将类加载到内存
中。 -
语法:
Class.forName("类的全局限定名称")
。 -
并且
会进行类的静态初始化工作
。 -
返回
java.lang.Class
对象。
new MyClass().getClass()
-
通过 new MyClass().getClass() 获取
,这种方式使用了 new 进行实例化
操作。 -
因为使用了 new 实例化,相应的也可以指定调用不同的构造方法。如:
new MyClass().getClass();
、new MyClass("aaa").getClass();
。 -
推荐将 new MyClass().getClass() 方式转换成 MyClass.class 方式
。 -
因此,
类的静态初始化和非静态初始化工作都会进行
。 -
getClass() 方法属于顶级 Object 类中的方法
,任何子类对象都可以调用,哪个子类调用,就返回那个子类的 java.lang.Class 对象。 -
返回
java.lang.Class
对象。
应用扩展
-
优化
简单工厂模式
。- 程序运行时通过 ClassLoader 类加载器动态获取到配置文件中定义的子类的全局定名。
-
实现
代理模式
中的动态代理
。 -
Java JDBC
数据库操作。- 利用反射加载 JDBC 驱动。
Class.forName("com.mysql.cj.jdbc.Driver"); //加载MySQL驱动
Class.forName("oracle.jdbc.driver.OracleDriver"); //加载Oracle驱动