16.1 Java反射机制概述

1、Java Reflection

  • Reflection(反射)被视为动态语言的关键,反射机制允许程序在执行期间借助于反射API获得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象地称之为:反射。

2、动态语言、静态语言

  • 动态语言

是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其结构上的变化。

通俗点说就是:在运行时,代码可以根据某些条件改变自身结构。

主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。

  • 静态语言

与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。

Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

3、Java反射机制研究及应用

  • Java反射机制提供的功能:
    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时获取泛型信息
    • 在运行时调用任意一个对象的成员变量和方法
    • 在运行时处理注解
    • 生成动态***

4、反射相关的主要API

  • java.lang.Class:代表一个类

  • java.lang.reflect.Method:代表类的方法

  • java.lang.reflect.Field:代表类的成员变量

  • java.lang.reflect.Constructor:代表类的构造器

  • ……

  import org.junit.jupiter.api.Test;

  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;

  public class ReflectionTest {
      /**
       * 反射之前,对Person的操作。
       */
      @Test
      public void test1() {
          // 1、创建Person类的对象
          Person p1 = new Person("Tom", 12);
          // 2、通过对象,调用其内部的属性、方法
          p1.age = 10;
          System.out.println(p1.toString());
          p1.show();
          // 在Person类外部,不可以通过Person类对象,调用其内部私有结构。
          // 比如:name、showNation()、私有的构造器
      }

      /**
       * 反射之后,对Person的操作。
       */
      @Test
      public void test2() throws Exception {
          Class clazz = Person.class;
          // 1、通过反射,创建Person类的对象
          Constructor cons = clazz.getConstructor(String.class, int.class);
          Object obj = cons.newInstance("Tom", 12);
          Person p = (Person) obj;
          System.out.println(p);
          // 2、通过反射,调用对象指定的属性、方法
          // 调用属性
          Field age = clazz.getDeclaredField("age");
          age.set(p, 10);
          System.out.println(p);
          // 调用方法
          Method show = clazz.getDeclaredMethod("show");
          show.invoke(p);
          // 3、通过反射,可以调用Person类的私有结构。比如:私有的构造器、方法、属性
          // 调用私有的构造器
          Constructor cons1 = clazz.getDeclaredConstructor(String.class);
          cons1.setAccessible(true);
          Person p1 = (Person) cons1.newInstance("Jerry");
          System.out.println(p1);
          // 调用私有的属性
          Field name = clazz.getDeclaredField("name");
          name.setAccessible(true);
          name.set(p1, "HanMeimei");
          System.out.println(p1);
          // 调用私有的方法
          Method showNation = clazz.getDeclaredMethod("showNation", String.class);
          showNation.setAccessible(true);
          String nation = (String) showNation.invoke(p1, "中国");// 相当于String nation = p1.showNation("中国")
          System.out.println(nation);
      }
  }
  public class Person {
      private String name;
      public int age;

      public Person() {

      }

      private Person(String name) {
          this.name = name;
      }

      public Person(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;
      }

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

      public void show() {
          System.out.println("你好,我是一个人");
      }

      private String showNation(String nation) {
          System.out.println("国籍:" + nation);
          return nation;
      }

  }

5、思考

  • 通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?

建议new的方式。

  • 什么时候会用反射的方式?

反射的特征:动态性。根据请求动态创建相应对象时使用反射。

6、如何看待反射和封装性这两个技术

  • 不矛盾。

16.2 理解Class类并获取Class实例

  • java.lang.Class

1、类的加载过程

程序经过javac.exe命令后,会生成一个或多个字节码文件(.class)。

接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类就作为Class的实例。

换句话说,Class的实例就对应着一个运行时类。

加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

