20210330Java反射机制

Java反射机制

编辑时间:2021/03/30

读完本节:大概花费60钟,共6779词

1.Java反射机制概述
  1. Reflection(反射)是被视为动态语言的关键,反射机制运行程序在执行期借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性和方法

  2. 加载完类之后,在堆的方法区中就产生了一个Class类型的对象(一个类只有一个Class类对象)这个对象就包含了完整的类的结构和信息。可以通过这个对象看到类的结构。

  3. 正常情况下获取实例以及通过反射获取类的结构

    image-20210330080559666

  4. 动态语言与静态语言

    1. 动态语言:动态语言时一类在运行时可以改变其结构的语言,例如新的函数,对象甚至代码可以被引进,已有的函数可以被删除或者时其他结构上的变化。即在运行时代码可以根据某些条件改变自身结构

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

    2. 静态语言:运行时结构不可变的语言就是静态语言。如Java、C、C++

    3. Java不是动态语言,但是Java可以称为“准动态语言”。即Java有一定的动态性,可以利用反射机制、字节码操作获得类似的动态语言的特性

  5. Java反射机制提供的功能

    1. 在运行时判断任意一个对象所属的类
    2. 在运行时构造任意一个类的对象
    3. 在运行时构造任意一个类所具有的成员变量和方法
    4. 在运行时获取泛型信息
    5. 在运行时调用任意一个对象的成员变量和方法
    6. 在运行时处理注解
    7. 生成动态代理
  6. 反射相关的主要API

    1. java.lang.Class:代表一个类
    2. java.lang.reflect.Method:代表类的方法
    3. java.lang.reflect.Field:代表类的成员变量
    4. java.lang.reflect.Constructor:代表类的构造器
  7. ReflectionTest

    import org.junit.Test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 8:16
     * @Description:
     */
    public class ReflectionTest {
        @Test
        /** 反射之前对Person的操作 */
        public void test1(){
            //1. 创建Person类的对象
            Person p = new Person("Biggy", 21);
    
            //2. 通过对象,调用其内部的属性和方法
            p.age = 22;
            System.out.println(p.toString());
    
            p.show();
            //3. 在Person类外部,不可以通过Person类的对象调用其内部的私有结构
    
        }
    
        @Test
        /** 反射之后,对于person的操作 */
        public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
            //1. 通过反射创建Person类的对象
            Class clazz = Person.class;
            //通过反射获取Person中的构造器
            Constructor cons = clazz.getConstructor(String.class, int.class);
    
            Object obj = cons.newInstance("Biggy", 22);
            Person p = (Person) obj;
            System.out.println(p.toString());
    
            //2. 通过反射,调用对象指定的属性、方法
            //调用属性
            Field age = clazz.getDeclaredField("age");
            age.set(p,21);
            //调用方法
            Method show = clazz.getDeclaredMethod("show");
            show.invoke(p);
    
            //通过反射,可以调用Person类的私有结构,比如私有的构造器、方法、属性
            //通过反射调用私有的构造器
            Constructor cons1 = clazz.getDeclaredConstructor(String.class);
            cons1.setAccessible(true);
            Person p1 = (Person)cons1.newInstance("frank");
            System.out.println(p1);
    
            //通过反射调用私有的属性
            Field name = clazz.getDeclaredField("name");
            name.setAccessible(true);
            name.set(p1,"bili");
            System.out.println(p1);
    
            //调用私有的方法
            Method showNation = clazz.getDeclaredMethod("showNation", String.class);
            showNation.setAccessible(true);
            //相当于:p1.showNation("CHN");
            System.out.println(showNation.invoke(p1, "CHN"));
        }
    }
    

    Person

    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 8:17
     * @Description:
     */
    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("A person");
        }
    
        private String showNation(String nation){
            return "nation is: " + nation;
        }
    
    }
    
  8. 通过直接new的方式和获取反射的方式都可以调用公共结构,开发中优先选择使用哪一个?

    1. 直接使用new的方式
    2. 反射方式的使用场景:需要动态的获取类的构造器、属性或方法
  9. 反射机制和与面向对象中的封装性是否矛盾?如何看待?

    1. 反射机制和面向对象中的封装性并不矛盾,封装性是开发过程中,通过权限修饰符限定构造器、属性、方法的调用实现它们的封装,这些封装好的部分告诉开发者能不调用就不调用这些私有的结构,而是直接使用公开的结构
    2. 反射机制是能够解决因为封装而获取不到私有结构的问题,当程序运行时,想要动态获取这些结构时就需要反射来对这些结构进行操作
