Java反射复习
Java反射复习
1.动态代理
动态代理是一种程序设计模式,它允许程序在运行时创建代理对象,而无需在编译时将对象接口显示的硬编码到程序中。动态代理可以帮助程序员在不改变现有代码的情况下,在运行时动态地添加功能或修改现有功能。
简单来说,动态代理允许程序创建一个中间人对象,该对象在运行时可以拦截对原始对象的调用,并向其添加额外的行为或修改其行为。动态代理通常用于实现AOP(面向切面编程)和RPC(远程过程调用)等技术,以提高程序的可维护性和灵活性。在Java中,动态代理可以使用Java自带的Proxy类和InvocationHandler接口来实现。
2.创建动态代理
Java中的Proxy类可以用于实现动态代理。动态代理是一种代理设计模式,它允许在运行时创建代理对象并连接到底层方法。在Java中,动态代理是通过实现 InvocationHandler 接口和使用 Proxy 类来实现的。下面是一个实战示例。
假设我们有一个接口 UserService 和一个实现类 UserServiceImpl,我们希望为 UserService 创建一个代理,以便在访问它的方法时记录日志。代码如下:
public interface UserService {void saveUser(String name);
}public class UserServiceImpl implements UserService {public void saveUser(String name) {// 保存用户到数据库System.out.println("User " + name + " saved.");}
}
现在,我们创建一个 InvocationHandler 来实现代理逻辑。这里的逻辑很简单,只是在方法调用前后记录一些日志。代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class LoggingProxy implements InvocationHandler {private Object target;public LoggingProxy(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Entering " + method.getName());Object result = method.invoke(target, args);System.out.println("Exiting " + method.getName());return result;}
}
现在我们可以使用上述类来创建一个代理。代码如下:
import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {UserService userService = new UserServiceImpl();LoggingProxy handler = new LoggingProxy(userService);UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),new Class[] { UserService.class },handler);proxy.saveUser("John");}
}
3.反射
反射允许对成员变量,成员方法和构造方法的信息进行编程访问。
反射能获取到的
Java反射(Reflection)是Java程序中一种高级特性,它允许在程序运行时获取对象的属性和方法,并且可以动态地调用这些属性和方法,而不需要编译时确定这些属性和方法。
Java反射的优势在于它可以在运行时检查,创建和操作对象,这使得Java程序更加灵活和可扩展。在反射中,可以使用 Java.lang.reflect 包中的类,如Class、Method、Constructor、Field 等,来获取类的信息,并且操作对象的属性和方法。
通过Java反射,可以做到以下的事情:
- 获取任何一个类的class对象
- 创建任意类型的对象
- 获取字段的值或者方法的返回值
- 设置字段值或者调用方法
- 构造方法的调用
- 动态代理
总的来说,Java反射是一种非常有用的技术,它可以让程序员在编码的过程中更加灵活,同时也为框架的设计提供了更多的可能性。
4.获取Class对象
三种方式
Class.forName("全类名")
类名.class
对象.getClass();
public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}
- 通过类名.class方式获取
Class<Person> clazz1 = Person.class;Person person1 = clazz1.newInstance();
person1.setName("Tom");
person1.setAge(18);System.out.println(person1.getName() + " " + person1.getAge());
- 通过对象.getClass()方法获取
Person person2 = new Person("Jack", 20);Class<? extends Person> clazz2 = person2.getClass();Person person3 = clazz2.newInstance();
person3.setName("Jerry");
person3.setAge(22);System.out.println(person3.getName() + " " + person3.getAge());
- 通过Class.forName()方法获取
Class clazz3 = Class.forName("com.example.Person");Person person4 = (Person) clazz3.newInstance();
person4.setName("Amy");
person4.setAge(24);System.out.println(person4.getName() + " " + person4.getAge());
以上这三种方式都可以获取到Person类的Class对象,并且可以使用Class对象进行一些操作,例如创建新的实例,设置对象的属性值等。其中,第三种方式需要传入类的全限定名,相比于前两种方式稍显繁琐,但是它可以动态地加载类,更加灵活。
5. 反射获取构造方法
通过反射获取构造方法是Java反射的重要应用之一。Java中,使用Class类的getConstructor()方法和getDeclaredConstructor()方法可以获取类的构造方法,其中getConstructor()方法获取的是public修饰的构造方法,getDeclaredConstructor()方法获取的是全部的构造方法(包含private、protected和默认访问权限)。
下面以一个Student类为例,演示如何通过反射获取构造方法:
public class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}private Student(String name) {this.name = name;}public String getName() {return name;}public int getAge() {return age;}
}
- 获取public修饰的构造方法
Class<Student> clazz = Student.class;Constructor<Student> constructor1 = clazz.getConstructor(String.class, int.class);Student student1 = constructor1.newInstance("Tom", 18);System.out.println(student1.getName() + " " + student1.getAge());
- 获取全部的构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();for (Constructor<?> constructor : constructors) {System.out.println("constructor: " + constructor);
}Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);constructor2.setAccessible(true);Student student2 = (Student) constructor2.newInstance("Jack");System.out.println(student2.getName());
在第二个示例中,先使用Class类的getDeclaredConstructors()方法获取Student类的所有构造方法,然后通过遍历构造方法的方式展示构造方法的信息。接着,使用getDeclaredConstructor()方法获取Student类的private构造方法,由于private构造方法无法直接访问,所以需要使用setAccessible()方法将其设置为可访问,然后通过newInstance()方法创建Student类的实例。
通过反射获取构造方法可以使程序更加灵活,由此,我们可以动态地创建对象,而不需要知道具体的类名或者构造方法的参数列表。
6. 获取成员变量
规则:
get表示获取
Declared表示私有
最后的s表示所有,复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名:
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
代码示例:
public class ReflectDemo4 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {//获取成员变量对象//1.获取class对象Class clazz = Class.forName("com.itheima.reflectdemo.Student");//2.获取成员变量的对象(Field对象)只能获取public修饰的Field[] fields1 = clazz.getFields();for (Field field : fields1) {System.out.println(field);}System.out.println("===============================");//获取成员变量的对象(public + private)Field[] fields2 = clazz.getDeclaredFields();for (Field field : fields2) {System.out.println(field);}System.out.println("===============================");//获得单个成员变量对象//如果获取的属性是不存在的,那么会报异常//Field field3 = clazz.getField("aaa");//System.out.println(field3);//NoSuchFieldExceptionField field4 = clazz.getField("gender");System.out.println(field4);System.out.println("===============================");//获取单个成员变量(私有)Field field5 = clazz.getDeclaredField("name");System.out.println(field5);}
}public class Student {private String name;private int age;public String gender;public String address;public Student() {}public Student(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}public Student(String name, int age, String gender, String address) {this.name = name;this.age = age;this.gender = gender;this.address = address;}/* 获取* @return name*/public String getName() {return name;}/* 设置* @param name*/public void setName(String name) {this.name = name;}/* 获取* @return age*/public int getAge() {return age;}/* 设置* @param age*/public void setAge(int age) {this.age = age;}/* 获取* @return gender*/public String getGender() {return gender;}/* 设置* @param gender*/public void setGender(String gender) {this.gender = gender;}/* 获取* @return address*/public String getAddress() {return address;}/* 设置* @param address*/public void setAddress(String address) {this.address = address;}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";}
}
7. 获取成员变量并获取值和修改值
方法 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
代码示例:
public class ReflectDemo5 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {Student s = new Student("zhangsan",23,"广州");Student ss = new Student("lisi",24,"北京");//需求://利用反射获取成员变量并获取值和修改值//1.获取class对象Class clazz = Class.forName("com.itheima.reflectdemo.Student");//2.获取name成员变量//field就表示name这个属性的对象Field field = clazz.getDeclaredField("name");//临时修饰他的访问权限field.setAccessible(true);//3.设置(修改)name的值//参数一:表示要修改哪个对象的name?//参数二:表示要修改为多少?field.set(s,"wangwu");//3.获取name的值//表示我要获取这个对象的name的值String result = (String)field.get(s);//4.打印结果System.out.println(result);System.out.println(s);System.out.println(ss);}
}public class Student {private String name;private int age;public String gender;public String address;public Student() {}public Student(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}public Student(String name, int age, String gender, String address) {this.name = name;this.age = age;this.gender = gender;this.address = address;}/* 获取* @return name*/public String getName() {return name;}/* 设置* @param name*/public void setName(String name) {this.name = name;}/* 获取* @return age*/public int getAge() {return age;}/* 设置* @param age*/public void setAge(int age) {this.age = age;}/* 获取* @return gender*/public String getGender() {return gender;}/* 设置* @param gender*/public void setGender(String gender) {this.gender = gender;}/* 获取* @return address*/public String getAddress() {return address;}/* 设置* @param address*/public void setAddress(String address) {this.address = address;}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}";}
}
8.获取成员方法
规则:
get表示获取
Declared表示私有
最后的s表示所有,复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
代码示例:
public class ReflectDemo6 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {//1.获取class对象Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");//2.获取方法//getMethods可以获取父类中public修饰的方法Method[] methods1 = clazz.getMethods();for (Method method : methods1) {System.out.println(method);}System.out.println("===========================");//获取所有的方法(包含私有)//但是只能获取自己类中的方法Method[] methods2 = clazz.getDeclaredMethods();for (Method method : methods2) {System.out.println(method);}System.out.println("===========================");//获取指定的方法(空参)Method method3 = clazz.getMethod("sleep");System.out.println(method3);Method method4 = clazz.getMethod("eat",String.class);System.out.println(method4);//获取指定的私有方法Method method5 = clazz.getDeclaredMethod("playGame");System.out.println(method5);}
}
9. 获取成员方法并运行
方法
Object invoke(Object obj, Object… args) :运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
代码示例:
package com.itheima.a02reflectdemo1;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class ReflectDemo6 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {//1.获取字节码文件对象Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");//2.获取一个对象//需要用这个对象去调用方法Student s = new Student();//3.获取一个指定的方法//参数一:方法名//参数二:参数列表,如果没有可以不写Method eatMethod = clazz.getMethod("eat",String.class);//运行//参数一:表示方法的调用对象//参数二:方法在运行时需要的实际参数//注意点:如果方法有返回值,那么需要接收invoke的结果//如果方法没有返回值,则不需要接收String result = (String) eatMethod.invoke(s, "重庆小面");System.out.println(result);}
}public class Student {private String name;private int age;public String gender;public String address;public Student() {}public Student(String name) {this.name = name;}private Student(String name, int age) {this.name = name;this.age = age;}/* 获取* @return name*/public String getName() {return name;}/* 设置* @param name*/public void setName(String name) {this.name = name;}/* 获取* @return age*/public int getAge() {return age;}/* 设置* @param age*/public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}private void study(){System.out.println("学生在学习");}private void sleep(){System.out.println("学生在睡觉");}public String eat(String something){System.out.println("学生在吃" + something);return "学生已经吃完了,非常happy";}
}
10. 面试题:
你觉得反射好不好?好,有两个方向
第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。
第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。
11. 练习泛型擦除
理解:(掌握)
集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。
代码示例:(了解)
package com.itheima.reflectdemo;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;public class ReflectDemo8 {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {//1.创建集合对象ArrayList<Integer> list = new ArrayList<>();list.add(123);
// list.add("aaa");//2.利用反射运行add方法去添加字符串//因为反射使用的是class字节码文件//获取class对象Class clazz = list.getClass();//获取add方法对象Method method = clazz.getMethod("add", Object.class);//运行方法method.invoke(list,"aaa");//打印集合System.out.println(list);}
}
12 练习:修改字符串的内容
在这个练习中,我需要你掌握的是字符串不能修改的真正原因。
字符串,在底层是一个byte类型的字节数组,名字叫做value
private final byte[] value;
真正不能被修改的原因:final和private
final修饰value表示value记录的地址值不能修改。
private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。
如果要强行修改可以用反射:
代码示例:(了解)
String s = "abc";
String ss = "abc";
// private final byte[] value= {97,98,99};
// 没有对外提供getvalue和setvalue的方法,不能修改value记录的地址值
// 如果我们利用反射获取了value的地址值。
// 也是可以修改的,final修饰的value
// 真正不可变的value数组的地址值,里面的内容利用反射还是可以修改的,比较危险//1.获取class对象
Class clazz = s.getClass();//2.获取value成员变量(private)
Field field = clazz.getDeclaredField("value");
//但是这种操作非常危险
//JDK高版本已经屏蔽了这种操作,低版本还是可以的
//临时修改权限
field.setAccessible(true);//3.获取value记录的地址值
byte[] bytes = (byte[]) field.get(s);
bytes[0] = 100;System.out.println(s);//dbc
System.out.println(ss);//dbc
13 练习,反射和配置文件结合动态获取的练习(重点)
需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。
分析:
①通过Properties加载配置文件
②得到类名和方法名
③通过类名反射得到Class对象
④通过Class对象创建一个对象
⑤通过Class对象得到方法
⑥调用方法
代码示例:
public class ReflectDemo9 {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.读取配置文件的信息Properties prop = new Properties();FileInputStream fis = new FileInputStream("day14-code\\\\prop.properties");prop.load(fis);fis.close();System.out.println(prop);String classname = prop.get("classname") + "";String methodname = prop.get("methodname") + "";//2.获取字节码文件对象Class clazz = Class.forName(classname);//3.要先创建这个类的对象Constructor con = clazz.getDeclaredConstructor();con.setAccessible(true);Object o = con.newInstance();System.out.println(o);//4.获取方法的对象Method method = clazz.getDeclaredMethod(methodname);method.setAccessible(true);//5.运行方法method.invoke(o);}
}配置文件中的信息:
classname=com.itheima.a02reflectdemo1.Student
methodname=sleep
14. 利用发射保存对象中的信息(重点)
public class MyReflectDemo {public static void main(String[] args) throws IllegalAccessException, IOException {/*对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去*/Student s = new Student("小A",23,'女',167.5,"睡觉");Teacher t = new Teacher("播妞",10000);saveObject(s);}//把对象里面所有的成员变量名和值保存到本地文件中public static void saveObject(Object obj) throws IllegalAccessException, IOException {//1.获取字节码文件的对象Class clazz = obj.getClass();//2. 创建IO流BufferedWriter bw = new BufferedWriter(new FileWriter("myreflect\\\\a.txt"));//3. 获取所有的成员变量Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);//获取成员变量的名字String name = field.getName();//获取成员变量的值Object value = field.get(obj);//写出数据bw.write(name + "=" + value);bw.newLine();}bw.close();}
}
public class Student {private String name;private int age;private char gender;private double height;private String hobby;public Student() {}public Student(String name, int age, char gender, double height, String hobby) {this.name = name;this.age = age;this.gender = gender;this.height = height;this.hobby = hobby;}/* 获取* @return name*/public String getName() {return name;}/* 设置* @param name*/public void setName(String name) {this.name = name;}/* 获取* @return age*/public int getAge() {return age;}/* 设置* @param age*/public void setAge(int age) {this.age = age;}/* 获取* @return gender*/public char getGender() {return gender;}/* 设置* @param gender*/public void setGender(char gender) {this.gender = gender;}/* 获取* @return height*/public double getHeight() {return height;}/* 设置* @param height*/public void setHeight(double height) {this.height = height;}/* 获取* @return hobby*/public String getHobby() {return hobby;}/* 设置* @param hobby*/public void setHobby(String hobby) {this.hobby = hobby;}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";}
}
public class Teacher {private String name;private double salary;public Teacher() {}public Teacher(String name, double salary) {this.name = name;this.salary = salary;}/* 获取* @return name*/public String getName() {return name;}/* 设置* @param name*/public void setName(String name) {this.name = name;}/* 获取* @return salary*/public double getSalary() {return salary;}/* 设置* @param salary*/public void setSalary(double salary) {this.salary = salary;}public String toString() {return "Teacher{name = " + name + ", salary = " + salary + "}";}
}
15.补充动态代理
15.1 好处:
无侵入式的给方法增强功能
15.2 动态代理三要素:
1,真正干活的对象
2,代理对象
3,利用代理调用方法
切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。
15.3 代码实现:
public class Test {public static void main(String[] args) {/*需求:外面的人想要大明星唱一首歌1. 获取代理的对象代理对象 = ProxyUtil.createProxy(大明星的对象);2. 再调用代理的唱歌方法代理对象.唱歌的方法("只因你太美");*///1. 获取代理的对象BigStar bigStar = new BigStar("鸡哥");Star proxy = ProxyUtil.createProxy(bigStar);//2. 调用唱歌的方法String result = proxy.sing("只因你太美");System.out.println(result);}
}
/*
*
* 类的作用:
* 创建一个代理
*
* */
public class ProxyUtil {/* 方法的作用:* 给一个明星的对象,创建一个代理 形参:* 被代理的明星对象 返回值:* 给明星创建的代理 需求:* 外面的人想要大明星唱一首歌* 1. 获取代理的对象* 代理对象 = ProxyUtil.createProxy(大明星的对象);* 2. 再调用代理的唱歌方法* 代理对象.唱歌的方法("只因你太美");* */public static Star createProxy(BigStar bigStar){/* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)参数一:用于指定用哪个类加载器,去加载生成的代理类参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法参数三:用来指定生成的代理对象要干什么事情*/Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法//参数三:用来指定生成的代理对象要干什么事情new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/ 参数一:代理的对象* 参数二:要运行的方法 sing* 参数三:调用sing方法时,传递的实参* */if("sing".equals(method.getName())){System.out.println("准备话筒,收钱");}else if("dance".equals(method.getName())){System.out.println("准备场地,收钱");}//去找大明星开始唱歌或者跳舞//代码的表现形式:调用大明星里面唱歌或者跳舞的方法return method.invoke(bigStar,args);}});return star;}
}
public interface Star {//我们可以把所有想要被代理的方法定义在接口当中//唱歌public abstract String sing(String name);//跳舞public abstract void dance();
}
public class BigStar implements Star {private String name;public BigStar() {}public BigStar(String name) {this.name = name;}//唱歌@Overridepublic String sing(String name){System.out.println(this.name + "正在唱" + name);return "谢谢";}//跳舞@Overridepublic void dance(){System.out.println(this.name + "正在跳舞");}/* 获取* @return name*/public String getName() {return name;}/* 设置* @param name*/public void setName(String name) {this.name = name;}public String toString() {return "BigStar{name = " + name + "}";}
}
15.4 额外扩展
动态代理,还可以拦截方法
比如:
在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。
但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。
/*
* 类的作用:
* 创建一个代理
* */
public class ProxyUtil {public static Star createProxy(BigStar bigStar){public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)Star star = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),new Class[]{Star.class},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if("cleanWC".equals(method.getName())){System.out.println("拦截,不调用大明星的方法");return null;}//如果是其他方法,正常执行return method.invoke(bigStar,args);}});return star;}
}
15.5 动态代理的练习
对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强
public class MyProxyDemo1 {public static void main(String[] args) {//动态代码可以增强也可以拦截//1.创建真正干活的人ArrayList<String> list = new ArrayList<>();//2.创建代理对象//参数一:类加载器。当前类名.class.getClassLoader()// 找到是谁,把当前的类,加载到内存中了,我再麻烦他帮我干一件事情,把后面的代理类,也加载到内存//参数二:是一个数组,在数组里面写接口的字节码文件对象。// 如果写了List,那么表示代理,可以代理List接口里面所有的方法,对这些方法可以增强或者拦截// 但是,一定要写ArrayList真实实现的接口// 假设在第二个参数中,写了MyInter接口,那么是错误的。// 因为ArrayList并没有实现这个接口,那么就无法对这个接口里面的方法,进行增强或拦截//参数三:用来创建代理对象的匿名内部类List proxyList = (List) Proxy.newProxyInstance(//参数一:类加载器MyProxyDemo1.class.getClassLoader(),//参数二:是一个数组,表示代理对象能代理的方法范围new Class[]{List.class},//参数三:本质就是代理对象new InvocationHandler() {@Override//invoke方法参数的意义//参数一:表示代理对象,一般不用(了解)//参数二:就是方法名,我们可以对方法名进行判断,是增强还是拦截//参数三:就是下面第三步调用方法时,传递的参数。//举例1://list.add("阿玮好帅");//此时参数二就是add这个方法名//此时参数三 args[0] 就是 阿玮好帅//举例2://list.set(1, "aaa");//此时参数二就是set这个方法名//此时参数三 args[0] 就是 1 args[1]"aaa"public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//对add方法做一个增强,统计耗时时间if (method.getName().equals("add")) {long start = System.currentTimeMillis();//调用集合的方法,真正的添加数据method.invoke(list, args);long end = System.currentTimeMillis();System.out.println("耗时时间:" + (end - start));//需要进行返回,返回值要跟真正增强或者拦截的方法保持一致return true;}else if(method.getName().equals("remove") && args[0] instanceof Integer){System.out.println("拦截了按照索引删除的方法");return null;}else if(method.getName().equals("remove")){System.out.println("拦截了按照对象删除的方法");return false;}else{//如果当前调用的是其他方法,我们既不增强,也不拦截method.invoke(list,args);return null;}}});//3.调用方法//如果调用者是list,就好比绕过了第二步的代码,直接添加元素//如果调用者是代理对象,此时代理才能帮我们增强或者拦截//每次调用方法的时候,都不会直接操作集合//而是先调用代理里面的invoke,在invoke方法中进行判断,可以增强或者拦截proxyList.add("aaa");proxyList.add("bbb");proxyList.add("ccc");proxyList.add("ddd");proxyList.remove(0);proxyList.remove("aaa");//打印集合System.out.println(list);}
}