2、获取Class实例

  • 方式:

    1. 调用运行时类的属性:.class。
    2. 通过运行时类的对象,调用getClass()方法。
    3. 调用Class的静态方法:Class.forName(String classPath)。
    4. 使用类的加载器:ClassLoader。
  import org.junit.jupiter.api.Test;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.sql.Ref;
  public class ReflectionTest {
      /**
       * 获取Class的实例的方式。
       */
      @Test
      public void test1() {
          // 方式一、调用运行时类的属性:.class
          Class<Person> clazz1 = Person.class;
          System.out.println(clazz1); // class Person
          // 方式二、通过运行时类的对象,调用getClass()
          Person p1 = new Person();
          Class<? extends Person> clazz2 = p1.getClass();
          System.out.println(clazz2); // class Person
          // 方式三、调用Class的静态方法:forName(Stirng classPath)
          Class clazz3 = null;
          try {
              clazz3 = Class.forName("Person");
              System.out.println(clazz3); // class Person
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
          System.out.println(clazz1 == clazz2);   // true
          System.out.println(clazz1 == clazz3);   // true
          // 方式四、使用类的加载器:ClassLoader
          ClassLoader classLoader = ReflectionTest.class.getClassLoader();
          Class<?> clazz4 = null;
          try {
              clazz4 = classLoader.loadClass("Person");
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
          System.out.println(clazz1 == clazz4);   // true
      }
  }

3、哪些类型可以有Class对象?

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。

  • interface:接口

  • [ ]:数组

  • enum:枚举

  • annotation:注解@interface

  • primitive type:基本数据类型

  • void

  import org.junit.jupiter.api.Test;

  import java.lang.annotation.ElementType;

  public class ReflectionTest {
      @Test
      public void test1() {
          Class<Object> objectClass = Object.class;
          Class<Comparable> comparableClass = Comparable.class;
          Class<String[]> aClass = String[].class;
          Class<int[][]> aClass1 = int[][].class;
          Class<ElementType> elementTypeClass = ElementType.class;
          Class<Override> overrideClass = Override.class;
          Class<Integer> integerClass = int.class;
          Class<Void> voidClass = void.class;
          Class<Class> classClass = Class.class;

          int[] a = new int[10];
          int[] b = new int[100];
          Class<? extends int[]> aClass2 = a.getClass();
          Class<? extends int[]> aClass3 = b.getClass();
          // 只要数组元素类型与维度一样,就是同一个Class
          System.out.println(aClass2 == aClass3); // true
      }

  }

16.3 类的加载与ClassLoader的理解

1、类的加载

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
    • 验证:确保架子啊的类信息符合JVM规范。例如:以cafe开头,没有安全方面的问题。
    • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化:
    • 执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。</clinit></clinit>
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
    • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。</clinit>

2、ClassLoader

  • 类加载器的作用:

    • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
    • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
  • 分类:

    • Bootstrap ClassLoader:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来加载核心类库。该加载器无法直接获取。
    • Extension ClassLoader:负责jre/lib/ext目录下的jar包或-D java ext.dirs指定目录下的jar包装入工作库。
    • System ClassLoader:负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。
    • 自定义加载器。
  import org.junit.jupiter.api.Test;

  public class ClassLoaderTest {
      @Test
      public void test() {
          // 对于自定义类,使用系统类加载器进行加载
          ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
          // 调用系统类加载器的getParent()方法,可以获得扩展类加载器
          System.out.println(classLoader); //jdk.internal.loader.ClassLoaders$AppClassLoader@15db9742
          ClassLoader parent = classLoader.getParent();
          System.out.println(parent); // jdk.internal.loader.ClassLoaders$PlatformClassLoader@76707e36
          // 掉一共扩展类加载器的getParent()方法,无法获得引导类加载器
          ClassLoader parent1 = parent.getParent();
          System.out.println(parent1);    // null
      }
  }

3、使用ClassLoader加载配置文件

    import org.junit.jupiter.api.Test;

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;

    public class ClassLoaderTest {
        @Test
        public void test1() throws IOException {
            Properties properties = new Properties();
            // 此时的文件默认在当前Project下。
    //        properties.load(new FileInputStream("src\\main\\resources\\jdbc.properties"));
            properties.load(new FileInputStream("jdbc.properties"));
            String user = properties.getProperty("user");
            String password = properties.getProperty("password");
            System.out.println(user);
            System.out.println(password);
        }

        @Test
        public void test2() throws IOException {
            Properties properties = new Properties();
            ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
            // 此时的文件默认在src下
            InputStream is = classLoader.getResourceAsStream("jdbc.properties");
            properties.load(is);
            String user = properties.getProperty("user");
            String password = properties.getProperty("password");
            System.out.println(user);
            System.out.println(password);
        }
    }

16.4 创建运行时类的对象

1、创建方式

    import org.junit.jupiter.api.Test;

    /**
     * 通过反射创建对应的运行时类的对象。
     */
    public class NewInstanceTest {
        @Test
        public void test() throws IllegalAccessException, InstantiationException {
            Class<Person> clazz = Person.class;
            /*
                newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的无参的构造器。

                要想此方法正常的创建运行时类的对象,要求:
                1、运行时类必须提供无参的构造器,否则报:InstantiationException
                2、无参的构造器的访问权限足够,通常设置为public。否则报:IllegalAccessException

                在JavaBean中,要求提供一个public的无参构造器。原因:
                1、便于通过反射,创建运行时类的对象。
                2、便于子类继承此运行时类,默认调用super()时,保证父类有此构造器。
             */
            Person person = clazz.newInstance();
            System.out.println(person);
        }
    }

2、理解反射的动态性

    import org.junit.jupiter.api.Test;

    import java.util.Random;

    public class NewInstanceTest {
        @Test
        public void test() throws Exception {
            int num = new Random().nextInt(3);//0,1,2
            String classPath = "";
            switch (num) {
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "Person";
                    break;
            }
            Object instance = getInstance(classPath);
            System.out.println(instance);
        }

        /**
         * 创建一个指定类的对象
         * @param classPath:指定类的全类名
         * @return
         * @throws ClassNotFoundException
         * @throws IllegalAccessException
         * @throws InstantiationException
         */
        public Object getInstance(String classPath) throws Exception {
            Class<?> clazz = Class.forName(classPath);
            return clazz.newInstance();
        }
    }

16.5 获取运行时类的完整结构

1、测试类信息

  • Creature

    import java.io.Serializable;
    
    public class Creature<T> implements Serializable {
        public static final long serialVersionUID = 4654654687L;
        private char gender;
        public double weight;
        private void breath() {
            System.out.println("生物呼吸");
        }
        public void eat() {
            System.out.println("生物吃东西");
        }
    }
  • MyInterface

    public interface MyInterface {
        void info();
    }
  • MyAnnotation

    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.*;
    
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        String value() default "hello";
    }
  • Person

    @MyAnnotation(value="hi")
    public class Person extends Creature<String> implements Comparable<String>, MyInterface {
        private String name;
        int age;
        public int id;
    
        public Person() {}
    
        @MyAnnotation(value="abc")
        private Person(String name) {
            this.name = name;
        }
    
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @MyAnnotation
        private String show(String nation) throws IllegalAccessException, ClassNotFoundException {
            System.out.println("我的国籍是:" + nation);
            return nation;
        }
    
        public String display(String interests) {
            return interests;
        }
    
        @Override
        public int compareTo(String o) {
            return 0;
        }
    
        @Override
        public void info() {
            System.out.println("我是一个人");
        }
    
        private static void showDesc() {
            System.out.println("我是一个可爱的人");
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", id=" + id +
                    '}';
        }
    
    }

2、获取属性结构

    import org.junit.jupiter.api.Test;

    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;

    public class FieldTest {
        @Test
        public void test1() {
            Class<Person> clazz = Person.class;
            // 获取属性结构
            // getFields():获取当前运行时类及其所有父类中,声明为public的属性。
            Field[] fields = clazz.getFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            System.out.println();
            // getDeclaredFields():获取当前运行时类中,声明的所有属性(不包含父类中声明的属性)。
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                System.out.println(field);
            }
        }

        // 权限修饰符、数据类型、变量名
        @Test
        public void test2() {
            Class<Person> clazz = Person.class;
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field f : declaredFields) {
                // 1、权限修饰符
                int modifier = f.getModifiers();
                System.out.print(Modifier.toString(modifier) + " ");
                // 2、数据类型
                Class<?> type = f.getType();
                System.out.print(type.getName() + " ");
                // 3、变量名
                String name = f.getName();
                System.out.print(name);
                System.out.println();
            }
        }
    }

