网站游戏制作开发,互动网站建设什么意思,做网站什么程序好,海南省住房和建设厅网站1. 反射
1.1 反射的概述#xff1a;
专业的解释#xff08;了解一下#xff09;#xff1a;
是在运行状态中#xff0c;对于任意一个类#xff0c;都能够知道这个类的所有属性和方法#xff1b;
对于任意一个对象#xff0c;都能够调用它的任意属性和方法#xff…1. 反射
1.1 反射的概述
专业的解释了解一下
是在运行状态中对于任意一个类都能够知道这个类的所有属性和方法
对于任意一个对象都能够调用它的任意属性和方法
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
通俗的理解掌握 利用反射创建的对象可以无视修饰符调用类里面的内容 可以跟配置文件结合起来使用把要创建的对象信息和方法写在配置文件中。 读取到什么类就创建什么类的对象 读取到什么方法就调用什么方法 此时当需求变更的时候不需要修改代码只要修改配置文件即可。
1.2 学习反射到底学什么
反射都是从class字节码文件中获取的内容。 如何获取class字节码文件的对象 利用反射如何获取构造方法创建对象 利用反射如何获取成员变量赋值获取值 利用反射如何获取成员方法运行
1.3 获取字节码文件对象的三种方式 Class这个类里面的静态方法forName“全类名”最常用 通过class属性获取 通过对象获取字节码文件对象
代码示例
//1.Class这个类里面的静态方法forName
//Class.forName(类的全类名) 全类名 包名 类名
Class clazz1 Class.forName(com.itheima.reflectdemo.Student);
//源代码阶段获取 --- 先把Student加载到内存中再获取字节码文件的对象
//clazz 就表示Student这个类的字节码文件对象。
//就是当Student.class这个文件加载到内存之后产生的字节码文件对象
//2.通过class属性获取
//类名.class
Class clazz2 Student.class;
//因为class文件在硬盘中是唯一的所以当这个文件加载到内存之后产生的对象也是唯一的
System.out.println(clazz1 clazz2);//true
//3.通过Student对象获取字节码文件对象
Student s new Student();
Class clazz3 s.getClass();
System.out.println(clazz1 clazz2);//true
System.out.println(clazz2 clazz3);//true
1.4 字节码文件和字节码文件对象
java文件就是我们自己编写的java代码。
字节码文件就是通过java文件编译之后的class文件是在硬盘上真实存在的用眼睛能看到的
字节码文件对象当class文件加载到内存之后虚拟机自动创建出来的对象。
这个对象里面至少包含了构造方法成员变量成员方法。
而我们的反射获取的是什么字节码文件对象这个对象在内存中是唯一的。
1.5 获取构造方法
规则
get表示获取
Declared表示私有
最后的s表示所有复数形式
如果当前获取到的是私有的必须要临时修改访问权限否则无法使用
方法名说明Constructor?[] getConstructors()获得所有的构造只能public修饰Constructor?[] getDeclaredConstructors()获得所有的构造包含private修饰ConstructorT getConstructor(Class?... parameterTypes)获取指定构造只能public修饰ConstructorT getDeclaredConstructor(Class?... parameterTypes)获取指定构造包含private修饰
代码示例
public class ReflectDemo2 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {//1.获得整体class字节码文件对象Class clazz Class.forName(com.itheima.reflectdemo.Student);
//2.获取构造方法对象//获取所有构造方法publicConstructor[] constructors1 clazz.getConstructors();for (Constructor constructor : constructors1) {System.out.println(constructor);}
System.out.println();
//获取所有构造带私有的Constructor[] constructors2 clazz.getDeclaredConstructors();
for (Constructor constructor : constructors2) {System.out.println(constructor);}System.out.println();
//获取指定的空参构造Constructor con1 clazz.getConstructor();System.out.println(con1);
Constructor con2 clazz.getConstructor(String.class,int.class);System.out.println(con2);
System.out.println();//获取指定的构造(所有构造都可以获取到包括public包括private)Constructor con3 clazz.getDeclaredConstructor();System.out.println(con3);//了解 System.out.println(con3 con1);//每一次获取构造方法对象的时候都会新new一个。
Constructor con4 clazz.getDeclaredConstructor(String.class);System.out.println(con4);}
}
1.6 获取构造方法并创建对象
涉及到的方法newInstance
代码示例
//首先要有一个javabean类
public class Student {private String name;
private int age;
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 };}
}
//测试类中的代码
//需求1
//获取空参并创建对象
//1.获取整体的字节码文件对象
Class clazz Class.forName(com.itheima.a02reflectdemo1.Student);
//2.获取空参的构造方法
Constructor con clazz.getConstructor();
//3.利用空参构造方法创建对象
Student stu (Student) con.newInstance();
System.out.println(stu);
System.out.println();
//测试类中的代码
//需求2
//获取带参构造并创建对象
//1.获取整体的字节码文件对象
Class clazz Class.forName(com.itheima.a02reflectdemo1.Student);
//2.获取有参构造方法
Constructor con clazz.getDeclaredConstructor(String.class, int.class);
//3.临时修改构造方法的访问权限暴力反射
con.setAccessible(true);
//4.直接创建对象
Student stu (Student) con.newInstance(zhangsan, 23);
System.out.println(stu);
1.7 获取成员变量
规则
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 privateField[] fields2 clazz.getDeclaredFields();for (Field field : fields2) {System.out.println(field);}
System.out.println();//获得单个成员变量对象//如果获取的属性是不存在的那么会报异常//Field field3 clazz.getField(aaa);//System.out.println(field3);//NoSuchFieldException
Field 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 };}
}
1.8 获取成员变量并获取值和修改值
方法说明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 };}
}
1.9 获取成员方法
规则
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);}
}
1.10 获取成员方法并运行
方法
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;}
}
面试题
你觉得反射好不好好有两个方向
第一个方向无视修饰符访问类中的内容。但是这种操作在开发中一般不用都是框架底层来用的。
第二个方向反射可以跟配置文件结合起来使用动态的创建对象动态的调用方法。
1.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.创建集合对象ArrayListInteger 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);}
}
1.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
1.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);}
}配置文件中的信息
classnamecom.itheima.a02reflectdemo1.Student
methodnamesleep
1.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 };}
}2. 动态代理
2.1 好处
无侵入式的给方法增强功能
2.2 动态代理三要素
1真正干活的对象
2代理对象
3利用代理调用方法
切记一点代理可以增强或者拦截的方法都在接口中接口需要写在newProxyInstance的第二个参数里。
2.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 };}
}2.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;}
}
2.5 动态代理的练习
对add方法进行增强对remove方法进行拦截对其他方法不拦截也不增强
public class MyProxyDemo1 {public static void main(String[] args) {//动态代码可以增强也可以拦截//1.创建真正干活的人ArrayListString 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]aaapublic 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);}
}