63_反射机制

反射机制

基本概念

  • 通常情况下编写代码都是固定的,无论运行多少次执行的结果也是固定的,在某些特殊场合中编写 代码时不确定要创建什么类型的对象,也不确定要调用什么样的方法,这些都希望通过运行时传递 的参数来决定,该机制叫做动态编程技术,也就是反射机制

  • 通俗来说,反射机制就是用于动态创建对象并且动态调用方法的机制

  • 反射机制就是在运行阶段才知道要创建什么类型的对象、调用什么方法的机制。也就是动态编程的机制。

  • 要想实现动态编程,就得写通用的代码。

  • 目前主流的框架底层都是采用反射机制实现的

  • 如:

Person p = new Person(); - 表示声明Person类型的引用指向Person类型的对象 ,也就是创建Person类型的对象

p.show(); - 表示调用Person类中的成员方法show()

Class类

  1. 基本概念

    • java.lang.Class类的实例(也就是对象)可以用于描述Java应用程序中的类和接口,也就是一种数据类型。 以前Java官方的类也好,我们自己写的类也好,本质上就是在堆区内存空间中申请了一块内存区域而已。Class类比较独特,它的实例不再单纯是指堆区中的一块内存区域了,它有一个特殊的含义:是代表一种数据类型了。
    • 该类没有公共构造方法(也就是说我们不能自己new对象了),该类的实例由Java虚拟机和类加载器自动构造完成,不需要我们自己new、自己创建,本质上就是加载到内 存中的运行时类,以前的对象就是对象,但是这个类比较特殊,这个类的对象代表的是一个类,说明类也是一种比较特殊的对象。
    • 理解:以前写的.java文件和编译之后的.class字节码文件都是存储在硬盘中。只有当java xxx的时候才会把字节码文件加载到内存中,放到一个叫方法区的地方,就意味着这个类的信息、这个字节码信息加载到方法区里面之后,这整个类的信息、这个整体,我们就把它叫做Class的一个实例,这就是加载到方法区中正在运行时的这个类的信息。
    • Class类的实例跟我们之前讲的类不同之处在于:它不再是堆区中的一块内存空间了,而是加载到方法区时的正在运行的一个类。也就是说只要是一个类,加载到内存空间中就会有一个对应的类信息,不同的类就会有不同的类信息,而这些不同的类信息、不同的运行时的类、Class类,对应的就是Class类不同的对象
  2. 获取Class对象的方式(掌握:数据类型.class 和 Class.forName()两种获取Class对象的方式即可,总共有5种方式)

    • 使用数据类型.class的方式可以获取对应类型的Class对象(掌握)、Class实例、也就是运行时类信息。
    • 使用==引用/对象.getClass()==的方式可以获取对应类型的Class对象。
    • 使用包装类.TYPE(每一个包装类里都有一个叫TYPE的常量)的方式来获取对应基本数据类型的Class对象
    • 使用==Class.forName()==的方式来获取参数指定类型的Class对象(掌握)。
    • 使用类加载器ClassLoader的方式获取指定类型的Class对象
    • 特别注意:一个数据类型只有一个Class类对象,因为它是一个类信息运行时加载到方法区的一个代表。
  3. 常用的方法(掌握)

    方法声明功能介绍
    static Class<?> forName(String className)用于获取参数指定类型对应的Class对象并返回,要求类名写成完全限定类名,否则会发生找不到类异常。
    T newInstance()用于创建该Class对象所表示类的新实例,就是创建一个Class类对象所表示的类的对象/实例,这也就对应了动态编程(动态创建对象)

    forName方法的注意事项:不能获取基本数据类型的Class对象,而且引用数据类型的类名也要写完整的名称:包名.类名(完全限定名)

    注意:Idea中的目录结构是从项目路径开始的,File类使用相对路径时特别注意。

  4. 案例题目:测试获取Class对象的5种方式

    package com.lagou.module05.task05;
    
    public class ClassTest {
    
        public static void main(String[] args) throws ClassNotFoundException {
    
            // 1.使用数据类型.class的方式可以获取对应类型的Class对象
            Class c1 = String.class;
            // Class类中的toString方法获取的是class/interface + 一个空格 + 一个类的完全限定名(包名.类名)
            System.out.println("c1 = " + c1); // 自动调用toString方法  class java.lang.String
            c1 = int.class;
            // 如果是基本数据类型得到的Class对象就是基本数据名称
            System.out.println("c1 = " + c1); // int
            c1 = void.class;
            // 如果是void得到的Class对象就是void
            System.out.println("c1 = " + c1); // void
            // 如果此类对象表示数组类型,则方法返回"class",后跟getName
            int[] a = new int[0];
            System.out.println("数组类型的Class对象为:" + a.getClass()); // class [I
    
            System.out.println("---------------------------------------------------");
            // 2.使用对象.getClass()的方式获取对应的Class对象,讲Object类的时候介绍过这个方法
            // 这个方法来自Object类,Object又是所有类的父类,所以只要是个类对象都可以使用:对象/引用名.getClass();
            String str1 = new String("hello");
            c1 = str1.getClass();
            // 只要是同一个类型,获取到的Class对象都是一样的,因为Class对象代表的是一种数据类型
            System.out.println("c1 = " + c1); // class java.lang.String
    
            Integer it1 = 20;
            c1 = it1.getClass();
            System.out.println("c1 = " + c1); // class java.lang.Integer
    
            int num = 5;
            // 方法是属于引用数据类型里面的成员,他表示的是一种行为,而基本数据类型根本就不是类,所以根本就不能调用方法,或者说基本类型的变量num根本就不是对象,也不是引用
            // getClass()这种方法只能拿着对象/引用去调用
            //num.getClass(); Error: 基本数据类型的变量不能调用方法
    
            System.out.println("---------------------------------------------------");
            // 3.使用包装类.TYPE的方式来获取对应基本数据类型的Class对象
            // .TYPE是包装类所对应的基本数据类型的Class对象,而.Class是包装类自己的类对象
            c1 = Integer.TYPE;
            System.out.println("c1 = " + c1); // int
    
            c1 = Integer.class;
            System.out.println("c1 = " + c1); // class java.lang.Integer
    
            System.out.println("---------------------------------------------------");
            // 4.调用Class类中的forName方法来获取参数指定类型对应的Class对象
            //c1 = Class.forName("String"); // Error:类型找不到异常 要求写完整的名称:包名.类名
            // 导包的时候它lang(lang包下的类不用导入可以直接用),但是我们现在是要往内存空间中加载的,那个运行时信息,它再lang也lang不起来。
            // 因为它得占用内存空间,得在运行时,这个它要写完整。
            c1 = Class.forName("java.lang.String");
            System.out.println("c1 = " + c1); // class java.lang.String
    
            c1 = Class.forName("java.util.Date");
            System.out.println("c1 = " + c1); // class java.util.Date
    
            //c1 = Class.forName("int");
            //System.out.println("c1 = " + c1); // 不能获取基本数据类型的Class对象
            // 报类找不到异常,int压根就不是类找不到是应该的。
    
            System.out.println("---------------------------------------------------");
            // 5.使用类加载器的方式来获取Class对象
            // getClassLoader()方法是Class类的方法,得拿着Class类的一个引用对象去调用
            // 为了确保万无一失,我们直接使用当前类的Class对象来获取类加载器对象,因为这个代码只要一运行起来java ClassTest,这个字节码文件一定会加载到内存空间中,这个Class对象一定是存在的
            // 这个Class对象是存在的,我们就可以通过它来获取类加载器对象了,再拿着这个类加载器去获取其它类的Class对象就很完美了,因为刚才写String类的时候没找着
            ClassLoader classLoader = ClassTest.class.getClassLoader();
            // ClassLoader classLoader = String.class.getClassLoader(); // null
            // 通过String类压根就无法获取有效的类加载器,classLoader为null,它调用loadClass类加载方法的时候会报空指针异常
            System.out.println("classLoader = " + classLoader); // jdk.internal.loader.ClassLoaders$AppClassLoader@1f89ab83
            // 加载类返回一个Class对象
            c1 = classLoader.loadClass("java.lang.String");
            System.out.println("c1 = " + c1); // class java.lang.String
        }
    }
    
    