3、获取方法结构

    import org.junit.jupiter.api.Test;

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.Parameter;

    public class MethodTest {
        @Test
        public void test1() {
            Class<Person> clazz = Person.class;
            // getMethods():获取当前运行时类及其所有父类中,声明为public的方法。
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
            System.out.println();
            // getDeclaredMethods():获取当前运行时类中声明的所有方法(不包含父类中声明的)。
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                System.out.println(method);
            }
        }

        // 权限修饰符、返回值类型、方法名、参数类型、形参名、异常列表、注解
        @Test
        public void test2() {
            Class<Person> clazz = Person.class;
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                // 1、获取方法声明的注解
                Annotation[] annotations = method.getAnnotations();
                for (Annotation annotation : annotations) {
                    System.out.println(annotation);
                }
                // 2、权限修饰符
                int modifiers = method.getModifiers();
                System.out.print(Modifier.toString(modifiers) + " ");
                // 3、返回值类型
                Class<?> returnType = method.getReturnType();
                System.out.print(returnType.getName() + " ");
                // 4、方法名
                System.out.print(method.getName() + "(");
                // 5、形参列表
                Parameter[] parameters = method.getParameters();
                for (Parameter parameter : parameters) {
                    System.out.print(parameter.getType().getName());
                    System.out.print(" " + parameter.getName());
                }
                System.out.print(")");
                // 6、异常列表
                Class<?>[] exceptionTypes = method.getExceptionTypes();
                if (exceptionTypes.length > 0) {
                    System.out.print(" throws");
                    for (int i = 0; i < exceptionTypes.length; i++) {
                        if (i == exceptionTypes.length-1) {
                            System.out.print(" " + exceptionTypes[i]);
                            break;
                        }
                        System.out.print(" " + exceptionTypes[i] + ",");
                    }
                }
                System.out.println();
            }
        }
    }