2.理解Class类并获取Class实例
  1. 类的加载过程:

    1. 程序经过javac.exe命令的编译后,会生成一个或多个字节码文件(.class文件)
    2. 使用java.exe命令对某个字节码文件进行解释运行。相当于将某额字节码文件加载到内存中。这个过程称为类的加载
    3. 加载到内存中的类称为运行时类,而这个运行时类就作为Class的一个实例
  2. 获取Class实例的方式

    1. 调用运行时类的属性".class"
    2. 通过运行时类的对象,调用“getClass()”
    3. 通过调用Class的静态方法"forName(String classPath)"
    4. 使用类的加载器:ClassLoader
    import org.junit.Test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 8:16
     * @Description:
     */
    public class ReflectionTest {
        @Test
        /** 获取Class实例的方式
         * 加载到内存中的运行时类会缓存一定的时间,在此时间之内,可以通过不同的方式获取此运行时类
         * */
        public void test3() throws ClassNotFoundException {
            //方式一:调用运行时类的属性.class
            Class clazz1 = Person.class;
            System.out.println(clazz1);
            //方式二:通过运行时类的对象,调用“getClass()”
            Person p1 = new Person();
            Class clazz2 = p1.getClass();
            System.out.println(clazz2);
            //方式三:通过Class的静态方法:“forName(String classPath)”
            Class clazz3 = Class.forName("Person");
            System.out.println(clazz3);
    
            //方式四:使用类的加载器:ClassLoader
            ClassLoader classLoader = ReflectionTest.class.getClassLoader();
            Class clazz4 = classLoader.loadClass("Person");
            System.out.println(clazz4);
    
    
            System.out.println(clazz1 == clazz2);//true
            System.out.println(clazz1 == clazz3);//true
            System.out.println(clazz1 == clazz4);//true
        }
    }
    
  3. 哪些类型可以有Class对象:

    1. 类:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

      Class c1 = Object.class;
      Class c9 = Class.class;
      
    2. interface:接口

      Class c2 = Comparable.class;
      
    3. []:数组

      Class c3 = String[].class;
      Class c4 = int[][].class;
      
      int[] a = new int[10];
      int[] b = new int[100];
      Class c10 = a.getClass();
      Class c11 = b.getClass();
      //只要元素类型与维度一样,就是同一个Class
      System.out.println(c10 == c11);//true
      
    4. enum:枚举

      Class c5 = ElementType.class;
      
    5. annotation:注解@interface

      Class c6 = Override.class;
      
    6. primitive type:基本数据类型

      Class c7 = int.class;
      
    7. void

      Class c8 = void.class;
      
3.类的加载与ClassLoader的理解
  1. 当程序主动使用某个类时,如果该类还未被加载到内存总,则系统会通过如下三个步骤对该类进行初始化

    image-20210330093013317

    1. 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程只需要类加载器参与

    2. 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程

      1. 验证:确保加载的类信息符合JVM规范,例如以cafe开头,没有安全方面的问题
      2. 准备:正式为变量(static)分配内存并设置变量默认初始值的阶段,这些内存都将在方法区进行分配
      3. 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用的过程
    3. 初始化:

      1. 执行**类构造器<clinit>()**方法的过程,类构造器<clinit>()是由编译期自动手机类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造器类信息的,不是构造该类对象的构造器)
      2. 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
      3. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步
      public class ClassLoaderTest{
          public static void main(String[] args){
              System.out.printl(A.m);
          }
      }
      
      class A{
          static{
              m = 300;
          }
          static int m = 100;
      }
      //第一步:加载
      //第二步:链接结束后m = 0;
      //第三步:初始化后,m的值由<clinit>()方法执行决定这个A类的构造器<clinit>()方法由类变量的赋值和静态代码块中语句按照顺序合并产生
      //类似于
      //<clinit>(){
      

