Java反射和动态代理

文章目录

    • 1、反射
      • 1.1 反射的概述
      • 1.2 反射作用
      • 1.3 获取字节码文件对象的方式
      • 1.4 字节码文件和字节码文件对象
      • 1.5 获取构造方法
      • 1.6 获取构造方法并创建对象
      • 1.7 获取成员变量并获取值和修改值
      • 1.8 获取成员方法
      • 1.9 获取成员方法并运行
      • 1.10 反射练习
        • 1.10.1 泛型擦除
        • 1.10.2 修改字符串的内容
        • 1.10.3 利用反射保存对象中的信息
        • 1.10.4反射结合配置文件
    • 2. 动态代理
      • 2.1 动态代理好处
      • 2.2 动态代理三要素
      • 2.3 动态代理简单实现
      • 2.4 动态代理扩展
      • 2.5 动态代理练习

1、反射

1.1 反射的概述

反射允许对成员变量(字段),成员方法和构造方法的信息进行编程访问。

专业的解释

  • 是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
  • 对于任意一个对象,都能够调用它的任意属性和方法;
  • 这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

通俗的理解

  • 利用反射创建的对象可以无视修饰符调用类里面的内容

  • 可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。

  • 读取到什么类,就创建什么类的对象

  • 读取到什么方法,就调用什么方法

  • 此时当需求变更的时候不需要修改代码,只要修改配置文件即可。

1.2 反射作用

反射都是从class字节码文件中获取的内容。

  • 获取class字节码文件的对象
  • 利用反射如何获取构造方法(创建对象)
  • 利用反射如何获取成员变量(赋值,获取值)
  • 利用反射如何获取成员方法(运行)

反射的好处:

  • 无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的;
  • 反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。

1.3 获取字节码文件对象的方式

  • Class这个类里面的静态方法forName(“全类名”)(最常用)
  • 通过class属性获取,一般更多的是当做参数进行传递
  • 通过对象获取字节码文件对象,当我们已经有了这个类的对象时,才可以使用。

代码示例:

public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException {//1. 第一种方式//全类名 : 包名 + 类名Class clazz1 = Class.forName("com.ya.reflect.demo01.Student");//2. 第二种方式Class clazz2 = Student.class;//3.第三种方式Student s = new Student();Class clazz3 = s.getClass();System.out.println(clazz1 == clazz2);System.out.println(clazz2 == clazz3);}
}

image-20231227144539976

1.4 字节码文件和字节码文件对象

java文件(源代码阶段):就是编写的java代码。

字节码文件(加载阶段):就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)

字节码文件对象(运行阶段):当class文件加载到内存之后,虚拟机自动创建出来的对象。这个对象里面至少包含了:构造方法,成员变量,成员方法。

反射获取的是字节码文件对象,这个对象在内存中是唯一的。

1.5 获取构造方法

规则:

  • get表示获取
  • Declared表示私有
  • 最后的s表示所有,复数形式
  • 如果当前获取到的是私有的,必须要临时修改访问权限setAccessible(true),否则无法使用
方法名说明
Constructor<?>[] getConstructors()获得所有的构造(只能public修饰)
Constructor<?>[] getDeclaredConstructors()获得所有的构造(包含private修饰)
Constructor getConstructor(Class<?>… parameterTypes)获取指定构造(只能public修饰)
Constructor getDeclaredConstructor(Class<?>… parameterTypes)获取指定构造(包含private修饰)
Constructor类中用于创建对象的方法:
- T newInstance(Object... initargs) :根据指定的构造方法创建对象
- setAccessible(boolean flag) :设置为true,表示取消访问检查

代码示例:

public class Student {private String name;private int age;public Student() {}public Student(String name) {this.name = name;}protected Student(int age) {this.age = age;}private Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.获取class字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo02.Student");//2.获取构造方法// 2.1 返回所有公共构造方法对象的数组System.out.println("-----返回所有公共构造方法对象的数组----");Constructor[] con1 = clazz.getConstructors();for (Constructor con : con1) {System.out.println(con);}// 2.2 返回所有构造方法对象的数组System.out.println("-------返回所有构造方法对象的数组------");Constructor[] con2 = clazz.getDeclaredConstructors();for (Constructor con : con2) {System.out.println(con);}// 2.3 返回所有构造方法对象的数组System.out.println("-------返回单个公共构造方法对象------");Constructor con3 = clazz.getConstructor(String.class);System.out.println(con3);//2.4 获取指定的空参构造System.out.println("-------获取指定的空参构造------");Constructor con4 = clazz.getConstructor();System.out.println(con4);// 2.5 返回所有构造方法对象的数组System.out.println("-------返回单个构造方法对象------");Constructor con5 = clazz.getDeclaredConstructor(String.class,int.class);System.out.println(con5);System.out.println("-----获取构造方法的权限修饰符(返回整数)--------");// 返回的是整数,public 1,private 2,protected 4,abstract 1024int modifiers = con5.getModifiers();System.out.println(modifiers);System.out.println("-----获取构造方法的参数--------");Parameter[] parameters = con5.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter);}System.out.println("--------创建对象-----------");//暴力反射:表示临时取消权限校验con5.setAccessible(true);   // 如果这个权限修饰符是私有的,就要取消权限校验Student stu = (Student) con5.newInstance("张三", 23);System.out.println(stu);}
}

image-20231227152918797

1.6 获取构造方法并创建对象

涉及到的方法:newInstance

代码示例:

public class ReflectDemo02 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//需求1:获取空参,并创建对象(所创建的对象的属性是默认值)//1.获取整体的字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo02.Student");//2.获取空参的构造方法Constructor con = clazz.getConstructor();//3.利用空参构造方法创建对象Student stu = (Student) con.newInstance();System.out.println(stu);System.out.println("----------------------------------");//需求2:获取带参构造,并创建对象//1.获取整体的字节码文件对象Class clazz2 = Class.forName("com.ya.reflect.demo02.Student");//2.获取有参构造方法Constructor con2 = clazz2.getDeclaredConstructor(String.class, int.class);//3.临时修改构造方法的访问权限(暴力反射)con2.setAccessible(true);//4.直接创建对象Student stu2 = (Student) con2.newInstance("zhangsan", 23);System.out.println(stu2);}
}

image-20231227153804258

1.7 获取成员变量并获取值和修改值

获取成员变量规则:

  • get表示获取
  • Declared表示私有
  • 最后的s表示所有,复数形式
  • 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名说明
Field[] getFields()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

获取值和修改值:

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值

代码示例:

public class Student {private String name;private int age;public String gender;public Student() {}public Student(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {//1.获取class字节码文件的对象Class clazz = Class.forName("com.ya.reflect.demo03.Student");//2.获取所有的成员变量System.out.println("-----获取所有的成员变量----");Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {System.out.println(field);}//3.获取单个的成员变量System.out.println("-----获取单个的成员变量----");Field fieldName = clazz.getDeclaredField("name");System.out.println(fieldName);System.out.println("-----获取成员变量的数据类型----");Class<?> type = fieldName.getType();System.out.println(type);System.out.println("-----获取成员变量记录的值----");Student student = new Student("zhangsan", 23, "男");fieldName.setAccessible(true);String value = (String) fieldName.get(student);System.out.println(value);System.out.println("-----修改对象里面记录的值---");fieldName.set(student,"lisi");System.out.println(student);}
}

image-20231227160023230

1.8 获取成员方法

规则:

  • get表示获取
  • Declared表示私有
  • 最后的s表示所有,复数形式
  • 如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public的,包含父类中所有的公共方法)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到,不能获取父类的
Method getMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到
获取方法的修饰符
获取方法的名字
获取方法的形参
获取方法的返回值
获取方法的抛出的异常

代码示例:

public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public void sleep(){System.out.println("睡觉");}private String eat(String something) throws IOException,NullPointerException,ClassCastException {System.out.println("在吃" + something);return "奥利给";}private void eat(String something,int a) {System.out.println("在吃" + something);}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {//1. 获取class字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo04.Student");//2. 获取里面所有的方法对象(包含父类中所有的公共方法)System.out.println("-------获取里面所有的方法对象(包含父类中所有的公共方法)-----");Method[] methods = clazz.getMethods();for (Method method : methods) {System.out.println(method);}// 3. 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)System.out.println("--获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)--");Method[] declaredMethods = clazz.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod);}// 4.获取指定的单一方法System.out.println("-------获取指定的单一方法-----");Method eatMethod = clazz.getDeclaredMethod("eat", String.class);System.out.println(eatMethod);// 5.获取方法的修饰符(返回整数)System.out.println("-------获取方法的修饰符(返回整数)-----");int modifiers = eatMethod.getModifiers();System.out.println(modifiers);// 6.获取方法的名字System.out.println("-------获取方法的名字-----");String eatMethodName = eatMethod.getName();System.out.println(eatMethodName);// 7.获取方法的形参System.out.println("-------获取方法的形参-----");Parameter[] parameters = eatMethod.getParameters();for (Parameter parameter : parameters) {System.out.println(parameter);}//8.获取方法的抛出的异常System.out.println("-------获取方法的抛出的异常-----");Class[] exceptionTypes = eatMethod.getExceptionTypes();for (Class exceptionType : exceptionTypes) {System.out.println(exceptionType);}}
}

image-20231227163212848

image-20231227163234833

1.9 获取成员方法并运行

Object invoke(Object obj, Object… args) :运行方法