4、获取其他结构

    import org.junit.jupiter.api.Test;

    import java.lang.annotation.Annotation;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;

    public class OtherTest {
        /**
         * 获取构造器
         */
        @Test
        public void test1() {
            Class<Person> clazz = Person.class;
            // getConstructors():获取当前运行时类中,声明为public的构造器。
            Constructor<?>[] constructors = clazz.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }
            System.out.println();
            // getDeclaredConstructors():获取当前运行时类中声明的所有构造器。
            Constructor<?>[] declaredClasses = clazz.getDeclaredConstructors();
            for (Constructor<?> constructor : declaredClasses) {
                System.out.println(constructor);
            }
        }

        /**
         * 获取运行时类的父类
         */
        @Test
        public void test2() {
            Class<Person> clazz = Person.class;
            Class<? super Person> superclass = clazz.getSuperclass();
            System.out.println(superclass);
        }

        /**
         * 获取运行时类的带泛型的父类
         */
        @Test
        public void test3() {
            Class<Person> clazz = Person.class;
            Type genericSuperclass = clazz.getGenericSuperclass();
            System.out.println(genericSuperclass);
        }

        /**
         * 获取运行时类的带泛型的父类的泛型
         */
        @Test
        public void test4() {
            Class<Person> clazz = Person.class;
            Type genericSuperclass = clazz.getGenericSuperclass();
            ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
            // 获取泛型类型
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            System.out.println(actualTypeArguments[0]);
            System.out.println(actualTypeArguments[0].getTypeName());
            System.out.println(((Class)actualTypeArguments[0]).getName());
        }

        /**
         * 获取运行时类的接口
         */
        @Test
        public void test5() {
            Class<Person> clazz = Person.class;
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class inter : interfaces) {
                System.out.println(inter);
            }
            System.out.println();
            // 获取运行时类的父类的实现的接口
            Class<?>[] interfaces1 = clazz.getSuperclass().getInterfaces();
            for (Class inter : interfaces1) {
                System.out.println(inter);
            }
        }

        /**
         * 获取运行时类所在的包
         */
        @Test
        public void test6() {
            Class<Person> clazz = Person.class;
            Package aPackage = clazz.getPackage();
            System.out.println(aPackage);
        }

        /**
         * 获取运行时类声明的注解
         */
        @Test
        public void test7() {
            Class<Person> clazz = Person.class;
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
        }
    }