Constructor类

  1. 基本概念

    • java.lang.reflect.Constructor类主要用于描述获取到的构造方法信息
  2. Class类的常用方法

    方法声明功能介绍
    Constructor getConstructor(Class<?>… parameterTypes)用于获取此Class对象所表示类型中参数指定的 公共构造方法,参数是参数列表的参数类型(参数是Class类型的对象,无参构造就不传类型)
    Constructor<?>[] getConstructors()用于获取此Class对象所表示类型中所有的公共 构造方法
    Constructor<?>[] getDeclaredConstructors()用于获取此Class对象所表示类型中所有的 构造方法,构造方法一般是公有的。
  3. Constructor类的常用方法(newInstance()方法已经过时了,若还是想创建无参对象,可以使用该类)

    方法声明功能介绍
    T newInstance(Object… initargs)使用此Constructor对象描述的构造方法来构造Class对象代表类 型的新实例,参数是实参值(无参构造就不传参数)
    int getModifiers()获取方法的访问修饰符
    String getName()获取方法的名称
    Class<?>[] getParameterTypes()获取方法所有参数的类型
  4. 案例题目:编程实现动态编程之中的动态创建对象,分为有参方式和无参方式,总而言之,无参方式是先使用forName()方法得到Class对象,再调用newInstance()得到对应类实例对象(过时了)。有参方式是先得到Class对象之后调用获得构造方法,然后再构造对象。

    相比原始方式,虽然使用反射机制的代码更复杂,但是有优点:我们实现了动态编程,编译时不创建对象,运行时再去创建对象。而且只看代码也可以看出我们编译阶段压根就不知道要创建什么类型的变量,运行时阶段再根据我们从控制台输入,或者是读取配置文件的方式来创建对象。以前是拿着构造方法去构造对象,现在是获取构造方法去构造对象。一切都是通过类型为出发点,一步一步的得到的。

    package com.lagou.module05.task05;
    
    import java.io.BufferedReader;
    import java.io.FileInputStream;
    import java.io.InputStreamReader;
    import java.lang.reflect.Constructor;
    import java.util.Scanner;
    
    // 此处讲的是动态编程中的动态创建对象
    public class PersonConstructorTest {
    
        public static void main(String[] args) throws Exception {
    
            // 1.使用原始方式以无参形式构造Person类型的对象并打印
            Person p1 = new Person();
            System.out.println("无参方式创建的对象是:" + p1); // null 0 都是默认值
    
            System.out.println("---------------------------------------------------");
            // 2.使用反射机制以无参形式构造Person类型的对象并打印(2种方式)
            // 创建对象的类型可以从键盘输入  验证了在运行时再去动态的创建对象
            //System.out.println("请输入要创建对象的类型:");
            //Scanner sc = new Scanner(System.in);
            //String str1 = sc.next();
            //Class c1 = Class.forName("com.lagou.task20.Person");
            // 创建对象的类型可以从配置文件中读取 开发中我们不再使用上边的让用户从控制台输入的方式(很low),而是采用读取配置文件的方式,比如下面这种。
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/a.txt")));
            // 也可以循环读,毕竟里面写了很多行
            String str1 = br.readLine();
            Class c1 = Class.forName(str1);
            // 代码比原始方式复杂一些,但是有它的优势:动态的创建对象,再编译阶段可能不确定创建什么类型的对象,到运行时阶段才确定创建什么类型的对象
            //System.out.println("无参方式创建的对象是:" + c1.newInstance()); // null 0 过时了
            // 获取Class对象对应类中的无参构造方法,也就是Person类中的无参构造方法
            Constructor constructor = c1.getConstructor();
            // 使用获取到的无参构造方法来构造对应类型的对象,也就是Person类型的对象
            // 也就是无参方式构造Person类型对象的一个优化
            System.out.println("无参方式创建的对象是:" + constructor.newInstance());
            //sc.close();
            br.close();
    
            System.out.println("---------------------------------------------------");
            // 3.使用原始方式以有参方式构造Person类型的对象并打印
            Person p2 = new Person("zhangfei", 30);
            System.out.println("有参方式构造的对象是:" + p2); // zhangfei 30
    
            System.out.println("---------------------------------------------------");
            // 4.使用反射机制以有参方式构造Person类型的对象并打印(取代原始方式)
            // 获取Class对象对应类中的有参构造方法,也就是Person类中的有参构造方法
            Constructor constructor1 = c1.getConstructor(String.class, int.class);
            // 使用获取到的有参构造方法来构造对应类型的对象,也就是Person类型的对象
            // newInstance方法中的  实参是用于给有参构造方法的形参进行初始化的  ,也就是给name和age进行初始化的,构造方法种需要传两个参数,一个String,一个int
            System.out.println("有参方式构造的对象是:" + constructor1.newInstance("zhangfei", 30)); // zhangfei 30
    
            System.out.println("---------------------------------------------------");
            // 5.使用反射机制获取Person类中  所有的公共构造方法  并打印,返回值是多个,所以返回值是一个数组
            // 通过下面这个可以获取构造方法的所有信息:访问修饰符、方法名、参数列表(类型)
            // 如果不知道这个类中有哪些构造方法我们可以怎么去得到呢?可以按照下面方式得到。
            Constructor[] constructors = c1.getConstructors();
            for (Constructor ct : constructors) {
                System.out.println("构造方法的访问修饰符是:" + ct.getModifiers());
                System.out.println("构造方法的方法名称是:" + ct.getName());
                Class[] parameterTypes = ct.getParameterTypes(); // 参数列表所有参数的数据类型
                System.out.print("构造方法的所有参数类型是:");
                for (Class cs : parameterTypes) {
                    System.out.print(cs + " ");
                }
                System.out.println();
                System.out.println("-------------------------------------------------");
            }
        }
    }
    
    
    d:/a.txt中的内容:
    	com.lagou.task20.Person
        java.lang.String
        ...
        后边还可以接着写,只要愿意写,写多少都可以。当然咯,正常的配置文件都是从properties为后缀
    
    package com.lagou.module05.task05;
    
    import java.io.IOException;
    
    public class Person {
        private String name;
        //public String name;
        private int age;
    
        public Person() {
        }
    
        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) throws IOException {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    Modifier and Type			Constant Field	Value
     public static final int		ABSTRACT	1024
     public static final int		FINAL		16
     public static final int		INTERFACE	512
     public static final int		NATIVE		256
     public static final int		PRIVATE		2
     public static final int		PROTECTED	4
     public static final int		PUBLIC		1
     public static final int		STATIC		8
     public static final int		STRICT		2048
     public static final int		SYNCHRONIZED 32
     public static final int		TRANSIENT	128
     public static final int		VOLATILE	64
        
    Modifier类中的static String toString(int mod) 可以返回描述指定修饰符中的访问修饰符标志的字符串,也就是说可以将使用 getModifiers()犯法获取到的整数值转换为对应的 权限修饰符字符串
        
    Class类中的getSimpleName()方法可以获得去掉包名的类名
    

Field类动态

  1. 基本概念

    • java.lang.reflect.Field类主要用于描述获取到的单个成员变量信息
    • 也就是说通过Class类除了能获取构造方法之外,还能获取成员变量。
  2. Class类的常用方法

    方法声明功能介绍
    Field getDeclaredField(String name)用于获取此Class对象所表示类中参数指定的单个成员变量 信息
    Field[] getDeclaredFields()用于获取此Class对象所表示类中所有成员变量信息
    Field getField(String name)用于获取此Class对象所表示类中参数指定的公共的单个成员变量 信息(参数是成员变量名)
    Field[] getFields()用于获取此Class对象所表示类中所有公共的成员变量信息
  3. Field类的常用方法

    方法声明功能介绍
    Object get(Object obj)获取参数对象obj中此Field对象所表示成员变量的数值,也就是说此时有一个Person对象p1,用field.get(p1);可以把当前成员变量对应的值获取出来
    void set(Object obj, Object value)将参数对象obj中此Field对象表示成员变量的数值修改为参数 value的数值,修改
    ==void setAccessible(boolean flag)==设置可访问的权限当实参传递true时,则反射对象在使用时应该取消 Java 语言访 问检查,也就是说我们可以在任意包中访问这个类中私有变量,也就是说取消掉对访问限定修饰符的检查。忽略私有的检查。也就是 “暴力反射”
    int getModifiers()获取成员变量的访问修饰符
    Class getType()获取成员变量的数据类型
    String getName()获取成员变量的名称
  4. 案例题目:通过Class类获取Filed中单个成员变量,和所有成员变量的实现。特别注意:setAcessible(true)暴力反射的使用

    package com.lagou.module05.task05;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    public class PersonFieldTest {
    
        public static void main(String[] args) throws Exception {
    
            // 1.使用原始方式来构造对象以及获取成员变量的数值并打印
            Person p1 = new Person("zhangfei", 30);
            //System.out.println("获取到的成员变量数值为:" + p1.name); // zhangfei
    
            System.out.println("-------------------------------------------------------");
            // 2.使用反射机制来构造对象以及获取成员变量的数值并打印
            // 2.1 获取Class对象
            Class c1 = Class.forName("com.lagou.module05.task05.Person");
            // 2.2 根据Class对象获取对应的有参构造方法
            Constructor constructor = c1.getConstructor(String.class, int.class);
            // 2.3 使用有参构造方法来得到Person类型的对象
            Object object = constructor.newInstance("zhangfei", 30);
            // 2.4 根据Class对象获取对应的成员变量信息
            Field field = c1.getDeclaredField("name");
    
            // 设置Java语言访问检查的取消  暴力反射 反射很复杂,除了动态性之外,还有暴力性。
            field.setAccessible(true);
    
            // 2.5 使用Person类型的对象来获取成员变量的数值并打印
            // 获取对象object中名字为field成员变量的数值,也就是成员变量name的数值
            System.out.println("获取到的成员变量数值为:" + field.get(object)); // zhangfei
    
            System.out.println("-------------------------------------------------------");
            // 3.使用原始方式修改指定对象中成员变量的数值后再次打印
            //p1.name = "guanyu";
            //System.out.println("修改后成员变量的数值为:" + p1.name); // guanyu
    
            System.out.println("-------------------------------------------------------");
            // 4.使用反射机制修改指定对象中成员变量的数值后再次打印
            // 表示修改对象object中名字为field成员变量的数值为guanyu,也就是成员变量name的数值为guanyu
            field.set(object, "guanyu");
            System.out.println("修改后成员变量的数值为:" + field.get(object)); // guanyu
    
            System.out.println("-------------------------------------------------------");
            // 5.获取Class对象对应类中所有的成员变量
            // 如果我们一开始不知道该类中有些什么样的成员变量,我们可以使用这种方式来获取成员变量
            Field[] declaredFields = c1.getDeclaredFields();
            for (Field ft : declaredFields) {
                System.out.println("获取到的访问修饰符为:" + ft.getModifiers());
                System.out.println("获取到的数据类型为:" + ft.getType());
                System.out.println("获取到的成员变量名称是:" + ft.getName());
                System.out.println("---------------------------------------------");
            }
        }
    }
    

Method类

  1. 基本概念

    • java.lang.reflect.Method类主要用于描述获取到的单个成员方法信息
  2. Class类的常用方法

    方法声明功能介绍
    Method getMethod(String name, Class… parameterTypes)用于获取该Class对象表示类中名字为name参数(参数类型)为 parameterTypes的指定公共成员方法
    Method[] getMethods()用于获取该Class对象表示类中所有公共成员方法,包括继承的方法
    Method[] getDeclaredMethods()用于获取该Class对象表示类中所有成员方法,包括公共,受保护,默认(包)访问和私有方法,但不包括继承的方法
    Method getDeclaredMethod(String name, Class<?>… parameterTypes)用于获取该Class对象表示类中名字为name参数为 parameterTypes的指定成员方法,不包括继承而来的方法
  3. Method类的常用方法

    方法声明功能介绍
    Object invoke(Object obj, Object… args)使用对象obj来调用此Method对象所表示的成员方法,实参传递args,obj是当前Class对象的实例对象
    int getModifiers()获取方法的访问修饰符
    Class getReturnType()获取方法的返回值类型
    String getName()获取方法的名称
    Class[] getParameterTypes()获取方法所有参数的类型
    Class[] getExceptionTypes()获取方法的异常信息

| setAccessible(boolean flag) | 暴力反射 |

  1. 案例题目:编程实现获取所有公共的成员方法和获取更加详细的信息。

    package com.lagou.module05.task05;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    public class PersonMethodTest {
    
        public static void main(String[] args) throws Exception {
    
            // 1.使用原始方式构造对象并调用方法打印结果
            Person p1 = new Person("zhangfei", 30);
            System.out.println("调用方法的返回值是:" + p1.getName()); // zhangfei
    
            System.out.println("------------------------------------------------------");
            // 2.使用反射机制构造对象并调用方法打印结果
            // 2.1 获取Class对象
            Class c1 = Class.forName("com.lagou.module05.task05.Person");
            // 2.2 根据Class对象来获取对应的有参构造方法
            Constructor constructor = c1.getConstructor(String.class, int.class);
            // 2.3 使用有参构造方法构造对象并记录
            Object object = constructor.newInstance("zhangfei", 30);
            // 2.4 根据Class对象来获取对应的成员方法
            // 相当于直接把getName这个方法拿出来了
            Method method = c1.getMethod("getName"); // 第一个参数是方法名,第二个参数是形式参数列表
            // 2.5 使用对象调用成员方法进行打印
            // 表示使用object对象调用method表示的方法,也就是调用getName方法来获取姓名
            // 第一个参数要的是一个当前方法所在类的实例对象,第二个参数是给方法传的实参,此处由于无形参,故而不用传实参
            System.out.println("调用方法的返回值是:" + method.invoke(object)); // zhangfei
    
            System.out.println("------------------------------------------------------");
            // 3.使用反射机制来获取类中的所有成员方法并打印
            // 开发中很少获取这些太过详细的信息,此处只是为了让大家感受一下反射的强大和魅力。
            Method[] methods = c1.getMethods(); // 可以获取到本类中和父类中所有继承下来的公共成员方法
            for (Method mt : methods) {
                System.out.println("成员方法的修饰符是:" + mt.getModifiers());
                System.out.println("成员方法的返回值类型是:" + mt.getReturnType());
                System.out.println("成员方法的名称是:" + mt.getName());
                System.out.println("成员方法形参列表的类型是:");
                Class<?>[] parameterTypes = mt.getParameterTypes();
                for (Class ct : parameterTypes) {
                    System.out.print(ct + " ");
                }
                System.out.println();
                System.out.println("成员方法的异常类型列表是:");
                Class<?>[] exceptionTypes = mt.getExceptionTypes();
                for (Class ct: exceptionTypes) {
                    System.out.print(ct + " ");
                }
                System.out.println();
                System.out.println("---------------------------------------------------");
            }
        }
    }
    
  2. 案例题目:通过反射实现对Person类代码:成员变量、构造方法、成员方法进行的一个复刻(不包含方法体)

    package com.lagou.module05.task05;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.Arrays;
    import java.util.Iterator;
    
    /**
     * @author hhc19
     * @date 2022/1/25 18:10
     * @description
     */
    public class PersonReflectTest {
    
        public static void main(String[] args) throws ClassNotFoundException {
    
            Class c = Class.forName("com.lagou.module05.task05.Person");
            System.out.println(c.getPackage() + ";\n");
    
            System.out.println(Modifier.toString(c.getModifiers()) + " " +
                    (c.isInterface()? "interface ": c.isAnnotation()? "annotation " : c.isEnum()? "enum ":"class ") +
                    c.getSimpleName() + " {\n");
    
            Field[] declaredFields = c.getDeclaredFields();
            for (Field field : declaredFields) {
                System.out.print("    " + Modifier.toString(field.getModifiers()) + " ");
                System.out.print(field.getType().getSimpleName()+ " "); // 返回Type对象,该对象表示此字段对象表示的字段声明类型
                System.out.print(field.getName() + ";");
                System.out.println();
            }
            System.out.println();
    
            Constructor[] constructors = c.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.print("    " + Modifier.toString(constructor.getModifiers()) + " " +
                        constructor.getName().substring(constructor.getName().lastIndexOf(".") + 1, constructor.getName().length()) + " (");
                Class[] pts = constructor.getParameterTypes();
                Iterator<Class> iterator = Arrays.asList(pts).iterator();
                while (iterator.hasNext()) {
                    System.out.print(iterator.next().getSimpleName());
                    if (iterator.hasNext()) {
                        System.out.print(", ");
                    }
                }
                System.out.println(") {...};");
            }
            System.out.println();
    
            // Method[] methods = c.getDeclaredMethods(); // 只能获取到本类中所有成员方法,不能获取到继承到的成员方法
            Method[] methods = c.getMethods(); // 能获取到本类和从父类中继承下来的所有公共的成员方法
            for (Method method : methods) {
                System.out.print("    " + Modifier.toString(method.getModifiers()) + " " +
                        method.getReturnType().getSimpleName() + " " + method.getName() +
                        " (");
                Class[] pts = method.getParameterTypes();
                Iterator<Class> iterator = Arrays.asList(pts).iterator();
                while (iterator.hasNext()) {
                    System.out.print(iterator.next().getSimpleName());
                    if (iterator.hasNext()) {
                        System.out.print(", ");
                    }
                }
                System.out.print(") ");
                Class<?>[] et = method.getExceptionTypes();
    
                if (0 != et.length) {
                    System.out.print("throws ");
                    Iterator<Class<?>> iterator1 = Arrays.asList(et).iterator();
                    while(iterator1.hasNext()) {
                        System.out.print(iterator1.next().getSimpleName());
                        if (iterator1.hasNext()) {
                            System.out.print(", ");
                        }
                    }
                }
                System.out.println(";");
            }
            System.out.println("}");
        }
    }
    
    