  • 参数一:用obj对象调用该方法
  • 参数二:调用方法的传递的参数(如果没有就不写)
  • 返回值:方法的返回值(如果没有就不写)

代码示例:

public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {//1. 获取class字节码文件对象Class clazz = Class.forName("com.ya.reflect.demo04.Student");//2. 获取指定的单一方法Method eatMethod = clazz.getDeclaredMethod("eat", String.class);// 3. 方法运行System.out.println("-------方法运行-----");Student student = new Student();eatMethod.setAccessible(true);String result = (String) eatMethod.invoke(student, "汉堡包");System.out.println(result);}
}

image-20231227163822950

1.10 反射练习

1.10.1 泛型擦除

集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。

代码示例:

public class Test1 {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {//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);}
}

image-20231227165333141

image-20231227165351201

1.10.2 修改字符串的内容

在这个练习中,需要掌握的是字符串不能修改的真正原因。

字符串,在底层是一个byte类型的字节数组,名字叫做value

private final byte[] value;

真正不能被修改的原因:final和private

final修饰value表示value记录的地址值不能修改。

private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。

如果要强行修改可以用反射:

代码示例:

public class Test2 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {String s = "abc";String ss = "abc";// private final byte[] value= {97,98,99};/*** 没有对外提供getvalue和setvalue的方法,不能修改value记录的地址值,* 如果我们利用反射获取了value的地址值,也是可以修改的,* final修饰的value,真正不可变的value数组的地址值,里面的内容利用反射还是可以修改的,* 但是这种操作非常危险,JDK高版本已经屏蔽了这种操作,低版本还是可以的*///1.获取class对象Class clazz = s.getClass();//2.获取value成员变量(private)Field field = clazz.getDeclaredField("value");//临时修改权限field.setAccessible(true);//3.获取value记录的地址值byte[] bytes = (byte[]) field.get(s);bytes[0] = 100; // 对应的字符编码为 'd'System.out.println(s);  //输出dbcSystem.out.println(ss); //输出dbc}
}

image-20231227170402073

image-20231227170444908

1.10.3 利用反射保存对象中的信息

利用反射保存对象中的信息:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。

需求:创建对象,并将对象信息保存到txt文件中。

Student

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;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}public String getHobby() {return hobby;}public void setHobby(String hobby) {this.hobby = hobby;}public String toString() {return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";}
}

Teacher

public class Teacher {private String name;private double salary;public Teacher() {}public Teacher(String name, double salary) {this.name = name;this.salary = salary;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public String toString() {return "Teacher{name = " + name + ", salary = " + salary + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws IOException, IllegalAccessException {Student s = new Student("张三",23,'女',167.5,"睡觉");Teacher t = new Teacher("李四",10000);//saveObject(s);saveObject(t);}//把对象里面所有的成员变量名和值保存到本地文件中public static void saveObject(Object obj) throws IllegalAccessException, IOException {//1.获取字节码文件的对象Class clazz = obj.getClass();//2. 创建IO流BufferedWriter bw = new BufferedWriter(new FileWriter("my-reflect-dynamicproxy/src/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();}
}

image-20231227172337515

image-20231227172433181

1.10.4反射结合配置文件

需求:利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。

分析:

  • 通过Properties加载配置文件
  • 得到类名和方法名
  • 通过类名反射得到Class对象
  • 通过Class对象创建一个对象
  • 通过Class对象得到方法
  • 调用方法

代码示例:

prop.properties

classname=com.ya.reflect.demo06.Teacher
method=teach#classname=com.ya.reflect.demo06.Student
#method=study
public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public void study(){System.out.println("学生在学习!");}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String toString() {return "Student{name = " + name + ", age = " + age + "}";}
}
public class Teacher {private String name;private double salary;public Teacher() {}public Teacher(String name, double salary) {this.name = name;this.salary = salary;}public void teach(){System.out.println("老师在教书!");}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public String toString() {return "Teacher{name = " + name + ", salary = " + salary + "}";}
}
public class ReflectDemo {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.读取配置文件中的信息Properties prop = new Properties();FileInputStream fis = new FileInputStream("my-reflect-dynamicproxy/src/prop.properties");prop.load(fis);fis.close();System.out.println(prop);//2.获取全类名和方法名String className = (String) prop.get("classname");String methodName = (String) prop.get("method");System.out.println(className);System.out.println(methodName);//3.利用反射创建对象并运行方法Class clazz = Class.forName(className);//获取构造方法Constructor con = clazz.getDeclaredConstructor();Object o = con.newInstance();System.out.println(o);//获取成员方法并运行Method method = clazz.getDeclaredMethod(methodName);method.setAccessible(true);method.invoke(o);}
}

image-20231227171506023

image-20231227171526327

2. 动态代理

2.1 动态代理好处

无侵入式的给方法增强功能。调用者(BitStar)–>代理(ProxyUtil)–>对象(Star)

2.2 动态代理三要素

  • 接口:代理对象,被代理类和代理类都需要实现这个接口(本例是Star)
  • 代理类:通过Proxy类动态生成的代理类(本例是ProxyUtil)
  • 被代理类:代理类实例,它会代替被代理对象处理方法调用(本例是BigStar)

注:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

2.3 动态代理简单实现

需求:外面的人想要大明星唱一首歌