16.6 调用运行时类的指定结构

    import org.junit.jupiter.api.Test;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;

    public class ReflectionTest {
        /**
         * 属性
         */
        @Test
        public void test1() throws Exception {
            Class<Person> clazz = Person.class;
            // 创建运行时类的对象
            Person p = clazz.newInstance();
            // 获取指定的属性:要求运行时类中属性声明为public(通常不采用此方法)
            Field id = clazz.getField("id");
            /*
                设置当前属性的值
                set():
                    参数1:指明设置哪个对象的属性
                    参数2:将此属性值设置为多少
             */
            id.set(p, 1001);
            /*
                获取当前属性的值
                get():
                    参数1:获取哪个对象的当前属性
             */
            Object pId = id.get(p);
            System.out.println(pId);
        }

        /**
         * 如何操作运行时类中的指定属性!!!
         * @throws Exception
         */
        @Test
        public void test2() throws Exception {
            Class<Person> clazz = Person.class;
            // 创建运行时类的对象
            Person p = clazz.newInstance();
            // 1、getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
            Field name = clazz.getDeclaredField("name");
            // 2、保证当前属性是可访问的
            name.setAccessible(true);
            // 3、获取,设置指定对象的此属性值
            name.set(p, "Tom");
            System.out.println(name.get(p));
            // 静态属性的操作如下:静态方法的操作
        }

        /**
         * 如何操作运行时类中的指定方法!!!
         */
        @Test
        public void test3() throws Exception {
            Class<Person> clazz = Person.class;
            // 创建运行时类的对象
            Person p = clazz.newInstance();
            /*
                 1、获取指定的某个方法
                 getDeclaredMethod():
                    参数1:指明获取的方法的名称
                    参数2:指明获取的方法的形参列表
             */
            Method show = clazz.getDeclaredMethod("show", String.class);
            // 2、保证当前方法是可访问的
            show.setAccessible(true);
            /*
                3、调用方法
                invoke():
                    参数1:方法的调用者
                    参数2:给方法形参传递的实参
                    返回值:对应方法的返回值
             */
            Object returnVal = show.invoke(p, "中国");
            System.out.println(returnVal);
            System.out.println("************静态方法************");
            Method showDesc = clazz.getDeclaredMethod("showDesc");
            showDesc.setAccessible(true);
            // 如果调用的运行时类中的方法没有返回值,则此invoke()返回null
    //        Object returnVal2 = showDesc.invoke(clazz);
            Object returnVal2 = showDesc.invoke(null);
            System.out.println(returnVal2);
        }

        /**
         * 如何调用运行时类中的指定的构造器
         */
        @Test
        public void test4() throws Exception {
            Class<Person> clazz = Person.class;
            /*
                1、获取指定的构造器
                    getDeclaredConstructor()
                        参数:指明构造器的参数列表
             */
            Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
            // 2、保证此构造器是可访问的
            constructor.setAccessible(true);
            // 3、调用此构造器创建运行时类的对象
            Person p = constructor.newInstance("Tom");
            System.out.println(p);
        }
    }

16.7 反射的应用:动态***

1、***设计模式

  • 原理

图片说明

使用一个***将对象包装起来,然后用该***对象取代原始对象。任何对原始对象的调用都要通过***。***对象决定是否以及何时将方法调用转到原始对象上。

  • 静态***

***类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个***类只能为一个接口服务,这样一来程序开发中必然产生过多的***。

  • 动态***

动态***是指客户通过***类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的***对象。

动态***使用场合:

  • 调试
  • 远程方法调用
  • 动态***相比于静态***的优点

抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

2、静态***

  • 特点:***类和被***类在编译期间,就确定下来了。
  import org.junit.jupiter.api.Test;

  interface ClothFactory {
      void produceCloth();
  }

  // ***类
  class ProxyClothFactory implements ClothFactory {

      private ClothFactory factory;   // 用被***类对象进行实例化

      public ProxyClothFactory() {
      }

      public ProxyClothFactory(ClothFactory factory) {
          this.factory = factory;
      }

      @Override
      public void produceCloth() {
          System.out.println("***工厂做一些准备工作");
          factory.produceCloth();
          System.out.println("***工厂做一些后续的收尾工作");
      }

  }

  // 被***类
  class NikeClothFactory implements ClothFactory {

      @Override
      public void produceCloth() {
          System.out.println("Nike工厂生产一批运动服");
      }

  }

  public class StaticProxyTest {
      @Test
      public void test() {
          // 创建被***类的对象
          ClothFactory nike = new NikeClothFactory();
          // 创建***类的对象
          ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
          proxyClothFactory.produceCloth();
      }
  }

