1,概述

在程序的运行过程中, 通过Class对象得到类中的信息(构造方法, 成员方法, 成员变量), 并操作他们
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。非常规的手段操作对象。

Class类
java中存在一个类叫Class类,Class类创建的对象我们称为Class对象/类对象
Class对象会保存类中的信息(构造方法, 成员方法, 成员变量等)
有了Class对象就能得到类中的所有信息。可以说得到Class对象反射就完成了一半。
反射的应用场景

  • ①IDEA的智能提示
  • ②框架Spring/SpringMVC/Mybatis

利用反射调用它类中的属性和方法时,无视修饰符使用反射创建对象,代码更复杂功能更强大灵活,会破坏封装
三种获取Class对象的方式

  1. 类名.class
  2. 对象.getClass()
  3. Class.forName(“类全名”);

常用方法

方法 说明
String getSimpleName(); 获得类名字符串:类名
String getName(); 获得类全名:包名.类名
T newInstance 通过Class对象创建对象
操作构造器对象
Constructor<?>[] getConstructors() 返回所有构造器对象的数组(只能拿public的)
Constructor getConstructor(Class<?>… parameterTypes) 返回单个构造器对象(只能拿public的)
Constructor<?>[] getDeclaredConstructors() 返回所有构造器对象的数组,存在就能拿到
Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个构造器对象,存在就能拿到
构造器对象方法
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买票

  1. 真实对象: 真正工作的对象12306
  2. 代理对象: 黄牛
  3. 抽象对象: 代理对象和真实对象都要实现的接口 (卖票接口)

什么是动态代理?在程序的运行过程中创建出代理对象
动态代理的好处:可以对功能进行增强拦截

Proxy类


介绍:动态代理类
常用构造

方法 说明
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
创建代理对象
参数:
ClassLoader loader: 类加载器, 固定写法 当前类名.class.getClassLoader()
Class<?>[] interfaces: 多个接口, 代理对象会实现这些接口
InvocationHandler h: 执行处理器接口, 放入匿名内部类

示例

需求:统计list方法的执行时间,拦截remove方法

  1. public class TestCode {
  2. public static void main(String[] args) {
  3. // 创建真实对象
  4. ArrayList<Integer> list = new ArrayList<>();
  5. // 创建动态代理对象
  6. Object proxy = Proxy.newProxyInstance(
  7. // 固定写法
  8. TestCode.class.getClassLoader(),
  9. // 代理对象需要实现的接口
  10. new Class[]{List.class},
  11. // 代理对象调用任何方法都会执行invoke
  12. // invoke相当于重写了接口的所有方法,可以做统一处理
  13. new InvocationHandler() {
  14. @Override //代理对象 调用方法 参数
  15. public Object invoke(Object proxy, Method method, Object[] args)
  16. throws Throwable {
  17. // 对方法进行拦截
  18. if (method.getName().equals("remove")) {
  19. System.out.println("已拦截remove方法");
  20. return null;
  21. }
  22. // 对方法进行增强
  23. long start = System.currentTimeMillis();
  24. // 相当于list.add(1) 方法名.调用(调用者,参数)
  25. Object res = method.invoke(list, args);
  26. long end = System.currentTimeMillis();
  27. System.out.println(
  28. method.getName() + "耗时" + (end - start) + "ms");
  29. return res;
  30. }
  31. });
  32. List plist = (List) proxy;
  33. plist.add(1);
  34. plist.add(2);
  35. System.out.println(plist);
  36. plist.remove(1);
  37. System.out.println(plist);
  38. }
  39. }