Java学习笔记(十六)


学习内容来自 B站韩顺平老师的 Java 基础课

为什么需要反射

假设现在有一个配置文件:
在这里插入图片描述
如何根据文件中指定的类名和方法创建对象并调用方法?
之前学习的传统内容都无法完成这个功能。所以需要反射

例子:

public static void main(String[] args) throws Exception {
        // 传统方法
        Cat cat = new Cat();
        cat.hi();

        // 使用 Properties 类读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src//course//javaBase//re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        System.out.println("classfullpath = " + classfullpath);
        System.out.println("method = " + methodName);

        // 直接使用字符串对象创建对象
//        new classfullpath(); // 行不通

        // 使用反射
        // 1.加载类,返回 Class 类型的对象
        Class aClass = Class.forName(classfullpath);
        // 2. 通过 aClass 对象得到想要加载的类 course.javaBase.reflection.Cat 的对象实例
        Object o = aClass.newInstance();
        // 输入运行类型
        System.out.println(o.getClass());
        // 3. 通过 aClass 得到想要加载的类 course.javaBase.reflection.Cat 的 methodName 的方法对象
        // 即,在反射中要调用的方法成为了一个对象
        Method method = aClass.getMethod(methodName);
        // 4. 通过 method 对象调用方法
        // 传统 对象.方法()
        // 反射 方法对象.invoke(对象)
        method.invoke(o);
    }

反射机制

  1. 反射机制允许程序在执行期间借助于 Reflection API 取得任何类的内部信息 (比如成员变量,构造器,成员方法等),并能操作对象的属性及方法。反射在设计模式和底层框架中都会用到
  2. 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只会对应一个 Class 对象),这个对象包含了类的完整结构信息,就像一面镜子,通过它能看到类的结构,所以称之为“反射”

反射机制原理图

在这里插入图片描述

反射机制的作用

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时得到任意一个类所具有的成员方法和变量
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

反射相关的类

  • java.lang.Class:代表一个类,Class 对象表示某个类加载后在堆中的对象
  • java.lang.reflect.Method:代表类的方法,Method 对象表示某个类的方法
  • java.lang.reflect.Field:代表类的成员变量,Field 对象表示某个类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法,Constructor 对象表示某个类的构造器

例子:

public static void main(String[] args) throws Exception {
        // 使用 Properties 类读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src//course//javaBase//re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        System.out.println("classfullpath = " + classfullpath);
        System.out.println("method = " + methodName);

        // 直接使用字符串对象创建对象
//        new classfullpath(); // 行不通

        // 使用反射
        // 1.加载类,返回 Class 类型的对象
        Class aClass = Class.forName(classfullpath);
        // 2. 通过 aClass 对象得到想要加载的类 course.javaBase.reflection.Cat 的对象实例
        Object o = aClass.newInstance();
        // 输入运行类型
        System.out.println(o.getClass());
        // 3. 通过 aClass 得到想要加载的类 course.javaBase.reflection.Cat 的 methodName 的方法对象
        // 即,在反射中要调用的方法成为了一个对象
        Method method = aClass.getMethod(methodName);
        // 4. 通过 method 对象调用方法
        // 传统 对象.方法()
        // 反射 方法对象.invoke(对象)
        method.invoke(o);

        // 获取类的成员属性
        // getField 不能得到私有的属性
        Field name = aClass.getField("name");
        // 传统 对象.成员变量
        // 反射 成员变量对象.get(对象)
        System.out.println("name = " + name.get(o));

        // 获取构造器
        // 无参构造器
        Constructor constructor = aClass.getConstructor();
        System.out.println(constructor);
        // 有参构造器,需要加上对应参数的类型
        Constructor constructor1 = aClass.getConstructor(String.class, int.class);
        System.out.println("constructor1 = " + constructor1);
    }

反射的优点和缺点

  • 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活
  • 缺点:使用反射基本是解释执行,对执行速度有影响

测试:

public static void main(String[] args) throws Exception {
        Reflection02 reflection02 = new Reflection02();
        reflection02.testClassicSpeed();
        reflection02.testReflectionSpeed();
    }

    public void testClassicSpeed() {
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 90000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法耗时 " + (end - start));
    }

    public void testReflectionSpeed() throws Exception {
    	// 获取 Class 对象时,如果对应的类还未加载,则会在此时加载
        Class aClass = Class.forName("course.javaBase.reflection.Cat");
        Object o = aClass.newInstance();
        Method hi = aClass.getMethod("hi");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 90000000; i++) {
            hi.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法耗时 " + (end - start));
    }

输出结果:

传统方法耗时 5
反射方法耗时 610

反射调用优化

在上面的例子中,无法通过 getField() 获取私有的成员属性,不过可以通过关闭访问检查来访问私有成员

  • Method、Field、Constructor 对象都有 setAccessible() 方法
  • setAccessible() 方法是启用和禁用访问检查的开关
  • 参数为 true 表示关闭访问检查,反之表示开启

例子:

 public static void main(String[] args) throws Exception {
        Reflection02 reflection02 = new Reflection02();
        reflection02.testClassicSpeed();
        reflection02.testReflectionSpeed();
        reflection02.testReflectionSpeedWithoutAccessDetection();
    }

    public void testClassicSpeed() {
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 90000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法耗时 " + (end - start));
    }

    public void testReflectionSpeed() throws Exception {
        Class aClass = Class.forName("course.javaBase.reflection.Cat");
        Object o = aClass.newInstance();
        Method hi = aClass.getMethod("hi");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 90000000; i++) {
            hi.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法耗时 " + (end - start));
    }

    // 反射访问优化:去掉访问检查
    public void testReflectionSpeedWithoutAccessDetection() throws Exception {
        Class aClass = Class.forName("course.javaBase.reflection.Cat");
        Object o = aClass.newInstance();
        Method hi = aClass.getMethod("hi");
        // 取消访问检查
        hi.setAccessible(true);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 90000000; i++) {
            hi.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法耗时 " + (end - start));
    }

Class 类

  • Class 类也是一个类,继承自 Object
  • Class 类的对象不是 new 出来的,而是系统创建出来的
  • 对于某个类的 Class 对象,在内存中只有一份,因为类只加载一次
public static void main(String[] args) throws ClassNotFoundException {
       Class aClass = Class.forName("course.javaBase.reflection.Cat");
       Class bClass = Class.forName("course.javaBase.reflection.Cat");
       System.out.println(aClass.hashCode());	// 输出结果一致
       System.out.println(bClass.hashCode());
   }
  • 每个类的实例都会记得自己是由那个 Class 实例所生成,就像每个对象都有 getClass() 方法获取自己的运行类型
  • 通过 Class 对象可以完整的得到一个类的结构
  • Class 对象存在堆内存中
  • 类的字节码二进制数据是放在方法区的,有的地方称为类的元数据
    在这里插入图片描述

常用方法


例子:

 public static void main(String[] args) throws Exception {
        String classFullPath = "course.javaBase.reflection.Car";
        // <?> 表是不确定的 Java 类型
        Class<?> aClass = Class.forName(classFullPath);
        // 获取 aClass 相应信息
        System.out.println(aClass); // class course.javaBase.reflection.Car
        System.out.println(aClass.getClass());  // class java.lang.Class

        // 获取包名
        System.out.println(aClass.getPackage().getName());  // course.javaBase.reflection

        // 获取全类名
        System.out.println(aClass.getName());   // course.javaBase.reflection.Car

        // 通过 aClass 创建对象实例
        Car car = (Car) aClass.newInstance();
        System.out.println("car = " + car); // car = Car{brand='五菱宏光', color='银色', price=100000}

        // 通过反射获取属性值
        Field brand = aClass.getField("brand");
        System.out.println("brand = " + brand.get(car));    // brand = 五菱宏光

        // 通过反射给属性赋值
        brand.set(car, "一汽大众");
        System.out.println("brand = " + brand.get(car));    // brand = 一汽大众

        // 得到所有的属性
        Field[] fields = aClass.getFields();
        for (Field f : fields) {
            System.out.println(f.getName());
        }

    }