获取其它结构信息

方法声明功能介绍
Package getPackage()获取所在的包信息
Class getSuperclass()获取继承的父类信息
Class[] getInterfaces()获取实现的所有接口(可能实现多个接口,所以返回值是一个数组,此处获取的是不带泛型的接口信息)
Annotation[] getAnnotations()获取注解信息
Type[] getGenericInterfaces()获取泛型信息(带泛型的接口信息,实际上就是把接口及泛型信息一起获取出来),这个方法也就是获取实现的接口和其泛型信息

案例题目:编程实现对上述方法的使用。

package com.lagou.module05.task05;

import java.io.Serializable;

// 泛型、父类、包、接口、注解都有了
@MyAnnotation
public class Student<T, E> extends Person implements Comparable<String>, Serializable {

   /* @Override
    public int compareTo(Object o) {
        return 0;
    }*/

    // 为接口指定了泛型之后
    @Override
    public int compareTo(String o) {
        return 0;
    }
}

package com.lagou.module05.task05;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}

package com.lagou.module05.task05;

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

public class StudentTest {

    public static void main(String[] args) throws Exception {

        // 获取Student类型的Class对象
        Class c1 = Class.forName("com.lagou.module05.task05.Student");
        System.out.println("获取到的包信息是:" + c1.getPackage()); // 获取到的是一个Package类型的对象 package com.lagou.module05.task05
        System.out.println("获取到的包信息是:" + c1.getPackageName()); // 获取到的是一个String字符串 com.lagou.module05.task05
        System.out.println("获取到的父类信息是:" + c1.getSuperclass());

        System.out.println("-------------------------------------------------");
        System.out.println("获取到的接口信息是:");
        Class[] interfaces = c1.getInterfaces(); // interface java.lang.Comparable interface java.io.Serializable
        for (Class ct : interfaces) {
            System.out.print(ct + " ");
        }
        System.out.println();

        System.out.println("-------------------------------------------------");
        System.out.println("获取到的注解信息是:");
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation at : annotations) {
            System.out.print(at + " "); // @com.lagou.module05.task05.MyAnnotation()
        }
        System.out.println();

        System.out.println("-------------------------------------------------");
        System.out.println("获取到的泛型信息是:");
        Type[] genericInterfaces = c1.getGenericInterfaces(); // 实际上这个获取的是带泛型的接口信息(实现的接口)
        for (Type tt : genericInterfaces) {
            /**
             * public class Student<T, E> extends Person implements Comparable, Serializable 时
             * 此处只打印接口名,不打印泛型信息的根本原因:
             * Student类在实现接口的时候并没有指定泛型信息,所以我们此处获取到的打印结果没有任何泛型信息
             */
            System.out.print(tt + " "); // interface java.lang.Comparable interface java.io.Serializable
            // 为接口指定了泛型信息之后的输出结果:java.lang.Comparable<java.lang.String> interface java.io.Serializable
        }
        System.out.println();
    }
}

总结

反射机制这一块实际上是让我们熟悉和理解,为的是我们之后学的框架,底层全是用反射 + 注解 + 设计模式实现的,反射机制现在理解了的话,之后学框架也会理解的比较透彻。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值