// m=300;
// m=100;
//}
```

  1. 类加载器的作用:

    1. 图解

      image-20210330095601039

    2. 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口

    3. 类缓存:标准的JavaSE类加载器可以按要求查找类,但是一旦某个类被加载到类的加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

  2. ClassLoader:

    image-20210330100133320

    import org.junit.Test;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 10:41
     * @Description:
     */
    public class ClassLoaderTest {
        @Test
        public void test1(){
            //对于自定义类,使用系统类加载器进行加载
            ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
            System.out.println(classLoader);
    
            //调用系统类加载的getParent():获取扩展类的加载器
            ClassLoader classLoader1 = classLoader.getParent();
            System.out.println(classLoader1);
    
            //调用扩展类加载器的getParent():无法获取引导类加载器
            //引导类加载器主要负责加载java的核心类库,无法加载自定义类
            ClassLoader classLoader2 = classLoader1.getParent();
            System.out.println(classLoader2);
        }
    }
    

    image-20210330104704993

    import org.junit.Test;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.Properties;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 10:41
     * @Description:
     */
    public class ClassLoaderTest {
        @Test
        /** Properties: 用于读取配置文件 */
        public void test2(){
            Properties pros = null;
            try {
                pros = new Properties();
                //此时的文件默认再module下
                //读取配置文件的方式一:
    //            FileInputStream fis = new FileInputStream("jdbc.properties");
                FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
                pros.load(fis);
    
                //读取配置文件的方式二:使用ClassLoader
                //配置文件默认识别为:当前module的src下
    //            ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
    //            InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
    //            pros.load(is);
    
                String user = pros.getProperty("user");
                String pwd = pros.getProperty("password");
                System.out.println("user = " + user + ", password = " + pwd);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
4.创建运行时类的对象
import org.junit.Test;

import java.util.Random;

/**
 * @Author: xuehai.XUE
 * @MailBox: xuehai.xue@qq.com
 * @Date: 2021/3/30 11:20
 * @Description: 通过反射创建对应的运行时类的对象
 */
public class NewInstanceTest {
    @Test
    public void test1() throws IllegalAccessException, InstantiationException {
        Class clazz = Person.class;
        Person p1 = (Person)clazz.newInstance();
        System.out.println(p1);
    }
    @Test
    public void test2() throws IllegalAccessException, InstantiationException {
        //通过泛型避免强转
        Class<Person> clazz = Person.class;

        /*
        * newInstance(): 调用此方法,创建对应的运行时类的对象
        * 再newInstance内部调用了运行时类Person的空参构造器
        *
        * 要想newInstance()正常的创建运行时类的对象,要求满足:
        * 1. 运行时类必须要提供空参的构造器
        * 2. 空参的构造器的权限要足够大,通常直接使用public
        *
        * 再JavaBean中要求提供一个public带参的构造器,原因:
        * 1. 便于通过反射,创建运行时类的对象
        * 2. 便于子类类型继承此运行时类时,默认调用super()时,保证父类有构造器
        * */
        Person p1 = clazz.newInstance();
        System.out.println(p1);
    }

    @Test
    public void test3(){
        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;
            default:
        }
        try {
            Object obj = getInstance(classPath);
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } finally {
        }
    }

    /** 创建一个指定类的对象。classPath:指定全类名 */
    public Object getInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class clazz = Class.forName(classPath);
        return clazz.newInstance();
    }
}
5.获取运行时类的完整结构
  1. 带有各种常见结构的父类、子类、接口、注解

    父类Creature

    package structure;
    
    import java.io.Serializable;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 13:18
     * @Description: 带有各种结构的父类(Creature)
     */
    public class Creature<T> implements Serializable {
        private char gender;
        public double weight;
    
        private void breath(){
            System.out.println("生物呼吸");
        }
    
        public void eat(){
            System.out.println("生物进食");
        }
    }
    

    子类Person

    package structure;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 13:16
     * @Description: 带有各种权限属性、方法、构造器;继承于父类,实现了多个接口的演示类(Person)
     */
    @MyAnnotation(value = "class")
    public class Person extends Creature<String> implements Comparable<String>,MyInterface {
    
        @MyAnnotation(value = "Field")
        private String name;
        int age;
        public int id;
    
        public Person() {
        }
    
        @MyAnnotation(value = "Constructor")
        public Person(String name, int age, int id) {
            this.name = name;
            this.age = age;
            this.id = id;
        }
    
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        private Person(String name) {
            this.name = name;
        }
    
        @MyAnnotation(value = "private_method")
        private String show(String nation){
            System.out.println("国籍是:" + nation);
            return nation;
        }
    
        public String display(String instersts){
            return instersts;
        }
    
        @Override
        public int compareTo(String o) {
            return 0;
        }
    
        @Override
        public void info() {
            System.out.println("一个人");
        }
    }
    

    接口MyInterface

    package structure;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 13:20
     * @Description: 自定义接口
     */
    public interface MyInterface {
        void info();
    }
    

    注解MyAnnotation

    package structure;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import static java.lang.annotation.ElementType.*;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 13:26
     * @Description: 自定义注解演示
     */
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        String value() default "hello";
    }
    
  2. 获取当前运行时类的属性结构:FieldTest.java类中实现单元测试方法

    1. 获取属性结构:

      1. getFields():获取当前运行时类及其父类中声明为public访问权限的属性

      2. getDeclaredFields():获取当前运行时类中声明的所有的属性。(不包含父类中声明的属性)

        @Test
        public void test1(){
            Class clazz = Person.class;
            //获取属性结构
            //获取当前运行时类及其父类中声明为public访问权限的属性
            Field[] fields =clazz.getFields();
            for(Field f : fields){
                System.out.println(f);
            }
        
            //获取当前运行时类中声明的所有属性,不包括父类中声明的属性
            Field[] declaredFields = clazz.getDeclaredFields();
            for(Field f : declaredFields){
                System.out.println(f);
            }
        }
        
    2. 获取属性单独的权限修饰符、数据类型、变量名

      1. 权限修饰符 数据类型 变量名;

      2. 权限修饰符:getModifiers()

        1. 在Modifier类中定义了几个常量用于表示权限修饰符的权限大小

          image-20210330142931086

      3. 数据类型:getType()

      4. 变量名:getName()

        @Test
        public void test2(){
            Class clazz = Person.class;
            Field[] declaredFields = clazz.getDeclaredFields();
            for(Field f : declaredFields){
                //1. 权限修饰符
                int modifier = f.getModifiers();
                System.out.println(Modifier.toString(modifier));
        
                //2. 数据类型
                Class type = f.getType();
                System.out.println(type.getName() + "\t");
        
                //3. 变量名
                String fName = f.getName();
                System.out.println(fName);
        
                System.out.println();
        
            }
        }
        
  3. 获取当前运行时类的方法结构:MethodTest.java类中实现单元测试方法

    1. 获取方法结构:

      1. getMethods():获取当前运行时类及其父类中声明为public访问权限的方法

      2. getDeclaredMethods():获取当前运行时类中声明的所有的方法。(不包含父类中声明的方法)

        @Test
        public void test1(){
            Class clazz = Person.class;
        
            //获取方法结构
            //获取当前运行时类及其父类中声明为public访问权限的方法
            Method[] methods = clazz.getMethods();
            for(Method m : methods){
                System.out.print(m);
            }
        
            System.out.println();
        
            //获取当前运行时类中声明的所有方法,不包括父类中声明的方法
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for(Method m : declaredMethods){
                System.out.println(m);
            }
        }
        
    2. 获取方法单独的结构

      1. @Xxxx

        权限修饰符 返回值类型 方法名(参数类型1 形参名1, …) throws XxxException{}

      2. getAnnotations():获取方法声明的注解

      3. getModifiers():获取方法的权限修饰符

      4. getReturnType():获取方法的返回值类型

      5. getName():获取方法的方法名

      6. getParameterTypes():获取方法的形参列表(Class数组),通过遍历数组调用形参类型(.getName()),形参名(i)

      7. getExceptionTypes():获取方法的异常列表(Class数组),通过遍历数组调用异常类型类型(.getName()),异常名称(i)

        @Test
        public void test2(){
            Class clazz = Person.class;
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for(Method m : declaredMethods){
                //1. getAnnotations():获取方法声明的注解
                Annotation[] annos = m.getAnnotations();
                for (Annotation a : annos){
                    System.out.print(a);
                }
                //2. getModifiers():获取方法的权限修饰符
                System.out.print(Modifier.toString(m.getModifiers()) + "\t");
        
                //3. getReturnType():获取方法的返回值类型
                System.out.print(m.getReturnType().getName() + "\t");
        
                //4. getName():获取方法的方法名
                System.out.print(m.getName());
                System.out.print("(");
        
                //5. getParameterTypes():获取方法的形参列表(Class数组),通过遍历数组调用形参类型(.getName()),形参名(i)
                Class[] parameterTypes = m.getParameterTypes();
                if(!(parameterTypes == null && parameterTypes.length == 0)){
                    for(int i = 0;i < parameterTypes.length;i++){
                        if(i == parameterTypes.length - 1){
                            System.out.print(parameterTypes[i].getName() + "args_" + i);
                            break;
                        }
                        System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
                    }
                }
                System.out.print(")");
        
                System.out.println();
        
                //6. getExceptionTypes():获取方法的异常列表(Class数组),通过遍历数组调用异常类型类型(.getName()),异常名称(i)
                Class[] exceptionTypes = m.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].getName() + "args_" + i);
                            break;
                        }
                        System.out.print(exceptionTypes[i].getName() + "args_" + i + ",");
                    }
                }
        
        
                System.out.println();
            }
        }
        
  4. 获取当前运行时类的其他结构

    1. 获取构造器结构:

      1. getConstructors():获取当前运行时类中声明为public的构造器

      2. getDeclaredConstructors():获取当前运行时类中声明的所有构造器

        @Test
        /** 获取构造器结构 */
        public void test1(){
            Class clazz = Person.class;
            Constructor[] constructors = clazz.getConstructors();
            for(Constructor c : constructors){
                System.out.println(c);
            }
        
            System.out.println();
            
            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
            for(Constructor c : declaredConstructors){
                System.out.println(c);
            }
        }
        
    2. 获取运行时的父类

      1. getSuperClass()

        @Test
        /** 获取运行时类的父类 */
        public void test2(){
            Class clazz = Person.class;
        
            Class superClass = clazz.getSuperclass();
            System.out.println(superClass);
        }
        
    3. 获取运行时带泛型的父类

      1. getGenericSuperClass()

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

      1. getActualTypeArguments().getTypeName()

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

      1. getInterfaces()

      2. getSuperClass().getInterfaces():获取运行时父类实现的接口

        @Test
        /** 获取运行时类的接口 */
        public void test5(){
            Class clazz = Person.class;
            
            Class[] interfaces = clazz.getInterfaces();
            for(Class c : interfaces){
                System.out.println(c);
            }
        
            System.out.println();
            
            //获取父类的接口
            Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
            for(Class c : interfaces1){
                System.out.println(c);
            }
        }
        
    6. 获取运行时类所在的包

      1. getPackage()

        @Test
        /** 获取运行时类所在的包 */
        public void test6(){
            Class clazz = Person.class;
            Package pack = clazz.getPackage();
            System.out.println(pack);
        }
        
    7. 获取运行时类声明的注解

      1. getAnnotations()

        @Test
        /** 获取运行时所在类的注解 */
        public void test7(){
            Class clazz = Person.class;
        
            Annotation[] annos = clazz.getAnnotations();
            for(Annotation a : annos){
                System.out.println(a);
            }
        }
        
6.调用运行时类的指定结构(属性、方法、构造器)ReflectionTest.java中使用单元测试方法
  1. 创建运行时类的对象:newInstance()

  2. 获取指定的属性:getField(),只能获取运行时类中声明为public的属性,通常不采用此方法

  3. 设置当属性的值:

    1. set():参数1,指明设置哪个对象的属性;参数2,将此属性值设置为参数2
  4. 获取当前属性的值:

    1. get():参数1:获取哪个对象当前的属性值
    @Test
    public void testField() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class clazz = Person.class;
    
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
    
        //获取指定的属性
        Field id = clazz.getField("id");
    
        //设置当前属性的值
        id.set(p,1001);
    
        //获取当前属性的值
        int pId = (int)id.get(p);
        System.out.println(pId);
    }
    
  5. 操作运行时类中指定的属性

    @Test
    /** 操作运行时类中指定的属性 */
    public void testField1() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class clazz = Person.class;
    
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
    
        //1. 获取运行时类中指定变量名的属性
        Field name = clazz.getDeclaredField("name");
    
        //2. 保证当前属性是可访问的
        name.setAccessible(true);
    
        //3. 获取、设置指定对象的属性值
        name.set(p,"Biggy");
    
        System.out.println(name.get(p));
    
        //4. 如何调用运行时类的静态属性
        Field house = clazz.getDeclaredField("house");
        house.setAccessible(true);
        house.set(house,true);
        System.out.println(house.get(p));
    }
    
  6. 操作运行时类中指定的方法

    @Test
    /** 操作运行时类中的指定方法 */
    public void testMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Person.class;
    
        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();
    
        //1. 获取运行时类中指定方法名的方法
        Method show = clazz.getDeclaredMethod("show", String.class);
    
        //2. 保证当前方法是可访问的
        show.setAccessible(true);
    
        //3. 获取、设置指定对象的属性值
        //调用方法invoke():参数一:方法的调用者,参数二:给方法的形参赋值的实参
        //invoke()的返回值即为对应类中调用方法的返回值
        Object returnValue = show.invoke(p,"CHN");
        //类似于String nation = p.show("CHN");
        System.out.println(returnValue);
    
        //4.如何调用运行时类的静态方法
        //private static void showDesc()
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        showDesc.invoke(Person.class);
        Object returnValue1 = showDesc.invoke(Person.class);
        //可以使用null直接代替Person.class
        //因为static的方法在对空间中是共享的,不需要运行时对象即可调用
        //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
        System.out.println(returnValue1);//null
    }
    
  7. 操作运行时类中指定的构造器

    @Test
    /** 调用运行时类中指定的构造器 */
    public void testConstructor() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = Person.class;
    
        //private Person(String name){}
        //1. 获取指定的构造器
        //getDeclaredConstructor参数说明:指明构造器的形参列表
        Constructor constructor = clazz.getDeclaredConstructor(String.class);
    
        //2. 保证此构造器是可访问的
        constructor.setAccessible(true);
    
        //3. 调用此构造器创建运行时类的对象
        Person per = (Person)constructor.newInstance("biggy");
        System.out.println(per);
    }
    
7.动态代理
  1. 代理模式的设计原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上

  2. 静态代理的特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时每一代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能

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

  4. 动态代理使用场景:调试;远程方法调用

  5. 动态代理相较于静态代理的优点:抽象角色(接口)中声明的所有方法都会被转移到调用处理器一个集中的方法中处理,这样我们可以更加灵活和统一的处理众多的方法

  6. 静态代理举例:实现Runnable接口的方法创建多线程

    1. class MyThread implements Runnable{}//相当于被代理类
      class Thread implements Runnable{}//相当于代理类
      psvm{
      	MyThread t = new MyThread();
          Thread thread = new Thread(t);
          thread.start();//启动线程调用线程的run()
      }
      
  7. 静态代理

    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 18:11
     * @Description: 静态代理举例
     * 特点:代理类和被代理类在编译期间就被确定下来了
     */
    interface ClothFactory{
        void produceCloth();
    }
    
    //代理类
    class ProxyClothFactory implements ClothFactory{
    
        //用被代理类的对象进行实例化
        private ClothFactory factory;
    
        public ProxyClothFactory(ClothFactory factory){
            this.factory = factory;
        }
    
        @Override
        public void produceCloth() {
            System.out.println("代理工厂接收到订单");
    
            factory.produceCloth();
    
            System.out.println("代理工厂收取代理加工费");
        }
    }
    
    //被代理类
    class LiNingClothFactory implements ClothFactory{
    
        @Override
        public void produceCloth() {
            System.out.println("LINING工厂生产一批运动装备");
        }
    }
    
    public class StaticProxyTest {
        public static void main(String[] args) {
            //1. 创建被代理类的对象
            LiNingClothFactory lining = new LiNingClothFactory();
            //2. 创建被代理类的对象
            ProxyClothFactory proxyClothFactory = new ProxyClothFactory(lining);
    
            proxyClothFactory.produceCloth();
        }
    }
    

    动态代理

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/30 18:20
     * @Description: 动态代理举例
     */
    interface Human{
        String getBelief();
    
        void eat(String food);
    }
    
    //被代理类
    class SuperMan implements Human{
    
        @Override
        public String getBelief() {
            return "Marxist";
        }
    
        @Override
        public void eat(String food) {
            System.out.println(food);
        }
    }
    
    /** 要想实现动态代理需要解决的问题:
     * 问题1:如何根据加载到内存中的被代理类动态的创建一个代理类及其对象
     * 问题2:通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
     */
    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;
        }
    
        @Override
        /** 当通过代理类的对象,调用方法a时,就会自动的调用如下的方法,invoke()*/
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
            //obj:被代理类的对象
            Object returnValue = method.invoke(obj,args);
            //上述方法的返回值就作为当前类中的invoke()的返回值
            return returnValue;
        }
    }
    
    public class ProxyTest {
        public static void main(String[] args) {
            SuperMan superMan = new SuperMan();
            //proxyInstance:被代理的对象
            Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
            //当通过代理类的对象调用方法时,会自动调用被代理类中同名的方法
            System.out.println(proxyInstance.getBelief());
            proxyInstance.eat("apple");
    
            System.out.println("*************");
    
            LiNingClothFactory liNingClothFactory = new LiNingClothFactory();
    
            ClothFactory proxyClothFactory = (ClothFactory)ProxyFactory.getProxyInstance(liNingClothFactory);
    
            proxyClothFactory.produceCloth();
        }
    }
    
  8. 动态代理与AOP(Aspect Orient Programming)

    1. 不同代码段中存在的相同代码可以考虑封装进方法当中,但是这样做会导致这几个代码段会与一个特定的方法耦合,最理想的效果是:代码段1、2、3既可以执行方法A又无需在程序中以硬编码的方式直接调用已经封装好的方法

      image-20210330185118259

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      /**
       * @Author: xuehai.XUE
       * @MailBox: xuehai.xue@qq.com
       * @Date: 2021/3/30 18:20
       * @Description: 动态代理举例
       */
      interface Human{
          String getBelief();
      
          void eat(String food);
      }
      
      class HumanUtil{
          public void method1(){
              System.out.println("-------通用方法一-------");
          }
          public void method2(){
              System.out.println("-------通用方法二-------");
          }
      }
      
      //被代理类
      class SuperMan implements Human{
      
          @Override
          public String getBelief() {
              return "Marxist";
          }
      
          @Override
          public void eat(String food) {
              System.out.println(food);
          }
      }
      
      /** 要想实现动态代理需要解决的问题:
       * 问题1:如何根据加载到内存中的被代理类动态的创建一个代理类及其对象
       * 问题2:通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
       */
      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;
          }
      
          @Override
          /** 当通过代理类的对象,调用方法a时,就会自动的调用如下的方法,invoke()*/
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      
              HumanUtil util = new HumanUtil();
              util.method1();
      
              //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
              //obj:被代理类的对象
              Object returnValue = method.invoke(obj,args);
              //上述方法的返回值就作为当前类中的invoke()的返回值
      
      
              util.method2();
      
      
              return returnValue;
          }
      }
      
      public class ProxyTest {
          public static void main(String[] args) {
              SuperMan superMan = new SuperMan();
              //proxyInstance:被代理的对象
              Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
              //当通过代理类的对象调用方法时,会自动调用被代理类中同名的方法
              System.out.println(proxyInstance.getBelief());
              proxyInstance.eat("apple");
      
              System.out.println("*************");
      
              LiNingClothFactory liNingClothFactory = new LiNingClothFactory();
      
              ClothFactory proxyClothFactory = (ClothFactory)ProxyFactory.getProxyInstance(liNingClothFactory);
      
              proxyClothFactory.produceCloth();
          }
      }
      
8.创建类的对象的方式
  1. 方式1:new+构造器
  2. 方式2:要创建Xxx类的对象,可以考虑Xxx、Xxxs、XxxFactory、XxxBuilder类中查看是否有静态方法的存在。可以调用其静态方法,创建Xxx对象

image-20210303180846928

STAY ANGER!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值