美文网首页
Java高级-反射

Java高级-反射

作者: ttiga | 来源:发表于2021-05-27 10:40 被阅读0次

    15.1.Java反射机制概述

    15.2.理解Class类并获取Class实例(重点)

    • 用反射实现类的实例化,调用等操作
    public class ReflectionTest {
        // 反射之前,对于Person类的操作
        @Test
        public void test1(){
            // 1.创建Person类的对象
            Person p1 = new Person("tom", 12);
            // 2.通过对象调用内部属性,方法
            p1.age = 10;
            System.out.println(p1.toString());
            p1.show();
            // 在Person类外部,不可以通过Person类的对象调用其内部私有结构.
            // 比如: name . showNation()以及私有的构造器
        }
        // 反射之后,对于Person类的操作
        // Person类在运行时候,把它称作运行时类,相当
        @Test
        public void test2() throws Exception {
            // 原来可以做到的
            // 1.通过反射,创建Person类的对象
            // Person类有个属性".class"作为Class的实例
            Class clazz = Person.class;
            // 获取clazz对象指定的构造器
            Constructor cons = clazz.getConstructor(String.class, int.class);
            // 通过构造器造Object类型对象
            Object obj = cons.newInstance("Tom", 12);// 本质上obj就是Person类型,多态形式
            Person p = (Person) obj;// 因此可以强转
            System.out.println(obj.toString());// 实际调用Person类重写的toString方法
            // 2.通过反射,调用对象指定的属性,方法
            // 获取到clazz对应的Person类里age属性
            // 调属性
            Field age = clazz.getDeclaredField("age");
            // 给p对象的age属性赋值为10
            age.set(p, 10);// 可以看成 p.setage
            System.out.println(p.toString());
            // 调方法,其中有可变形参为参数类型,没有也可不写
            Method show = clazz.getDeclaredMethod("show");// 空参的
            // 调用
            show.invoke(p);// 参数为实参
            System.out.println("=============================");
            //原来做不到的: 通过反射,可以调用Person类(运行时类)的私有结构的。比如:私有的构造器、方法、属性
            // 调用私有构造器
            // 创建String类型的构造器
            Constructor cons1 = clazz.getDeclaredConstructor(String.class);
            cons1.setAccessible(true); // 简单的说就是把private封装的变量和方法,强行解锁了,使之可以被访问,被调用
            // 通过构造器创建对象,p1就Person类的对象
            Person p1 = (Person) cons1.newInstance("Jerry");
            System.out.println(p1.toString());
            // 调用私有属性,属性名为name
            Field name = clazz.getDeclaredField("name");
            name.setAccessible(true);
            name.set(p1, "hanmeimei");// 直接通过对象操作属性
            System.out.println(p1.toString());
            // 调用私有方法
            Method showNation = clazz.getDeclaredMethod("showNation", String.class);
            showNation.setAccessible(true);
            // showNation是Method类型的对象,Java里永远都是对象调,只不过这个对象恰好是原来的方法,参数p1是原来的对象
            // 其实仍然是对象调属性,方法 invoke():默认返回Object类型
            String nation = (String) showNation.invoke(p1, "中国");// 相当于 String naton = p1.showNation("中国")
            System.out.println(nation.toString());
        }
    }
    
    • 反射强大之处: 用反射调用类的私有结构
    //原来做不到的: 通过反射,可以调用Person类(运行时类)的私有结构的。比如:私有的构造器、方法、属性
            // 调用私有构造器
            // 创建String类型的构造器
            Constructor cons1 = clazz.getDeclaredConstructor(String.class);
            cons1.setAccessible(true); // 简单的说就是把private封装的变量和方法,强行解锁了,使之可以被访问,被调用
            // 通过构造器创建对象,p1就Person类的对象
            Person p1 = (Person) cons1.newInstance("Jerry");
            System.out.println(p1.toString());
            // 调用私有属性,属性名为name
            Field name = clazz.getDeclaredField("name");
            name.setAccessible(true);
            name.set(p1, "hanmeimei");// 直接通过对象操作属性
            System.out.println(p1.toString());
            // 调用私有方法
            Method showNation = clazz.getDeclaredMethod("showNation", String.class);
            showNation.setAccessible(true);
            // showNation是Method类型的对象,Java里永远都是对象调,只不过这个对象恰好是原来的方法,参数p1是原来的对象
            // 其实仍然是对象调属性,方法 invoke():默认返回Object类型
            String nation = (String) showNation.invoke(p1, "中国");// 相当于 String naton = p1.showNation("中国")
            System.out.println(nation.toString());
    
    • 如何看待反射和封装性两个技术

    疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
    建议:直接new的方式。
    什么时候会使用:编译的时候不确定要new哪个类的对象,就用反射的方式。反射的特征:动态性.因为有不同类型的构造方法,而到运行的时候才能决定需要怎样的对象
    疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
    不矛盾; 封装性是有public的结构就不建议调private的结构;反射机制是可以调用private的结构,看具体需求

    • Class类的理解

    关于java.lang.Class类的理解
    1.类的加载过程:
    编译过程:程序经过javac.exe(编译)命令以后,会生成一个或多个字节码文件(.cLass结尾)。
    解释运行,加载的过程: 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,这个过程就称为类的加载。
    加载到内存中的类,称之为运行时类,此运行时类,就作为Class类的一个实例
    2.换句话说, Class的实例就对应着一个运行时类, 而且该运行时类一旦加载到内存以后,实际上会缓存一段时间,下面的只是通过不同方式获取了内存当中唯一的运行时类
    说想提供一个Class的实例而去new一个Class是错误的.Class实例就是用一个运行时类来进行赋值的
    3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
    万事万物皆对象? 对象.xxx,File,URL,反射,前端,数据库操作

    • Class实例的四种方式
    // 获取Class的实例方式: (前三种方式要掌握)
        @Test
        public void test3() throws ClassNotFoundException {
            // 方式一: 调用运行时类的属性: 这个属性叫做: .class   class是Class的属性,用class去描述Class
            // 编译时写死了,若类不存在,编译会报错
            // Class是对于类的通用描述,此时其实是Person给他赋的值,所以Class具体操作都是Person
            Class clazz1 = Person.class;// 可以加上泛型避免后面要强转
            System.out.println(clazz1);// 获取到Person类本身
            // 方式二: 通过运行时类的对象,调用getClass(); 先创建了类的对象不好用
            Person p1 = new Person();
            // 获取该对象是哪个类造的方法,任何一个对象都知道在哪个类造的,所以声明在Ojbect类中
            Class clazz2 = p1.getClass();// 返回Person类本身赋给另外一个clazz2
            System.out.println(clazz2);
            // (最常用)方式三: 调用Class的静态方法: forName(String classPath),参数是类的全路径
            // 最常用,因为通过反射能做的第一件事就是能创建对应的运行时类的对象,运行时才确定是否存在运行时类
            // 这种方式更好的体现了动态性,因为反射主要想体现的就是运行时的动态性,编译的时候先不去确定
            // 不管是什么类,这个类本身都作为Class的实例
            Class clazz3 = Class.forName("com.senior.Person");// 类的全类名(包含包的完整路径)
            System.out.println(clazz3);
            // 虽然获取类的方式不同,但时都是获取内存中同一个运行时类,因为用的同一个类加载器,类只加载一次
            System.out.println(clazz1 == clazz2);
            System.out.println(clazz1 == clazz3);
            // 方式四: 用类的加载器: ClassLoader
            // 调用测试类的getClassLoader方法,获得一个类加载器
            ClassLoader classLoader = ReflectionTest.class.getClassLoader();
            // 通过加载器显式的加载某个类
            Class clazz4 = classLoader.loadClass("com.senior.Person");
        }
    
    • Class实例对应的结构的说明
    // Class实例可以是哪些结构的说明: Class不仅可以表示为运行时类,还可以表示其他结构
        @Test
        public void test4(){
            Class c1 = Object.class;
            Class c2 = Comparable.class;
            Class c3 = String[].class;
            Class c4 = int[][].class;
            Class c5 = ElementType.class;// 枚举类
            Class c6 = int.class;
            Class c7 = Override.class;
            Class c8 = void.class;
            Class c9 = Class.class;
            // 只要数组元素类型和维度一样,就是同一个Class
            int[] a = new int[10];
            int[] b = new int[100];
            Class c10 = a.getClass();
            Class c11 = b.getClass();
            System.out.println(c10 == c11);// true
        }
    
    • 理解类的加载过程
    image.png

    15.3.类的加载与ClassLoader的理解

    • ClassLoader的理解
    image.png image.png
    // 自定义加载类是系统类加载器加载的
    public class ClassLoaderTest {
        @Test
        public void test1(){
            // 先获得Class类的实例,再获取当前自定义类的类加载器
            ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
            System.out.println(classLoader); // AppClassLoader: 系统类加载器
            // getParent(): 获取上一级加载器
            ClassLoader classLoader1 = classLoader.getParent();
            System.out.println(classLoader1);// ExtClassLoader: 扩展类加载器
            ClassLoader classLoader2 = classLoader1.getParent();
            // 引导类加载器显示null不是没有,而是获取不到,不能主动去加载自定义类,不能直接通过它做一些事
            // 引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
            System.out.println(classLoader2);// null: 无法获取引导类加载器
            ClassLoader classLoader3 = String.class.getClassLoader();
            System.out.println(classLoader3);// null
        }
    }
    
    • 用ClassLoder加载配置文件

    Properties: 用来读取配置文件
    写web的时候properties文件写在src下,因为部署到Tomcat服务器是module下的配置文件就缺失了
    为了保证文件的存在,要写在src下

    @Test
        public void test2() throws IOException {
            // 创建配置文件的对象
            Properties pros = new Properties();
            // 把设置中File Encodings下的 Transparent native-to-ascii conversion 勾上防止乱码
            // 此时的配置文件默认在当前的module下
            // 读取配置文件方式一:
            // 造一个输入流对象,构造器参数传入一个配置文件,配置文件在当前module下new一个Resource Bundle
             FileInputStream fis = new FileInputStream("jdbc.properties");
             // 读取的配置文件还是当前module下,只是module下多了一层目录下的文件
            // FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
            // 通过(pros)读取文件对象去调用加载方法,参数传入输入流对象
             pros.load(fis);// pros加载对应的文件输入流对象,其实主要操作对应的jdbc配置文件
            // 方式二: 用ClassLoader 类加载器
            // 配置文件默认识别为:当前module的src下
            // 获取当前自定义加载类的类加载器
            ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
            // 通过系统类加载器以流的形式获取资源文件
            //  File类型获取的是当前module下的,类加载器获取的是源(src)文件下的
            InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
            // 通过文件对象读取输入流的资源
            //pros.load(is);
            // getProperty(): 获取配置文件的键值对中的值: 都是String类型
            // 分别获取user和password
            String user = pros.getProperty("user");
            String password = pros.getProperty("password");
            System.out.println("user = " + user + " password = " + password);
        }
    

    15.4.创建运行时类的对象(重点)

    通过反射,创建运行时类的对象

    通过反射创建对应的运行时类的对象
    Class对应的是哪个运行时类,就只能创建那个类的对象

    newInstance(): 调用此方法,创建对应的运行时类(Person)的实例(对象),内部调的是运行时类的空参构造器
    造对象,都得是用构造器来造,只是形式上不一样
    构造器私有,就是单例模式了,但也是从静态方法里调用构造器的
    要想此方法正常的创建运行时类的对象,要求:
    1.运行时类必须提供空参构造器
    2.提供足够条件的空参构造器的权限修饰符.通常 设置为public
    有可能在学框架的时候,框架底层要用到反射,造javabean的对象,通常通过反射造对象,普遍调空参构造器,要是有属性,后面再通过调属性方式给属性赋值
    在javabean中要求提供一个public的空参构造器。原因:
    1.便于通过反射,创建运行时类的对象
    2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

    public class NewInstanceTest {
        // InstantiationException: 初始化异常, 运行时类没有空参构造器
        // IllegalAccessException: 非法访问异常,构造器权限修饰符私有化,访问权限不够
        @Test
        public void test1() throws InstantiationException, IllegalAccessException {
            // 先获取Class类的实例
            // clazz实例指向方法区中的Person.class这个类
            Class clazz = Person.class;
            Person obj = (Person) clazz.newInstance();// 调的是运行时类的空参构造器
            // 获取运行时类的对象
            // Object obj = clazz.getDeclaredConstructor().newInstance();
            System.out.println(obj);
        }
    }
    
    • 举例体会反射的动态性
    // 体会反射的动态性: 运行时才知道到底要造哪个类的对象
        @Test
        public void test2(){
            for (int i = 0; i < 100; i++) {
                int num = new Random().nextInt(3); // 0~2
                String classPath = "";
                switch (num) {
                    case 0:
                        classPath = "java.util.Date";
                        break;
                    case 1:
                        classPath = "java.lang.Object";
                        break;
                    case 2:
                        classPath = "com.senior.Person";
                        break;
                }
                try {
                    Object obj = getInstance(classPath);
                    System.out.println(obj);
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
        public Object getInstance(String classPath) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            Class clazz = Class.forName(classPath);
            return clazz.newInstance();
        } 
    

    15.5.获取运行时类的完整结构

    • 提供结构丰富的Person类

    看看Person能不能拿到父类的泛型,并实现其他接口
    把自定义注解加到类的相关结构上,不想用默认值可以自行修改,通过反射方式可以拿到注解

    // 丰富结构都在Person里
    public class Creature <T> implements Serializable {
        private char gender;
        public double weight;
        public void breath(){
            System.out.println("生物呼吸");
        }
        public void eat(){
            System.out.println("生物吃东西");
        }
    }
    
    public interface MyInterface {
        // 定义抽象方法
        public abstract void info();
    }
    
    // 两个元注解: 需要什么结构,生命周期
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    // 要想通过反射获取当前的注解,要把SOURCE改成RUNTIME,这时会加载到内存当中,只有加载到内存中的结构才能通过反射获取
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        // 创建一个数组 默认值为hello
        String value() default "hello";
    }
    
    @MyAnnotation(value = "hi")
    public class Person extends Creature<String> implements Comparable<String>,MyInterface{
        private static String GENDER;
        private String name;
        int age;
        public int id;
        public Person(){
    
        }
    @MyAnnotation(value = "abc")
        private Person(String name) {
            this.name = name;
        }
    
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        private static void showDesc(){
            System.out.println("我是个可爱的人");
        }
        @MyAnnotation
        private String show(String nation){
            System.out.println("我的国籍是: " + nation);
            return nation;
        }
        public String display(String interests){
            return interests;
        }
        @Override
        public void info() {
            System.out.println("我是个人");
        }
    
        @Override
        public int compareTo(String o) {
            return 0;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", id=" + id +
                    '}';
        }
    }
    
    • 获取运行时类的属性结构及其内部结构
    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();
            // 获取声明过的属性
            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();
                // Modifier类中重写了toString方法,default默认为空
                System.out.print(Modifier.toString(modifier) + "\t");
                // 2.获取数据类型
                Class type = f.getType();
                // getName(): 省略class
                System.out.println(type.getName() + "\t");
                // 3.获取变量名
                String fName = f.getName();
                System.out.print(fName);
                System.out.println();
            }
        }
    }
    
    • 获取运行时类的方法结构
    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);
            }
            //getDeclaredMethods ():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method m : declaredMethods) {
                System.out.println(m);
            }
        }
    }
    
    • 获取运行时类的构造器结构
    @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的实例
            Class superClass = clazz.getSuperclass();
            System.out.println(superClass);
        }
        /*
        获取运行时类的带泛型的父类
         */
        @Test
        public void test3(){
            Class clazz = Person.class;
            // 父类也是Class的实例
            // Class类实现了Type接口
            Type genericSuperclass = clazz.getGenericSuperclass();
            System.out.println(genericSuperclass);
        }
    
    • 获取运行时类的带泛型的父类的泛型
    /*
        获取运行时类的带泛型的父类的泛型
         */
        @Test
        public void test4(){
            Class clazz = Person.class;
            // 父类也是Class的实例
            Type genericSuperclass = clazz.getGenericSuperclass();
            // 确定类型的参数
            ParameterizedType paramType = (ParameterizedType) genericSuperclass;
            // 获取实际上的类型参数,实际上就是获取泛型参数
            Type[] actualTypeArguments = paramType.getActualTypeArguments();
            System.out.println(actualTypeArguments[0]);// 返回泛型类
            // System.out.println(actualTypeArguments[0].getTypeName());// 省略class关键字
            // System.out.println(((Class)actualTypeArguments[0]).getName());// 同上
            System.out.println(paramType);
            System.out.println(genericSuperclass);
        }
    
    • 获取运行时类实现的接口,包,注解
    /*
        获取运行时类实现的接口
         */
        @Test
        public void test5(){
            Class clazz = Person.class;
            // 因为可以多实现所以返回数组类型
            Class[] interfaces = clazz.getInterfaces();
            for (Class c : interfaces) {
                System.out.println(c);
            }
            System.out.println();
            // 获取运行时类的父类实现的接口
            Class[] interface1 = clazz.getSuperclass().getInterfaces();
            for (Class c : interface1) {
                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);
            }
        }
    

    15.6.调用运行时类的指定结构(重点)

    • 调用运行时类的指定属性
    /*
    不需要掌握
    调用运行时类的指定属性
     */
    public class ReflectionTest {
        @Test
        public void testField() throws Exception{
            // 获取Class类的实例
            // 一个类只有唯一的一个运行时类,即大Class对象
            Class clazz = Person.class;
            // 调Person类的(空参)构造器创建运行时类的对象
            Person p = (Person) clazz.newInstance();
            // 获取运行时类的指定属性: 要求运行时类中属性声明为public
            // 通常不采用此方法
            Field id = clazz.getField("age");
            //id.setAccessible(true); // 不适用于getField()
            /*
             设置当前属性值
             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.getDclaredField(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 testField2() throws Exception{
            Class clazz = Person.class;
            // 获取运行时类的属性
            Field gender = clazz.getDeclaredField("GENDER");
            // 保证属性可访问
            gender.setAccessible(true);
            // 调用属性
            gender.set(null,"male");// 对象参数也可写当前运行时类
            System.out.println(gender.get(null));
        }
    }
    
    • 调用运行时类的指定方法
    /*
        如何操作运行时类的指定的方法 -- 要掌握
        非静态的方法: 必须要有运行类的对象
         */
        @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);// 因为show是Class类型的方法,所以参数类型也要是class类型
            // 2.保证方法可访问
            show.setAccessible(true);
            /*
            3.调用方法的invoke(): 参数1: 方法的调用者, 参数2: 给方法形参赋值的实参
            invoke()的返回值即为对应类中调用方法的返回值,默认是Object类型
             */
            Object returnValue = show.invoke(p, "CHN");// String nation = p.show("CHN");
            System.out.println(returnValue);
            /*
            如何调用静态方法
             */
            // 1.获取运行时类的方法
            Method showDesc = clazz.getDeclaredMethod("showDesc");
            // 2.保证方法可访问
            showDesc.setAccessible(true);
            // 3.方法调用invoke(): 如果调用的运行时类中的方法没有返回值,则此invoke()返回null
            // Object returnaVal = showDesc.invoke(clazz); 这样也可以
            Object returnaVal = showDesc.invoke(Person.class);
            // Object returnaVal = showDesc.invoke(null);// 因为静态方法都是共享的,所以调用不需要具体对象所以可以写null
            System.out.println(returnaVal);// null
        }
    
    • 调用运行时类的指定构造器
    @Test
        public void testConstructor() throws Exception{
            Class clazz = Person.class;
            // 获取运行时类指定的构造器
            // getDeclaredConstructor(): 参数: 指明构造器的参数列表
            Constructor constructor = clazz.getDeclaredConstructor(String.class);
            // 保证构造器可访问的
            constructor.setAccessible(true);
            // 调用构造器的newInstance()造对象
            // 构造器调的newinstance()和运行类调的不同,运行时类的是空参的,构造器调的是带参数的重载构造器
            Person p = (Person) constructor.newInstance("Tom");// 形参对应运行时类构造器的参数类型
            System.out.println(p);
        }
    

    15.7.反射的应用: 动态代理

    相关文章

      网友评论

          本文标题:Java高级-反射

          本文链接:https://www.haomeiwen.com/subject/fcyajltx.html