反射概述
反射概述及动态代理
Author: Msuenb
Date: 2023-02-21
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含着完整的类的结构信息,可以通过这个对象获取类的结构。
java.lang.Class类
要想获取一个类的结构,需要先获取到该类的Class对象,Class对象是反射的根源。
Java中所有类型都可以获取Class对象:
// 1.基本数据类型和void
Class<Integer> integerClass = int.class;
Class<Void> voidClass = void.class;
// 2.类和接口
Class<String> stringClass = String.class;
Class<Comparable> comparableClass = Comparable.class;
// 3.枚举和注解
Class<ElementType> elementTypeClass = ElementType.class;
Class<Override> overrideClass = Override.class;
// 4.数组
Class<String[]> aClass = String[].class;
获取Class对象的四种方式:
类型名.class
:要求编译期间已知类型对象.getClass()
:获取对象的运行时类型Class.forName(全类名)
:可以获取编译期间未知的类型ClassLoader.getSystemClassLoader().loadClass(全类名)
:可以用系统类加载对象加载指定路径下的类型
package demo;
public class GetClassObjTest {
public static void main(String[] args) throws ClassNotFoundException {
Class<GetClassObjTest> c1 = GetClassObjTest.class;
GetClassObjTest obj = new GetClassObjTest();
Class<? extends GetClassObjTest> c2 = obj.getClass();
Class<?> c3 = Class.forName("demo.GetClassObjTest");
Class<?> c4 = ClassLoader.getSystemClassLoader().loadClass("demo.GetClassObjTest");
}
}
反射的基本应用
Java反射机制提供的功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时获取泛型,注解,修饰符等信息
- 生成动态代理
反射相关的主要类:
- java.lang.Class:代表一个类
- java.lang.reflect.Constructor:代表类的构造器
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Array:Array对象可以代表所有的数组
可以通过这些反射相关类提供的API获取:包、修饰符、类型名、父类、父接口、成员(属性、构造器、方法、内部类),注解等信息
示例代码:获取类的常规信息
package demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class GetClassInfoTest {
public static void main(String[] args) {
// 先得到某个类型的Class对象
Class<String> clazz = String.class;
// 1.获取包对象
Package pkg = clazz.getPackage();
System.out.println("包名:" + pkg);
// 2.获取修饰符
int mod = clazz.getModifiers();
System.out.println("类的修饰符有:" + Modifier.toString(mod));
// 3.获取类型名
String name = clazz.getName();
System.out.println("类名:" + name);
// 4.获取父类对应的Class对象
Class<? super String> superclass = clazz.getSuperclass();
System.out.println("父类:" + superclass);
// 5.获取父接口们
Class<?>[] interfaces = clazz.getInterfaces();
System.out.print("父接口:");
for (Class<?> anInterface : interfaces) {
System.out.print(anInterface + ", ");
}
// 6.获取类的属性
System.out.println("\n----------属性----------");
Field[] fields = clazz.getFields();
for (Field field : fields) {
// 修饰符 数据类型 属性名
int modifiers = field.getModifiers();
System.out.println("属性的修饰符:" + Modifier.toString(modifiers));
String name1 = field.getName();
System.out.println("属性名:" + name1);
Class<?> type = field.getType();
System.out.println("数据类型:" + type);
}
System.out.println("----------构造器----------");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor<?> constructor = constructors[i];
System.out.println("第" + (i+1) + "个构造器:");
// 修饰符 构造器名称 构造器形参列表 抛出异常列表
int modifiers = constructor.getModifiers();
System.out.print("构造器修饰符:" + Modifier.toString(modifiers) + "\n");
String name1 = constructor.getName();
System.out.print("构造器名:" + name1 + "\n");
Class<?>[] parameterTypes = constructor.getParameterTypes();
System.out.print("形参列表:");
for (Class<?> parameterType : parameterTypes) {
System.out.print(parameterType + ", ");
}
System.out.print("\n异常列表:");
Class<?>[] exceptionTypes = constructor.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.print(exceptionType + ", ");
}
System.out.println();
Class<?>[] inners = clazz.getDeclaredClasses();
for (Class<?> inner : inners) {
System.out.println("内部类:" + inner);
}
}
}
}
创建运行时类的对象
-
方式1:直接通过 Class 对象来实例化(被实例化类必须要公共的无参构造)
-
方式2:通过获取构造器对象进行实例化
如果构造器的权限修饰符的范围不可见,可以调用 setAccessible(true)
示例代码:
package demo;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
public class CreateObjTest {
@Test
public void test01() throws Exception {
// 1.获取该类的 Class对象
Class<?> clazz = Class.forName("demo.Person");
// 2.创建对象
Object o = clazz.newInstance();
System.out.println(o);
}
@Test
public void test02() throws Exception{
// 1.获取该类的Class对象
Class<?> clazz = Class.forName("demo.Person");
// 2.获取构造器对象 有参
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
// 3.创建实例对象
Object o = constructor.newInstance("Tom", 19);
System.out.println(o);
}
}
class Person {
private String name;
private int age;
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
还可以通过反射的方式创建任意类型的数组对象:
import java.lang.reflect.Array;
public class ArrayObjTest {
public static void main(String[] args) {
Class<String> clazz = String.class;
Object arr = Array.newInstance(clazz, 5);
System.out.println("数组类型:" + arr.getClass());
Array.set(arr, 2, "tom");
System.out.println(Array.get(arr, 2));
}
}
访问运行时类的属性
-
获取该类型的Class对象:Class clazz = Class.forName("包.类名");
-
获取属性对象:Field field = clazz.getDeclaredField("属性名");
如果属性的权限修饰符不是public,那么需要设置属性可访问field.setAccessible(true);
-
创建实例对象:如果操作的是非静态属性,需要创建实例对象
-
设置属性值:field.set(obj,"属性值"); 如果操作静态变量,那么实例对象可以省略,用null表示
-
获取属性值:Object value = field.get(obj); 如果操作静态变量,那么实例对象可以省略,用null表示
package demo;
import java.lang.reflect.Field;
public class FieldTest {
public static void main(String[] args) throws Exception{
// 1. 获取Student的Class对象
Class<?> clazz = Class.forName("demo.Student");
// 2. 获取属性对象
Field idField = clazz.getDeclaredField("id");
Field jobField = clazz.getDeclaredField("job");
// 3. 设置可访问
idField.setAccessible(true);
// 4. 创建Student实例对象
Object stu = clazz.newInstance();
// 5. 获取属性值
Object id = idField.get(stu);
Object job = jobField.get(null); // 获取静态属性值
System.out.println("id = " + id + ", job = " + job);
// 6. 设置属性值
idField.set(stu, 181040211);
jobField.set(null, "student"); // 设置静态属性值
System.out.println("id = " + idField.get(stu) + ", job = " + jobField.get(null));
}
}
class Student {
private int id;
private String name;
public static String job = "学生";
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static String getJob() {
return job;
}
public static void setJob(String job) {
Student.job = job;
}
}
调用运行时类的方法
-
获取该类型的Class对象:Class clazz = Class.forName("包.类名");
-
获取方法对象:Method method = clazz.getDeclaredMethod("方法名",形参类型列表);
-
创建实例对象:Object obj = clazz.newInstance();
-
调用方法:Object result = method.invoke(obj, 实参列表);
如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
如果方法是静态方法,实例对象也可以省略,用null代替
package demo;
import java.lang.reflect.Method;
public class MethodTest {
public static void main(String[] args) throws Exception{
// 1. 获取Student类的对象
Class<?> clazz = Class.forName("demo.Student");
// 2. 获取方法对象
Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);
// 3. 创建对象实例
Object stu = clazz.newInstance();
// 4. 调用方法
setNameMethod.invoke(stu, "jerry"); // 返回值为null 因为setName没有返回值
Method getNameMethod = clazz.getDeclaredMethod("getName");
Object stuName = getNameMethod.invoke(stu);
System.out.println(stuName);
// 调用静态方法
Method getJob = clazz.getDeclaredMethod("getJob");
Object job = getJob.invoke(null);
System.out.println(job);
}
}
动态代理
代理设计模式的原理: 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
代理模式分静态代理和动态代理。
-
静态代理:特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代 理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
-
动态代理:动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
动态代理使用场合: 1. 调试 2. 远程方法调用
动态代理相比于静态代理的优点: 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样就可以更加灵活和统一的处理众多的方法。
Java动态代理相关API:
Proxy:专门完成代理的操作类,是所有动态代理类的父类。通过此类为一 个或多个接口动态地生成实现类。
提供用于创建动态代理类和动态代理对象的静态方法:
- static Class getProxyClass(ClassLoader loader, Class... interfaces) :创建 一个动态代理类所对应的Class对象
- static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) :创建一个动态代理对象
动态代理步骤:
-
创建一个实现 InvocationHandler 接口的类,重写 invoke 方法,以完成代理的具体操作
package demo.proxy_; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private Object target = null; // 目标对象是活动的 不是固定的 public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before..."); Object retVal = method.invoke(target, args); System.out.println("after..."); return retVal; } }
-
创建被代理的接及该接口实现类
package demo.proxy_; public interface Calculate { int add(int x, int y); }
package demo.proxy_; public class Calculator implements Calculate{ @Override public int add(int x, int y) { System.out.println(x + " + " + y + " = " + (x + y)); return x + y; } }
-
通过 Proxy 的 newProxyInstance 静态方法,创建一个接口代理
-
通过创建的接口代理调用接口实现类的方法
package demo.proxy_; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class ProxyTest { public static void main(String[] args) { // 1. 创建目标对象 Calculator calculator = new Calculator(); // 2. 创建InvocationHandler对象 InvocationHandler invocationHandler = new MyInvocationHandler(calculator); // 3. 创建代理对象 Calculate proxy = (Calculate) Proxy.newProxyInstance(calculator.getClass().getClassLoader(), calculator.getClass().getInterfaces(), invocationHandler); // 4. 通过代理对象执行Calculate的方法 proxy.add(2, 7); } }