3、动态***

  • 要想实现动态***,需要解决的问题:

    • 问题一:如何根据加载到内存中的被***类,动态的创建一个***类及其对象?
    • 问题二:当通过***类的对象调用方法时,如何动态的去调用被***类中的同名方法?
  import org.junit.jupiter.api.Test;

  import java.lang.reflect.InvocationHandler;
  import java.lang.reflect.Method;
  import java.lang.reflect.Proxy;

  interface Human {
      String getBelief();

      void eat(String food);

  }

  // 被***类
  class SuperMan implements Human {

      @Override
      public String getBelief() {
          return "I believe I can fly!";
      }

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

  }

  class ProxyFactory {
      /**
       * 动态***
       *
       * @param obj 被***类的对象
       * @return ***类的对象(解决问题一)
       */
      public static Object getProxyInstance(Object obj) {
          MyInvocationHandler handler = new MyInvocationHandler();
          handler.bind(obj);
          return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
      }
  }

  class MyInvocationHandler implements InvocationHandler {
      private Object obj; // 被***类的对象

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

      /**
       * 当我们通过***类的对象,调用方法a时,就会自动的调用此方法。
       * 将被***类要执行的方法a的功能声明在invoke()中。
       *
       * @param proxy  ***类的对象
       * @param method 被***类对应的方法
       * @param args   被***类对应方法的形参
       * @return 被***类对应方法的返回值
       * @throws Throwable
       */
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          Object returnValue = method.invoke(obj, args);
          return returnValue;
      }

  }

  public class ProxyTest {
      @Test
      public void test() {
          SuperMan superMan = new SuperMan();
          // proxy:***类的对象
          Human proxy = (Human) ProxyFactory.getProxyInstance(superMan);
          // 当通过***类对象调用方法时,此方法也就作为了被***类中同名的方法。
          System.out.println(proxy.getBelief());
          proxy.eat("饭");
          System.out.println("***************");
          ClothFactory nikeClothFactory = new NikeClothFactory();
          ClothFactory proxyInstance = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
          proxyInstance.produceCloth();
      }
  }

4、动态***与AOP(Aspect Orient Programming)

    import org.junit.jupiter.api.Test;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;

    interface Human {
        String getBelief();

        void eat(String food);
    }

    // 被***类
    class SuperMan implements Human {

        @Override
        public String getBelief() {
            return "I believe I can fly!";
        }

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

    class ProxyFactory {
        /**
         * 动态***
         *
         * @param obj 被***类的对象
         * @return ***类的对象(解决问题一)
         */
        public static Object getProxyInstance(Object obj) {
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.bind(obj);
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
        }
    }

    class HumanUtil {
        public void method1() {
            System.out.println("********************通用方法一********************");
        }

        public void method2() {
            System.out.println("********************通用方法二********************");
        }
    }

    class MyInvocationHandler implements InvocationHandler {
        private Object obj; // 被***类的对象

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

        /**
         * 当我们通过***类的对象,调用方法a时,就会自动的调用此方法。
         * 将被***类要执行的方法a的功能声明在invoke()中。
         *
         * @param proxy  ***类的对象
         * @param method 被***类对应的方法
         * @param args   被***类对应方法的形参
         * @return 被***类对应方法的返回值
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            HumanUtil humanUtil = new HumanUtil();
            humanUtil.method1();
            Object returnValue = method.invoke(obj, args);
            humanUtil.method2();
            return returnValue;
        }
    }

    public class ProxyTest {
        @Test
        public void test() {
            SuperMan superMan = new SuperMan();
            // proxy:***类的对象
            Human proxy = (Human) ProxyFactory.getProxyInstance(superMan);
            // 当通过***类对象调用方法时,此方法也就作为了被***类中同名的方法。
            System.out.println(proxy.getBelief());
            proxy.eat("饭");
            System.out.println("***************");
            ClothFactory nikeClothFactory = new NikeClothFactory();
            ClothFactory proxyInstance = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
            proxyInstance.produceCloth();
        }
    }