  • 获取代理的对象:代理对象 = ProxyUtil.createProxy(大明星的对象);
  • 再调用代理的唱歌方法:代理对象.唱歌的方法(“只因你太美”);

(1)接口:代理对象,定义被代理的方法

public interface Star {//可以把所有想要被代理的方法定义在接口当中//唱歌public abstract String sing(String name);//跳舞public abstract void dance();
}

(2)被代理类:需要使用代理的实体类,要实现代理对象接口

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 + "}";}
}

(3)代理类:创建代理

java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法

// 创建代理
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理有哪些方法
参数三:用来指定生成的代理对象要干什么事情
public class ProxyUtil {/***  形参:被代理的明星对象*  返回值:给明星创建的代理*/public static Star createProxy(BigStar bigStar){// 创建代理// 这个代码没有显示实现Star接口,但通过JDK动态代理机制隐式地为生成的代理类添加了Star接口的实现,从而可以看作实现了Star接口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 {/*** 参数一:代理的对象,即star变量引用的对象* 参数二:要运行的方法,sing,dance* 参数三:调用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;}
}

(4)代理测试

public class Test {public static void main(String[] args) {//1. 获取代理的对象BigStar bigStar = new BigStar("鸡哥");Star proxy = ProxyUtil.createProxy(bigStar);//2. 调用唱歌的方法String result = proxy.sing("只因你太美");System.out.println(result);}
}

image-20231227220017611

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 DynamicProxyTest {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(//参数一:类加载器DynamicProxyTest.class.getClassLoader(),//参数二:是一个数组,表示代理对象能代理的方法范围new Class[]{List.class},//参数三:本质就是代理对象new InvocationHandler() {/*** invoke方法参数的意义* 参数一:表示代理对象,一般不用(了解)* 参数二:就是方法名,我们可以对方法名进行判断,是增强还是拦截* 参数三:就是下面第三步调用方法时,传递的参数。** 举例1:* list.add("阿玮好帅");* 此时参数二就是add这个方法名* 此时参数三 args[0] 就是 阿玮好帅** 举例2:* list.set(1, "aaa");* 此时参数二就是set这个方法名* 此时参数三  args[0] 就是 1  args[1]"aaa"*/@Overridepublic 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);}
}

image-20231227221146481

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/602669.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 创建组件组件

组件&#xff1a;用于将某个功能的 HTML、CSS、JS 封装到一个文件中&#xff0c;提高代码的复用性和可维护性。 创建组件 一、在根目录中创建 components 文件夹&#xff0c;右键点击新建组件。 二、输入组件名称、选择默认模板、点击创建组件。 三、在组件中正常编写内容即可…

动态编译 - Dynamically Compile and Load External Java Classes

文章目录 概述Code 概述 动态编译和加载外部Java类的核心流程可以概括为以下几个步骤&#xff1a; 读取源代码: 首先&#xff0c;需要获取到外部的Java源代码。这通常是通过读取文件、网络资源或者数据库中的源代码字符串来实现的。编译源代码: 接下来&#xff0c;需要使用Ja…

计算机网络(8):因特网上的音频/视频服务

概述 计算机网络最初是为传送数据设计的。因特网 IP 层提供的 “尽最大努力交付” 服务以及每一个分组独立交付的策略&#xff0c;对传送数据信息十分合适。因特网使用的 TCP 协议可以很好地解决P层不能提供可靠交付这一问题。 音频/视频常称为多媒体信息 多媒体信息&#xff…

【BIAI】lecture 3 - GD BP CNN Hands-on

GD & BP & CNN & Hands-on 专业术语 gradient descent (GD) 梯度下降 back propagation (BP) 向传播 Convolutional Neural Network (CNN) 卷积神经网络 forward propagation 前向传播 biologically symmetry 生物对称性 synaptic 突触 axon 轴突 课程大纲 The go…

BUUCTF crypto做题记录(6)新手向

一、Unencode 得到的密文&#xff1a;89FQA9WMD<V1A<V1S83DY.#<W3$Q,2TM] 看题目名字&#xff0c;我们可以知道这是一个解码的题目&#xff0c;但是unencode 好像是不可解码的意思&#xff0c;我们可以对照着解码方式看一下哪个更适合&#xff0c;可以进行尝试一下C…

RocketMQ 投递消息方式以及消息体结构分析:Message、MessageQueueSelector

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…

腾讯云取消免费10G CDN流量包:免费CDN时代结束

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 免费送了7-8年的腾讯云10G免费流量包&#xff0c;从2024年开始&#xff0c;停止赠送了!自此&#xff0c;国内绝大多数互联网大厂的CDN都开收费了! 大概从2016年开始&#xff0c;腾讯云为了抢夺CDN客户&#xff0…

基于Spring-boot-websocket的聊天应用开发总结

目录 1.概述 1.1 Websocket 1.2 STOMP 1.3 源码 2.Springboot集成WS 2.1 添加依赖 2.2 ws配置 2.2.1 WebSocketMessageBrokerConfigurer 2.2.2 ChatController 2.2.3 ChatInRoomController 2.2.4 ChatToUserController 2.3 前端聊天配置 2.3.1 index.html和main.j…

mnn-llm: 大语言模型端侧CPU推理优化

在大语言模型(LLM)端侧部署上&#xff0c;基于 MNN 实现的 mnn-llm 项目已经展现出业界领先的性能&#xff0c;特别是在 ARM 架构的 CPU 上。目前利用 mnn-llm 的推理能力&#xff0c;qwen-1.8b在mnn-llm的驱动下能够在移动端达到端侧实时会话的能力&#xff0c;能够在较低内存…

MySQL之视图外连接、内连接和子查询的使用

一、视图 1.1 含义 虚拟表&#xff0c;和普通表一样使用 1.2 操作 创建视图 create view 视图名 as 修改视图 方式一&#xff1a; create or replace view 视图名 as 【查看视图相关字段】 方式二&#xff1a; alter view 视图名 as 【查看的SQL语句】 查看视图 方式一&…

Nginx快速入门:Nginx实现高可用|结合keepalived实现主备节点(九)

0. 引言 在生产中我们要尽可能避免单点故障&#xff0c;nginx也不例外&#xff0c;因此搭建主备节点必不可少&#xff0c;今天我们来学习下如何利用keepalived实现主备 1. keepalived简介 keepalived 是一个LINUX系统中开源的负载均衡和故障转移软件&#xff0c;它主要用于高…

2024年阿里云、腾讯云、华为云、LightNode、硅云服务器如何选?怎么买最划算?[最新价格表]

很多小伙伴都有一颗上云的心&#xff0c;包括我自己 有事没事的折腾一下自己的小破站&#xff0c;也挺有意思的&#xff01; 那么&#xff0c;云服务器哪家好&#xff1f;优惠力度哪家大&#xff1f;活动入口哪里进&#xff1f;云服务器如何配置&#xff1f;如何选型&#xf…

K8S集群部署解决工作节点couldn‘t get current server API group list问题

最近在自己电脑上装了VMWare Player&#xff0c;在上面装了两个Ubuntu虚拟机&#xff0c;为了方便学习云原生技术&#xff0c;决定在上面装一个2个节点&#xff08;一个控制面&#xff0c;一个工作节点&#xff09;的K8S集群。 参考这篇文章&#xff1a; Ubuntu 22.04 搭建K8…

kubectl的插件安装工具krew

最近得知一个kubectl插件安装工具&#xff0c;叫做krew。 官网地址是&#xff1a;Krew – kubectl plugin manager 安装krew 按照官网的做法&#xff0c;一直安装失败&#xff0c;于是拆解步骤&#xff0c;一步一步下载离线安装。 1、下载krew.yaml 地址&#xff1a;https:…

Proteus 各版本安装指南

Proteus下载链接 https://pan.baidu.com/s/1vHgg8jK9KSHdxSU9SDy4vQ?pwd0531 1.鼠标右击【Proteus8.15(64bit&#xff09;】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到Proteus8.15(64bit&#xff09; 】。 2.打开解压后的文件夹&#…

适用于生物行业的生信云平台

随着基因检测技术的不断发展&#xff0c;生物信息云平台在基因检测行业的应用越来越广泛。生物信息云平台是一种基于云计算的技术&#xff0c;可以将基因检测数据存储在云端&#xff0c;并通过数据分析、挖掘等技术手段&#xff0c;对基因数据进行处理、分析和解读。 这种技术的…

Linux安装nginx(带http ssl)

nginx安装 nginx文件 以及gcc pcre zlib openssl 网盘下载 1.安装gcc yum -y install gcc gcc-c 2.安装pcre rpm -ivh pcre-8.32-17.el7.x86_64.rpm --force --nodeps rpm -ivh pcre-devel-8.32-17.el7.x86_64.rpm --force --nodeps 3.安装zlib tar -zxvf zlib-1.2.11.ta…

metaSPAdes,megahit,IDBA-UB:宏基因组装软件安装与使用

metaSPAdes,megahit,IDBA-UB是目前比较主流的宏基因组组装软件 metaSPAdes安装 GitHub - ablab/spades: SPAdes Genome Assembler #3.15.5的预编译版貌似有问题&#xff0c;使用源码安装试试 wget http://cab.spbu.ru/files/release3.15.5/SPAdes-3.15.5.tar.gz tar -xzf SP…

C++ 给父类带参构造函数的赋值

在类的使用中&#xff0c;默认的构造函数不带任何参数&#xff0c;但是也会因为需要而使用带参数的构造函数。 在带参的构造函数中&#xff0c;是如何继承的呢&#xff0c;这里我们通过使用基类&#xff0c;子类&#xff0c;孙类的两重继承来观察&#xff0c;如何给带参构造函数…

完美版视频网站模板 – 苹果CMS v10大橙子vfed主题

源码下载&#xff1a; https://download.csdn.net/download/m0_66047725/88700504 这次提供的大橙子 vfed 模板 已经完美&#xff0c;只去除了授权验证和正版主题神秘后门&#xff0c;不影响任何功能体验性。主题优化&#xff1a;全站响应式自带主题设置面板自带联盟资源库大全…