获取 Class 类对象的方式

  • 已知一个类的全类名时,可通过 Class 类的静态方法 forName() 获取。应用场景:多用于配置文件,读取类全路径,加载类
  • 若已知具体的类,可以通过类的 class 获取,该方式最为安全可靠,程序性能最高。应用场景:多用于参数传递,比如通过反射得到对应的构造器对象
  • 若已有类的对象,可以直接通过 对象.getClass() 获取
  • 若已有类的对象,可以通过获取类加载器,再用类加载器的 .loadClass() 方法获取
  • 基本数据类型通过 基本数据类型.class 获取 Class 类对象
  • 基本数据类型对应的包装类通过 基本数据类型包装类.TYPE 获取 Class 类对象

例子:

public static void main(String[] args) throws ClassNotFoundException {
        // forNam(0
        String classAllPath = "course.javaBase.reflection.Car";
        Class<?> aClass = Class.forName(classAllPath);
        System.out.println("aClass = " + aClass);

        // 类名.class
        // 用于参数传递
        System.out.println(Car.class);

        // 对象.getClass()
        Car car = new Car();
        Class aClass1 = car.getClass();
        System.out.println("aClass1 = " + aClass1);

        // 通过类加载器获取类的 Class 对象
        // a. 得到类加载器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
        // b. 通过类加载器获取类的 Class 对象
        Class<?> aClass2 = classLoader.loadClass(classAllPath);
        System.out.println("aClass2 = " + aClass2);

        // 基本数据类型通过 基本数据类型.class 获取 Class 类对象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        System.out.println(integerClass);
        System.out.println(characterClass);

        // 基本数据类型对应的包装类通过 基本数据类型包装类.TYPE 获取 Class 类对象
        Class<Integer> type = Integer.TYPE;
        System.out.println("type = " + type);
        // 其实是同一个 Class 对象
        System.out.println(integerClass.hashCode());
        System.out.println(type.hashCode());
    }

输出结果:

aClass = class course.javaBase.reflection.Car
class course.javaBase.reflection.Car
aClass1 = class course.javaBase.reflection.Car
aClass2 = class course.javaBase.reflection.Car
int
char
type = int
1846274136
1846274136

哪些类型有 Class 对象

  • 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  • interface:接口
  • 数组
  • enum:枚举
  • annotation:注解
  • 基本数据类型
  • void

例子:

public static void main(String[] args) {
        Class<String> stringClass = String.class;   // 外部类
        Class<Serializable> serializableClass = Serializable.class; // 接口
        Class<Integer[]> aClass = Integer[].class;  // 数组
        Class<float[][]> aClass1 = float[][].class; // 二维数组
        Class<Deprecated> deprecatedClass = Deprecated.class;   // 注解
        Class<Thread.State> stateClass = Thread.State.class;    // 枚举
        Class<Long> longClass = long.class; // 基本类型
        Class<Void> voidClass = void.class; // void 类型
        Class<Class> classClass = Class.class;  // Class
        System.out.println("stringClass = " + stringClass);
        System.out.println("serializableClass = " + serializableClass);
        System.out.println("aClass = " + aClass);
        System.out.println("aClass1 = " + aClass1);
        System.out.println("deprecatedClass = " + deprecatedClass);
        System.out.println("longClass = " + longClass);
        System.out.println("voidClass = " + voidClass);
        System.out.println("classClass = " + classClass);
    }

输出结果:

stringClass = class java.lang.String
serializableClass = interface java.io.Serializable
aClass = class [Ljava.lang.Integer;
aClass1 = class [[F
deprecatedClass = interface java.lang.Deprecated
longClass = long
voidClass = void
classClass = class java.lang.Class

部分常用 API

public class ReflectionUtils {
    public static void main(String[] args) {

    }

    @Test
    public void api_01() throws ClassNotFoundException {
        Class<?> aClass = Class.forName("course.javaBase.reflection.Person");
        // 全类名
        System.out.println(aClass.getName());
        // 类名
        System.out.println(aClass.getSimpleName());
        // 获取 public 修饰的属性,包含本类及父类
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println("本类及父类的 public 属性 " + field.getName());
        }
        // 获取本类所有属性
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类的属性 " + declaredField.getName());
        }
        // 获取 public 修饰的方法,包含本类及父类
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类的 public 方法 " + method.getName());
        }
        // 获取所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类的方法 " + declaredMethod.getName());
        }
        // 获取 public 修饰的构造器
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("public 构造器 " + constructor);
        }
        // 获取所有构造器
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("构造器 " + declaredConstructor);
        }
        // 以 Package 形式返回 包信息
        System.out.println(aClass.getPackage());
        // 以 Class 形式返回 父类信息
        Class<?> superclass = aClass.getSuperclass();
        System.out.println(superclass);
        // 以 Class[] 形式返回 接口信息
        Class<?>[] interfaces = aClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }
        // 以 Annotation[] 形式返回注解信息
        Annotation[] annotations = aClass.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }

    @Test
    public void api_02() throws ClassNotFoundException {

        Class<?> aClass = Class.forName("course.javaBase.reflection.Person");
        // 全类名
        System.out.println(aClass.getName());
        // 类名
        System.out.println(aClass.getSimpleName());
        // 获取 public 修饰的属性,包含本类及父类
        Field[] fields = aClass.getDeclaredFields();
        // getModifiers 以 int 形式返回修饰符
        // 默认是 0,public 是 1, private 是 2,protected 是 4
        // static 是 8,final 是 16
        for (Field field : fields) {
            System.out.println("本类的属性 " + field.getName() + " 该属性的修饰符:" + field.getModifiers()
                            + " 该属性的类型:" + field.getType());
        }
        // 获取所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类的方法 " + declaredMethod.getName() + " 该方法的修饰符:" + declaredMethod.getModifiers()
                    + " 该方法的返回类型:" + declaredMethod.getReturnType());
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该方法形参列表 " + parameterType);
            }
        }
        // 获取所有构造器
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("构造器 " + declaredConstructor);
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该构造器形参列表 = " + parameterType);
            }
        }
    }
}


class AA {
    public String hobby;

    public AA() {

    }

    public AA(String hobby) {

    }
}

interface IAA {

}

interface IBB {
}

@Deprecated
class Person extends AA implements IAA, IBB {
    public String name;
    protected int age;
    String job;
    private double salary;

    public Person() {

    }

    public Person(String s) {

    }

    Person(int salary) {

    }
    public String m1() {
        return "";
    }

    protected void m2(String s, int age) {

    }

    void m3() {

    }

    private void m4() {

    }
}

类加载

反射机制是 Java 实现动态语言的关键,也就是通过反射实现类动态加载

  • 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
  • 动态加载:运行时加载需要的类,即使没有该类,只要运行时不用该类(if-else)就不会报错,降低了依赖性
    在这里插入图片描述
    在上述例子中,new Dog() 是静态加载,因此必须编写 Dog
    而 Person 类是动态加载,所以没有编写 Person 类也不会报错,只有到执行该语句时才会报错

类加载时机

  • 当创建对象时(new) // 静态加载
  • 当子类被加载时 // 静态加载
  • 调用类的静态成员时 // 静态加载
  • 通过反射 //动态加载

类加载过程

在这里插入图片描述

其中类加载各个阶段的任务为:
在这里插入图片描述

加载阶段

JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包、甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象

验证阶段

  1. 目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
  2. 包括:文件格式验证(是否以魔数 oxcafebabe 开头)、元数据验证、字节码验证和符号引用验证
  3. 可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间

准备阶段

  • JVM 会在该阶段对静态变量分配内存并默认初始化(对应数据类型的默认初始化,如 0、0L、null、false 等 )。这些变量所使用的内存都将在方法区中进行分配
    在这里插入图片描述

解析阶段

  1. 虚拟机将常量池的符号引用替换为直接引用的过程
  2. 就像有两个类 A 和 B,在类加载之前它们之间的引用就只是符号引用(A 中的某个方法调用了 B 的某个方法,诸如此类),但是运行时需要知道引用内容的具体地址才能读取运行指令,于是 JVM 会将其转化为直接引用,就像是符号替换为实际的功能代码地址

初始化阶段

  1. 初始化阶段才真正执行类中定义的 Java 程序代码,此阶段是执行 <clinit>() 方法的过程
  2. <clinit>() 方法是由编译器按语句在源文件中出现的顺序,一次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并
  3. 虚拟机会保证一个类的 <clinit>() 方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的 <clinit>() 方法,其它线程都需要阻塞等待,直到hu活动线程执行 <clinit>() 方法完毕

