1,概述
在程序的运行过程中, 通过Class对象得到类中的信息(构造方法, 成员方法, 成员变量), 并操作他们
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。非常规的手段操作对象。
Class类
java中存在一个类叫Class类,Class类创建的对象我们称为Class对象/类对象
Class对象会保存类中的信息(构造方法, 成员方法, 成员变量等)
有了Class对象就能得到类中的所有信息。可以说得到Class对象反射就完成了一半。
反射的应用场景
- ①IDEA的智能提示
- ②框架Spring/SpringMVC/Mybatis
利用反射调用它类中的属性和方法时,无视修饰符使用反射创建对象,代码更复杂功能更强大灵活,会破坏封装
三种获取Class对象的方式
- 类名.class
- 对象.getClass()
- Class.forName(“类全名”);
常用方法
方法 | 说明 |
---|---|
String getSimpleName(); | 获得类名字符串:类名 |
String getName(); | 获得类全名:包名.类名 |
T newInstance | 通过Class对象创建对象 |
操作构造器对象 | |
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor |
返回单个构造器对象(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor |
返回单个构造器对象,存在就能拿到 |
构造器对象方法 | |
T newInstance(Object… initargs) | 根据指定的构造器创建对象 |
public voidsetAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
操作成员方法对象 | |
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
成员方法对象的方法 | |
Object invoke(Object obj, Object… args) | 运行方法 参数一:方法的调用者 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
操作成员变量 | |
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
成员变量方法 | |
void set(Object obj, Object value): | 赋值 |
Object get(Object obj) | 获取值 |
注意:反射中方法参数类型若是泛型,则需要传入Object.class
2,动态代理
代理模式三要素
你 -> 黄牛-> 12306买票
- 真实对象: 真正工作的对象12306
- 代理对象: 黄牛
- 抽象对象: 代理对象和真实对象都要实现的接口 (卖票接口)
什么是动态代理?在程序的运行过程中创建出代理对象
动态代理的好处:可以对功能进行增强或拦截
Proxy类
介绍:动态代理类
常用构造
方法 | 说明 |
---|---|
public static Object newProxyInstance( ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) |
创建代理对象 参数: ClassLoader loader: 类加载器, 固定写法 当前类名.class.getClassLoader() Class<?>[] interfaces: 多个接口, 代理对象会实现这些接口 InvocationHandler h: 执行处理器接口, 放入匿名内部类 |
示例
需求:统计list方法的执行时间,拦截remove方法
public class TestCode {
public static void main(String[] args) {
// 创建真实对象
ArrayList<Integer> list = new ArrayList<>();
// 创建动态代理对象
Object proxy = Proxy.newProxyInstance(
// 固定写法
TestCode.class.getClassLoader(),
// 代理对象需要实现的接口
new Class[]{List.class},
// 代理对象调用任何方法都会执行invoke
// invoke相当于重写了接口的所有方法,可以做统一处理
new InvocationHandler() {
@Override //代理对象 调用方法 参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 对方法进行拦截
if (method.getName().equals("remove")) {
System.out.println("已拦截remove方法");
return null;
}
// 对方法进行增强
long start = System.currentTimeMillis();
// 相当于list.add(1) 方法名.调用(调用者,参数)
Object res = method.invoke(list, args);
long end = System.currentTimeMillis();
System.out.println(
method.getName() + "耗时" + (end - start) + "ms");
return res;
}
});
List plist = (List) proxy;
plist.add(1);
plist.add(2);
System.out.println(plist);
plist.remove(1);
System.out.println(plist);
}
}