• 17.Java的反射机制


    一、反射的定义与理解

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

    2. 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为“反射”。

    在这里插入图片描述

    1. 动态语言是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。主要的动态语言:Object-C,C#,JavaScript,PHP,Python等。

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

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

    二、Java反射机制研究和应用

    1. Java反射机制提供的功能
      在这里插入图片描述

    2. Java反射相关的主要API

    在这里插入图片描述
    代码演示

    package com.zzw;
    
    public class Person {
    
        private String name;
        public int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        private Person(String name) {
            this.name = name;
        }
    
        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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    package com.zzw;
    
    import org.junit.Test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class ReflectionTest {
    
        @Test
        public void test() throws Exception {
            Class clazz = Person.class;
            //1.通过反射,创建Person类的对象
            Constructor constructor = clazz.getConstructor(String.class, int.class);
            Object instance = constructor.newInstance("Tom", 12);
            Person p = (Person)instance;
            System.out.println(p.toString());
    
            //2.通过反射调用属性和方法
            //调用属性
            Field declaredField = clazz.getDeclaredField("age");
            declaredField.set(p,10);
            System.out.println(p.toString());
    
            //调用方法
            Method show = clazz.getDeclaredMethod("show");
            show.invoke(p);
    
            System.out.println("**********************");
    
            //通过反射不仅可以调用类的公有属性和方法,还可以调用类的私有构造器、属性和方法
            //调用私有的构造器
            Constructor constructor1 = clazz.getDeclaredConstructor(String.class);
            constructor1.setAccessible(true);
            Object object = constructor1.newInstance("Jerry");
            Person p1 = (Person)object;
            System.out.println(p1.toString());
    
            //调用私有的属性
            Field field = clazz.getDeclaredField("name");
            field.setAccessible(true);
            field.set(p1,"xiaoming");
            System.out.println(p1.toString());
    
            //调用私有的方法
            Method showNation = clazz.getDeclaredMethod("showNation", String.class);
            showNation.setAccessible(true);
            Object invoke = showNation.invoke(p1, "中国");
            String nation = (String)invoke;
            System.out.println(nation);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    关于java.lang.Class类的理解

    1.类的加载过程:
    程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对每个字节码文件进行解释运行。相当于将整个字节码文件加载到内存中,此过程称为类的加载。加载到内存中的类,称为运行时类,就可以作为Class类的一个实例。

    简而言之,Class的实例对应的就是对应着一个运行时类。

    三、理解Class类并获取Class的实例

    在这里插入图片描述

    对象照镜子后可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

    • Class本身也是一个类
    • Class对象只能由系统建立对象
    • 一个加载的类在JVM中只会有一个Class实例
    • 一个Class对象对应的是一个加载到JVM中的一个.class文件
    • 每个类的实例都会记得自己是由哪个Class实例所生成
    • 通过Class可以完整的得到一个类中所有被加载的结构
    • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

    在这里插入图片描述

    反射的应用举例可以参考上面的代码

    1. 加载到内存中的类会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类,下面介绍获取Class的实例的几种方式
    @Test
        public void test1() throws ClassNotFoundException {
            //方式一:调用运行时类的属性.class
            Class<Person> clazz1 = Person.class;
            System.out.println(clazz1);
    
            //方式二:通过创建运行时类的对象,然后通过对象调用getClass方法
            Person person = new Person();
            Class clazz2 = person.getClass();
            System.out.println(clazz2);
    
            //方式三:调用class的静态方法,forName(String classpath) (常用)
            Class clazz3 = Class.forName("com.zzw.Person");
            System.out.println(clazz3);
    
            //方式四:使用类的加载器(了解)
            ClassLoader classLoader = ReflectionTest.class.getClassLoader();
            Class clazz4 = classLoader.loadClass("com.zzw.Person");
            System.out.println(clazz4);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 那么哪些类型可以有Class对象呢?
    • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
    • interface:接口
    • 【】:数组
    • enum : 枚举
    • annotation:注解@interface
    • primitive type : 基本数据类型
    • void

    代码举例:

    Class c1 = Object.class;
    Class c2 = Comparable.class;
    Class c3 = String[].class;
    Class c4 = int[][].class;
    Class c5 = ElementType.class;
    Class c6 = Override.class;
    Class c7 = int.class;
    Class c8 = void.class;
    Class c9 = Class.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);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    四、了解类的加载过程

    当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化:

    在这里插入图片描述

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

    代码示例:

    public class ClassLoadingTest {
    	public static void main(String[] args) {
    		System.out.println(A.m);
    	} 
    }
    class A {
    	static { m = 300;
    	}
    	static int m = 100;
    }
    //第二步:链接结束后m=0
    //第三步:初始化后,m的值由()方法执行决定
    // 这个A的类构造器()方法由类变量的赋值和静态代码块中的语句按照顺序合并
    产生,类似于
    // (){
    // m = 300;
    // m = 100;
    // }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 了解什么时候会发生类初始化
      在这里插入图片描述

    2. 类加载器的作用
      在这里插入图片描述

    3. 了解ClassLoader
      在这里插入图片描述

    代码示例:

    //1.获取一个系统类加载器
    ClassLoader classloader = ClassLoader.getSystemClassLoader();
    System.out.println(classloader);
    //2.获取系统类加载器的父类加载器,即扩展类加载器
    classloader = classloader.getParent();
    System.out.println(classloader);
    //3.获取扩展类加载器的父类加载器,即引导类加载器
    classloader = classloader.getParent();
    System.out.println(classloader);//输出为null
    //4.测试当前类由哪个类加载器进行加载
    classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
    System.out.println(classloader);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 附加:加载配置文件的两种方式代码示例:
    package com.zzw;
    
    import org.junit.Test;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    
    public class ClassLoaderTest {
    
        @Test
        public void test3() throws IOException {
    
            Properties properties = new Properties();
            //加载配置文件的方式一
            //此时默认的properties文件在当前的module下
            FileInputStream fileInputStream = new FileInputStream("jdbc.properties");
            properties.load(fileInputStream);
            String user = properties.getProperty("User");
            String password = properties.getProperty("Password");
            System.out.println("User = " + user + ",Password = " + password);
    
            System.out.println("*****************");
    
            //加载配置文件方式二:使用类加载器的方式加载配置文件
            //此时默认的加载路径是当前src下
            ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
            InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc1.properties");
            properties.load(resourceAsStream);
            String user1 = properties.getProperty("User");
            String password1 = properties.getProperty("Password");
            System.out.println("User = " + user1 + ",Password = " + password1);
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    五、创建运行时类的对象

    1.代码示例:

    package com.zzw;
    
    public class Person {
    
        private String name;
        public int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        private Person(String name) {
            this.name = name;
        }
    
        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;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    package com.zzw;
    
    import org.junit.Test;
    
    public class NewInstaneTest {
    
        @Test
        public void test() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            //Class clazz = (Class) Class.forName("com.zzw.Person");
            Class<Person> clazz = Person.class;
            /*
            newInstance():调用此方法,创建对应的运行时类的对象。其实该方法内部调用了运行时类的空参构造器
    
            要想此方法能够正常的创建运行时类的对象,有以下两个要求:
            1.需要运行时类提供相应的空参构造器
            2.空参构造器的访问权限得足够使用,一般修饰为public
    
            在JavaBean中要求提供一个空参构造器,原因为:
            1.在使用反射机制创建运行时类的对象时需要用到空参构造器
            2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
    
             */
            Person person = clazz.newInstance();
            System.out.println(person);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    1. 通过代码的方式体会反射的动态性
    @Test
        public void test1() {
            int num = new Random().nextInt(3);
            String classPath = "";
            switch (num) {
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "com.zze.Person";
                    break;
            }
            try {
                Object instance = getInstance(classPath);
                System.out.println(instance);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    
        /*
        创建一个指定类的对象
        classPath:代表指定类的全类名
         */
        public Object getInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            Class clazz = Class.forName(classPath);
            return clazz.newInstance();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    六、通过反射获取运行时类的完整结构

    Field、Method、Constructor、Superclass、Interface、Annotation

    • 实现的全部接口
    • 所继承的父类
    • 全部的构造器
    • 全部的方法
    • 全部的Field

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    package com.atguigu.java2;
    
    import com.atguigu.java1.Person;
    import org.junit.Test;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    
    /**
     * 获取当前运行时类的属性结构
     *
     * @author shkstart
     * @create 2019 下午 3:23
     */
    public class FieldTest {
    
        @Test
        public void test1(){
    
            Class clazz = Person.class;
    
            //获取属性结构
            //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
            Field[] fields = clazz.getFields();
            for(Field f : fields){
                System.out.println(f);
            }
            System.out.println();
    
            //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
            Field[] declaredFields = clazz.getDeclaredFields();
            for(Field f : declaredFields){
                System.out.println(f);
            }
        }
    
        //权限修饰符  数据类型 变量名
        @Test
        public void test2(){
            Class clazz = Person.class;
            Field[] declaredFields = clazz.getDeclaredFields();
            for(Field f : declaredFields){
                //1.权限修饰符
                int modifier = f.getModifiers();
                System.out.print(Modifier.toString(modifier) + "\t");
    
                //2.数据类型
                Class type = f.getType();
                System.out.print(type.getName() + "\t");
    
                //3.变量名
                String fName = f.getName();
                System.out.print(fName);
    
                System.out.println();
            }
    
    
        }
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    package com.atguigu.java2;
    
    import com.atguigu.java1.Person;
    import org.junit.Test;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    
    /**
     * 获取运行时类的方法结构
     *
     * @author shkstart
     * @create 2019 下午 3:37
     */
    public class MethodTest {
    
        @Test
        public void test1(){
    
            Class clazz = Person.class;
    
            //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
            Method[] methods = clazz.getMethods();
            for(Method m : methods){
                System.out.println(m);
            }
            System.out.println();
            //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for(Method m : declaredMethods){
                System.out.println(m);
            }
        }
    
        /*
        @Xxxx
        权限修饰符  返回值类型  方法名(参数类型1 形参名1,...) throws XxxException{}
         */
        @Test
        public void test2(){
            Class clazz = Person.class;
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for(Method m : declaredMethods){
                //1.获取方法声明的注解
                Annotation[] annos = m.getAnnotations();
                for(Annotation a : annos){
                    System.out.println(a);
                }
    
                //2.权限修饰符
                System.out.print(Modifier.toString(m.getModifiers()) + "\t");
    
                //3.返回值类型
                System.out.print(m.getReturnType().getName() + "\t");
    
                //4.方法名
                System.out.print(m.getName());
                System.out.print("(");
                //5.形参列表
                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(")");
    
                //6.抛出的异常
                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());
                            break;
                        }
    
                        System.out.print(exceptionTypes[i].getName() + ",");
                    }
                }
    
    
                System.out.println();
            }
    
    
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    package com.atguigu.java2;
    
    import com.atguigu.java1.Person;
    import org.junit.Test;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    
    /**
     * @author shkstart
     * @create 2019 下午 4:19
     */
    public class OtherTest {
    
        /*
        获取构造器结构
    
         */
        @Test
        public void test1(){
    
            Class clazz = Person.class;
            //getConstructors():获取当前运行时类中声明为public的构造器
            Constructor[] constructors = clazz.getConstructors();
            for(Constructor c : constructors){
                System.out.println(c);
            }
    
            System.out.println();
            //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
            Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
            for(Constructor c : declaredConstructors){
                System.out.println(c);
            }
    
        }
    
        /*
        获取运行时类的父类
    
         */
        @Test
        public void test2(){
            Class clazz = Person.class;
    
            Class superclass = clazz.getSuperclass();
            System.out.println(superclass);
        }
    
        /*
        获取运行时类的带泛型的父类
    
         */
        @Test
        public void test3(){
            Class clazz = Person.class;
    
            Type genericSuperclass = clazz.getGenericSuperclass();
            System.out.println(genericSuperclass);
        }
    
        /*
        获取运行时类的带泛型的父类的泛型
    
    
        代码:逻辑性代码  vs 功能性代码
         */
        @Test
        public void test4(){
            Class clazz = Person.class;
    
            Type genericSuperclass = clazz.getGenericSuperclass();
            ParameterizedType paramType = (ParameterizedType) genericSuperclass;
            //获取泛型类型
            Type[] actualTypeArguments = paramType.getActualTypeArguments();
    //        System.out.println(actualTypeArguments[0].getTypeName());
            System.out.println(((Class)actualTypeArguments[0]).getName());
        }
    
        /*
        获取运行时类实现的接口
         */
        @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);
            }
    
        }
        /*
            获取运行时类所在的包
    
         */
        @Test
        public void test6(){
            Class clazz = Person.class;
    
            Package pack = clazz.getPackage();
            System.out.println(pack);
        }
    
        /*
            获取运行时类声明的注解
    
         */
        @Test
        public void test7(){
            Class clazz = Person.class;
    
            Annotation[] annotations = clazz.getAnnotations();
            for(Annotation annos : annotations){
                System.out.println(annos);
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129

    七、调用运行时类的指定结构

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    代码实现

    package com.atguigu.java2;
    
    import com.atguigu.java1.Person;
    import org.junit.Test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 调用运行时类中指定的结构:属性、方法、构造器
     *
     * @author shkstart
     * @create 2019 下午 4:46
     */
    public class ReflectionTest {
    
        /*
    
            不需要掌握
         */
        @Test
        public void testField() throws Exception {
            Class clazz = Person.class;
    
            //创建运行时类的对象
            Person p = (Person) clazz.newInstance();
    
    
            //获取指定的属性:要求运行时类中属性声明为public
            //通常不采用此方法
            Field id = clazz.getField("id");
    
            /*
            设置当前属性的值
    
            set():参数1:指明设置哪个对象的属性   参数2:将此属性值设置为多少
             */
    
            id.set(p,1001);
    
            /*
            获取当前属性的值
            get():参数1:获取哪个对象的当前属性值
             */
            int pId = (int) id.get(p);
            System.out.println(pId);
    
    
        }
        /*
        如何操作运行时类中的指定的属性 -- 需要掌握
         */
        @Test
        public void testField1() throws Exception {
            Class clazz = Person.class;
    
            //创建运行时类的对象
            Person p = (Person) 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 testMethod() throws Exception {
    
            Class clazz = Person.class;
    
            //创建运行时类的对象
            Person p = (Person) clazz.newInstance();
    
            /*
            1.获取指定的某个方法
            getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
             */
            Method show = clazz.getDeclaredMethod("show", String.class);
            //2.保证当前方法是可访问的
            show.setAccessible(true);
    
            /*
            3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
            invoke()的返回值即为对应类中调用的方法的返回值。
             */
            Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
            System.out.println(returnValue);
    
            System.out.println("*************如何调用静态方法*****************");
    
            // private static void showDesc()
    
            Method showDesc = clazz.getDeclaredMethod("showDesc");
            showDesc.setAccessible(true);
            //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
    //        Object returnVal = showDesc.invoke(null);
            Object returnVal = showDesc.invoke(Person.class);
            System.out.println(returnVal);//null
    
        }
    
        /*
        如何调用运行时类中的指定的构造器
         */
        @Test
        public void testConstructor() throws Exception {
            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("Tom");
            System.out.println(per);
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 关于setAccessible方法的使用

    • Method和Field、Constructor对象都有setAccessible()方法。

    • setAccessible启动和禁用访问安全检查的开关。

    • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。(提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。使得原本无法访问的私有成员也可以访问)

    • 参数值为false则指示反射的对象应该实施Java语言访问检查。

    八、反射的应用:动态代理

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

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

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    代码实现:

    package com.zzw.java;
    
    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 {
        //调用该方法,返回一个代理类的对象,解决问题一,参数obj表示被代理类的对象
        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()方法中
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //method为代理类对象要调用的方法,此方法也就作为了被代理类对象要调用的方法
            Object returnVal = method.invoke(obj, args);
            return returnVal;
        }
    }
    
    public class ProxyTest {
        public static void main(String[] args) {
            superMan superMan = new superMan();
            //创建代理类的对象
            Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
    
            //通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
            String belief = proxyInstance.getBelief();
            System.out.println(belief);
            proxyInstance.eat("海底捞火锅");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
  • 相关阅读:
    js——arguments的使用
    Consul API Gateway 0.4 已正式发布,包括这些新功能
    《稻盛和夫给年轻人的忠告》阅读笔记
    移动Web第一天 1 字体图标 && 2 平面转换 && 3 渐变
    Ardupilot Rpanion iperf网络性能测试
    AOP相关概念
    Abnova丨Abnova GPCR单克隆抗体解决方案
    程序员副业大揭秘:如何利用技术优势实现财富自由?
    【代码源每日一题Div1】好序列/BZOJ4059「启发式分治」
    躲避小行星游戏
  • 原文地址:https://blog.csdn.net/zhaozhiwei314/article/details/126170388