通过反射创建对象

  • 调用 public 修饰的无参构造器
  • 调用指定构造器

例子:

public class ClassLoad02 {
    public static void main(String[] args) throws Exception {
        // 获取 User 的 Class 对象
        Class<?> aClass = Class.forName("course.javaBase.reflection.User");
        // 通过 public 的无参构造器创建实例
        Object o = aClass.newInstance();
        System.out.println("o = " + o);
        // 通过 public 的有参构造器创建实例
        /**
         * public User(String name) {
         *         this.name = name;
         *     }
         */
        Constructor<?> constructor = aClass.getConstructor(String.class);
        // 先得到构造器,再通过构造器传入形参创建实例
        Object jerry = constructor.newInstance("杰瑞给");
        System.out.println("杰瑞给 = " + jerry);
        // 通过通过非 public 的有参构造器创建实例
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
        // 获取访问权限
        declaredConstructor.setAccessible(true);
        Object jerryDog = declaredConstructor.newInstance("接若狗", 666);
        System.out.println("jerryDog = " + jerryDog);
    }
}

class User {
    private int age = 10;
    private String name = "jerry";
    public User() {

    }

    public User(String name) {
        this.name = name;
    }

    private User(String name, int age) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

通过反射操作属性

  • 根据属性名获取 Field 对象
  • 可以通过 setAccessible() 接触访问权限
  • 方法 属性.set(对象) 可以设置对象对应的属性值,如果是静态属性,对象可以为 null

例子:

public class AccessProperty {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("course.reflection.Student");
        // 创建对象
        // o 的运行类型是 Student
        Object o = aClass.newInstance();
        System.out.println("o = " + o.getClass());
        // 获取属性 age 的对象
        Field age = aClass.getField("age");
        // 通过反射操作属性
        age.set(o, 88);
        System.out.println("o = " + o);
        System.out.println("age = " + age.get(o));

        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(o, "jerry");
        System.out.println("o = " + o);
        System.out.println("name = " + name.get(o));
        // 静态属性可以直接用 null
        // 因为静态属性属于所有对象,不用特别指定对象
        name.set(null, "jerryDog");
        System.out.println("o = " + o);
        System.out.println("name = " + name.get(null));
    }
}

class Student {
    public int age;
    private static String name;

    public Student() {

    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age + ", name =" + name +
                '}';
    }
}

通过反射操作方法

  • 根据方法名和形参列表获取 Method 方法对象
  • 如果时静态方法,那么 invoke 调用的时候也可写作 null

例子:

public class AccessMethod {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("course.reflection.Boss");
        // 创建对象
        Object o = aClass.newInstance();
        // 调用 public 方法
        // 两种方式获得的方法对象一致
        Method hi = aClass.getMethod("hi", String.class);
        Method hi1 = aClass.getDeclaredMethod("hi", String.class);
        System.out.println("hi = " + hi);
        System.out.println("hi1 = " + hi1);
        // 调用
        hi.invoke(o, "jerry");
        hi1.invoke(o, "jerryDog");

        // 调用 static 方法
        Method say = aClass.getDeclaredMethod("say", int.class, String.class, char.class);
        say.setAccessible(true);
        System.out.println(say.invoke(o, 666, "jerry", 'X'));
        System.out.println(say.invoke(null, 777, "jerryDog", 'X'));
    }
}

class Boss {
    public int age;
    private static String name;

    public Boss() {

    }

    private static String say(int n, String s, char c) {
        return n + " " + s + " " + c;
    }

    public void hi(String s) {
        System.out.println("hi " + s);
    }
}

反射应用-动态代理

首先需要知道代理设计模式的原理:

  • 封装对象,使用代理对象取代原始对象
  • 任何对原始对象的调用都要通过代理
  • 代理对象决定决定是否或者何时将方法调用转到原始对象上

在静态代理中,代理类和目标类都是在编译期间确定下来的,不利于程序的扩展

静态代理举例:

public class StaticProxyTest {
    public static void main(String[] args) {
        VehicleFactoryA vehicleFactoryA = new VehicleFactoryA();
        ProxyFactoryA proxyFactoryA = new ProxyFactoryA(vehicleFactoryA);
        proxyFactoryA.produce(); 
    }
}

// 代理类
class ProxyFactoryA implements FactoryA {

