七、反射
# 七、反射
# 1、 什么是Java的反射?
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性; 这种动态获取类的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
# 2、 获取 Class 对象的方式?
- 1、知道具体类的情况下可以使用:
类名.class //不会触发类的初始化
- 2、知道类全限定名称的情况下
Class.forName("类的全限定名"); //会触发类的初始化
- 3、知道具体对象的情况下:
Object obj = new Object(); // 会触发类的初始化
obj.getClass();
- 4、通过类加载器获取:可以使用类加载器来获取 Class 对象。这种方式通常用于动态加载类,可以根据类的名称获取 Class 对象。例如:
package com.kinggm.test;
public class TestD {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader classLoader = TestD.class.getClassLoader(); // 这里也可以自定义类加载器
Class<?> clazz = classLoader.loadClass("com.kinggm.test.TestD"); // 不会触发类的初始化
}
}
需要注意到是: 通过类加载器获取 Class 对象不会触发类的初始化过程,这意味着类加载器加载的类不会执行静态代码块或初始化静态对象。 这种延迟初始化的特性可以在某些场景下提供性能优势,因为只有在需要使用类时才会进行初始化操作。
类初始化的条件:
- 创建类的实例
- 访问类或接口的静态字段,最终解析为初始化一个类
- 调用类的静态方法
- 使用java.lang.reflect包的方法对类进行反射调用,这要求类进行初始化
- 初始化一个类的子类,导致其父类被初始化(但接口的初始化不受此规则影响)
- 虚拟机启动时,需要初始化包含main方法的那个类(主类)
# 3、反射机制优缺点
优点:
- 运行期类型的判断,动态加载类,提高代码灵活度。
缺点:
- 1,性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。
- 2,安全问题,让我们可以动态操作改变类的属性同时也增加了类的安全隐患。
# 4、反射的应用场景
- 我们在使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序;
- Spring 框架的 IOC(动态加载管理 Bean)创建对象以及 AOP(动态代理)功能都和反射有联系;
- 动态配置实例的属性;
- JDK提供的动态代理功能;(代理对象通过反射的invoke方法调用被代理对象的原始方法)
- 注解功能的实现 (基于反射去获取注解标记,然后实现对应的功能);
# 5、反射的用法代码示例
需要注意的小知识点: getDeclaredXXX() 和 getXXX() 方法都用于获取类中的字段、方法、构造方法等信息,但它们之间有一些区别, 下面以getDeclaredField 方法和getField 方法举例:
①、访问权限:
getDeclaredField(String name) 方法可以获取类中声明的所有字段,包括公有(public)、私有(private)、受保护(protected)和默认(default)访问权限的字段。 但是,如果字段是私有的,就需要通过 setAccessible(true) 方法设置字段的可访问性才能访问。
getField(String name) 方法只能获取类中的公有字段,无法获取私有、受保护或默认访问权限的字段。②、查找范围:
getDeclaredField(String name) 方法在当前类中查找指定名称的字段。
getField(String name) 方法在当前类及其父类(包括父类的父类等)中查找指定名称的公有字段。③、用途:
getDeclaredField() 通常用于获取类中所有类型的字段,无论是公有还是私有的。
getField() 主要用于获取类的公有字段,适用于对外部公开的字段。
常用的反射API:
- 获取类的对象:
Class<?> aClass = Class.forName("com.kinggm.test.TestReflect");
- 获取类的构造器:
Constructor<?> constructor = aClass.getDeclaredConstructor();
- 获取类的实例:
TestReflect reflect = (TestReflect)constructor.newInstance(); // 或者直接 aClass.newInstance();
- 获取类的某个字段对象,获取字段值:
Field nameField = aClass.getDeclaredField("name");
nameField.setAccessible(true); // 如果修饰符是private 需要 setAccessible(true)
// 根据字段对象 获取字段的值
Object value = nameField.get(reflect);
System.out.println(value);
- 获取类的某个方法对象,执行该方法:
// 获取getName方法对象
Method method = aClass.getDeclaredMethod("getName");
// 执行getName方法
Object invoke = method.invoke(reflect);
System.out.println(invoke);
- 获取类的全部字段:
// 获取全部字段
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("字段名:"+declaredField.getName());
}
- 获取类的全部方法:
// 获取全部方法
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("方法名:"+declaredMethod.getName());
}
下面是完整代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TestD {
public static void main(String[] args) throws Exception{
Class<?> aClass = Class.forName("com.kinggm.test.TestReflect");
Constructor<?> constructor = aClass.getDeclaredConstructor();
// 通过反射 获取类的实例
TestReflect reflect = (TestReflect)constructor.newInstance(); // 或者直接 aClass.newInstance();
// 通过对象调用方法
String name = reflect.getName();
System.out.println(name);
// 获取字段对象
Field nameField = aClass.getDeclaredField("name");
nameField.setAccessible(true);
// 根据字段对象 获取字段的值
Object value = nameField.get(reflect);
System.out.println(value);
// 获取getName方法对象
Method method = aClass.getDeclaredMethod("getName");
// 执行getName方法
Object invoke = method.invoke(reflect);
System.out.println(invoke);
// 获取全部字段
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("字段名:"+declaredField.getName());
}
// 获取全部方法
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("方法名:"+declaredMethod.getName());
}
}
}
class TestReflect{
static {
System.out.println("执行了类初始化");
}
private String name = "秀逗";
public String getName(){
return name;
}
}