反射
1. 什么是反射?
在运行状态中,都能够查看/调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
主要实现是通过Class类进行动态使用其成员变量、方法和构建对象(java.lang.reflect)
获取Class对象:
- Class.forName(“类的全称”)
- 类名.class / 实例化对象.getClass()
根据class获取Field(成员变量)对象:
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
根据Field 获取设置成员变量值:
- filed.get(“实例化对象”) : 获取"实例化对象"的filed字段值
- filed.setAccessible(true) :设置访问权限,如果filed为private,则调用get或set时会抛异常 IllegalAccessException,若设置为true不管什么访问限制均可访问
- filed.set(“实例化对象”,“值”) : 设置"实例化对象"的filed字段值
根据class获取Method(成员方法)对象:
- Method getMethod(name, Class…):获取某个public的Method(包括父类)
- Method getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类) — 可获取private 方法
- Method[] getMethods():获取所有public的Method(包括父类)
- Method[] getDeclaredMethods():调用非public方法,我们通过Method.setAccessible(true)允许其调用
根据Method实现成员方法的调用实现:
- ‘class类’ invoke(‘实例化对象,静态方法此为null’, Object… ‘方法参数’) 方法调用
- getName():返回方法名称
- getReturnType():返回方法返回值类型,也是一个Class实例
- getParameterTypes():返回方法的参数类型,是一个Class数组
- getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
- setAccessible(true) : 非public 方法需要
通过Class获取Constructor 构造器
- getConstructor(Class…):获取某个public的Constructor;
- getDeclaredConstructor(Class…):获取某个Constructor;
- getConstructors():获取所有public的Constructor;
- getDeclaredConstructors():获取所有Constructor。
通过Constructor 实例对象
- newInstance(Object… parameters) :创建一个实例对象
- setAccessible(true) : 通过设置来访问非public构造方法。
通过 Class 获取继承关系:
- ‘父类Class’ getSuperclass() 获取父类class
2. 什么是 Java 序列化?什么情况下需要序列化?
序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。
注意事项:
某个类可以被序列化,则其子类也可以被序列化
声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
反序列化读取序列化对象的顺序要保持一致
3. 动态代理是什么?有哪些应用?
没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理(JDK动态代理)。
动态代理实际上有很多应用,比如spring aop的实现,rpc框架的实现,一些第三方工具库的内部使用等等。
Spring AOP的动态代理实现
Spring AOP的动态代理实现主要有两种方式,JDK动态代理和CGLIB字节码生成。
默认情况下,如果Spring AOP发现目标对象后实现了相应的interface,则采用JDK动态代理机制为其生成代理对象。如果没有发现接口,则采用CGLIB的方式为目标对象生成动态的代理对象实例
注: CGLIB 是一种基于ASM的字节码生成库,用于生成和转换Java字节码。 依赖包 cglib
4. 怎么实现动态代理?
JDK动态代理就是通过反射机制实现的
一. 定义一个InvocationHandler实例,它负责实现接口的方法调用;
二. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
- 使用的ClassLoader,通常就是接口类的ClassLoader;
- 需要实现的接口数组,至少需要传入一个接口进去;
- 用来处理接口方法调用的InvocationHandler实例。
三. 将返回的Object强制转型为接口。
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}