    private FactoryA factoryA;

    public ProxyFactoryA(FactoryA factoryA) {
        this.factoryA = factoryA;
    }

    @Override
    public void produce() {
        System.out.println("代理类做一些准备工作");

        factoryA.produce();

        System.out.println("代理类做一些收尾工作");
    }
}

// 被代理类
class VehicleFactoryA implements FactoryA {

    @Override
    public void produce() {
        System.out.println("生产汽车");
    }
}
interface FactoryA {
    public void produce();
}

动态代理实现

静态代理的每一个代理只能为一个接口服务,会导致程序开发的过程中出现过多的代理,所以可以通过反射实现动态代理来优化

  • 动态代理就是通过代理类来调用其他对象的方法,可以在程序运行时,根据需要动态创建目标类的代理对象
  • 使用场合:调试、远程方法调用
  • 优点:一个代理类搞定所有静态代理类的功能,灵活且通用

动态代理举例:

  1. 首先需要有被代理类和公用的工具类
interface Human {
    String getBelief();

    void eat(String food);
}

// 被代理类
class Man implements Human {

    @Override
    public String getBelief() {
        return "I can eat more";
    }

    @Override
    public void eat(String food) {
        System.out.println("eat " + food);
    }
}

// 工具类
class HumanUtils {
    public void method01() {
        System.out.println("通用方法 1~~~~~~~~~~~");
    }

    public void method02() {
        System.out.println("通用方法 2~~~~~~~~~~~");
    }
}
  1. 实现 InvocationHandler 接口,创建方法调用类
class MyInvocationHandler implements InvocationHandler {

    // 用来存被代理类的对象
    private Object object;

    public void bind(Object object) {
        this.object = object;
    }

    /**
     *
     * @param proxy 代理类的对象
     * @param method 代理类要调用的方法
     * @param args 被调用方法的参数
     * @return
     * @throws Throwable
     *
     * 当通过代理类的对象调用被代理类的方法时,会通过该方法来实现方法的调用
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        HumanUtils utils = new HumanUtils();
        utils.method01();   // 预处理
        // 调用方法
        // 被调用方法.invoke(被代理类, 参数列表)
        // 这样就实现了当代理类调用方法 method 时,就会调用被代理类的对应方法 method
        Object returnValue = method.invoke(object, args);
        utils.method02();   // 收尾
        return returnValue;
    }
}
  1. 创建代理类的生成类,用来动态生成代理类
class ProxyFactory {
    // 返回代理类的对象
    public static Object getProxyInstance(Object o) {   // o 为被代理类

        MyInvocationHandler invocationHandler = new MyInvocationHandler();
        // 给方法调用器绑定被代理类,在调用方法时使用
        invocationHandler.bind(o);

        // Proxy 是 java.lang.reflect 包下的类
        // newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法可以返回一个代理类的实例
        // 三个参数的含义如下
        /**
         * ClassLoader loader 为对应被代理类的类加载器(代理类需要知道自己明确被代理的类)
         * Class<?>[] interfaces 为被代理类实现的接口(代理类也要实现这些接口)
         * InvocationHandler h 实现了 InvocationHandler 接口的类的对象
         */
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), invocationHandler);
    }
}
  1. 测试:使用生成类生成被代理类的对象,并调用方法
public class ProxyTest {
    public static void main(String[] args) {
        Man man = new Man();
        // 获得代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(man);
        // 调用被代理类的方法
        // 即通过方法调用器调用被代理的同名方法
        System.out.println(proxyInstance.getBelief());
        proxyInstance.eat("牛肉烩馍");
        // 生成上个例子中被代理类的代理类
        VehicleFactoryA factoryA = new VehicleFactoryA();
        FactoryA proxyInstance1 = (FactoryA) ProxyFactory.getProxyInstance(factoryA);
        proxyInstance1.produce();
    }
}

输出结果:

通用方法 1~~~~~~~~~~~
通用方法 2~~~~~~~~~~~
I can eat more
通用方法 1~~~~~~~~~~~
eat 牛肉烩馍
通用方法 2~~~~~~~~~~~
通用方法 1~~~~~~~~~~~
生产汽车
通用方法 2~~~~~~~~~~~
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值