• 通过反射获取对象的构造器


    在学习这个之前,先复习下对象构造器的作用。

    一、构造器:

    构造方法的名字必须和所在类的名字一致,没有返回值,但不能声明void,访问权限可以为任意,但是一般情况下使用public方法权限,构造方法中的参数可以根据需要自行定义,参数的不同的构造方法构成重载;

    例如:

    public class Person {
        private String name;
        private String age;
        //getter setter ..
        public static void main(String[] args) {
            Person person = new Person();
        }
    }

     这个Person对象,我们在通过new的时候,默认调用了对象的无参构造方法,通过无参构造方法对这个类进行初始化,当我们没有给出构造方法的时候,java会默认提供一个无参构造器。

    当我给类一个构造器的时候,会默认覆盖类中的无参构造

    public class Person {
        private String name;
        private Integer age;
        Person(String name,Integer age){
            this.age = age;
            this.name = name;
        }
        //getter setter ..
        public static void main(String[] args) {
            Person person = new Person("张三",20);
        }
    }

    在这个类中,我们就调用了我们创建的构造器去初始化对象

    构造方法中的参数可以根据需要自行定义,参数的不同的构造方法构成重载

    例:

    public class Fu {
        Fu(){}//无参公有的构造方法
        Fu(int i){} //参数为Int的公有构造方法
    }
    public class Zi extends Fu{
        Zi(){} //无参的公有构造方法
        Zi(int i){} //参数类型为int的公有构造方法
        Zi(int i,double d){} //参数类型为int和double的公有构造方法
    }

     java中构造方法的使用有两个地方,一个是跟在关键字new后面,类名加上一个小括号(),小括号内根据实际加上实参,

    另外一个是跟在关键字super或this后加上一个小括号(),小括号内根据实际添加实参,下面进行举例。

    调用本类的构造方法:

    public class Zi extends Fu {
        Zi() {
            this(2);//调用参数为int类型的本类的构造方法
        }
    }

    调用父类的构造方法:

    public class Zi extends Fu {
        Zi() {
            super(2);//调用参数为int类型的父类的构造方法
        }
    }

    注意:

    public class Zi extends Fu {
        /**
         * 注意:例子中中this或super调用构造方法只能出现在构造方法中,
         * 而且必须出现在第一行,所以一个构造方法中第一行只能为this或super
         * 调用构造方法,两者不能同时调用构造方法出现,而且注意this或super
         * 调用构造方法时,要留构造方法出口,意思就是最后调用的构造方法中没
         * 有再调用别的构造方法!(下面并不能一起参数相同的构造器,演示需求)
         */
        Zi() {
            this(2);//调用参数为int类型的本类的构造方法
        }
        Zi() {
            super(2);//调用参数为int类型的父类的构造方法
        }
    }
     
    构造方法的作用 
    • 为了初始化成员属性,而不是初始化对象,初始化对象是通过new关键字实现的
    • 通过new调用构造方法初始化对象,编译时根据参数签名来检查构造函数,称为静态联编和编译多态(参数签名:参数的类型,参数个数和参数顺序)
    • 创建子类对象会调用父类构造方法但不会创建父类对象,只是调用父类构造方法初始化父类成员属性

    我总是要把构造器和方法混淆,后来发现,方法实际上是需要用于执行java代码的,而构造器,构造器是一个类的实例!

    为什么呢?

    类的实例,我们需要用类来创建对象,进而访问其属性,因为实例是需要被用来调用的,但是调用的话,我们不得不去考虑一个问题,就是对象,最终是被存储在内存里面的,而存储的时候,

    我们的内存不得不需要给他再另外开辟新的内存空间,那么,java是如何给这种我们所需要的类来开辟内存空间的呢?这就涉及到了java的内存机制,就是说,我们必须要给这个类制作一个构造器,

    而且这个构造器的名称必须和这个类的名称是一致的,这样,我们的java编译器才能识别这个类,进而给这个类在内存中开辟内存空间,也就是我们所说的,我们手动,人为的给他进行“初始化”,事例如下:

    public class Fu {
        Fu(){
            System.out.println("Construct");
        }//无参公有的构造方法
        Fu(int i){} //参数为Int的公有构造方法
    }

      这样,当我们在对Fu类进行调用的时候,我们的java编译器就会事先对他进行“自动”地初始化,开辟内存空间

      那么现在问题又来了,举个例子,我们的Fu()方法需要带有一个参数,形参,但是整个代码中,需要不仅仅是带有形参的Fu();还需要不带形参的Fu(),在我们的构造器对类进行构造的时候,

    需要将功能类似的,但形参不同的方法同时打包在该类下,以便在我们调用某个方法的时候,直接重载构造器中的该方法,可以说,这种构造形式,满足了我们对功能类似,形参不同的方法,

    调用的时候,进行重载,而满足了编译器自动初始化,人不需要手动初始化的需求。

      而且有个,问题,本来两个方法,功能上是类似的,一棵树和一株树苗,你非得要给他们起不同的名字,多别扭,好在有了构造器,能够是功能相似的方法起相同的名字,

    不同的参数,而能够在被调用的时候得以重载,多么牛逼的构造器啊。

    二、通过反射获取公共的构造器,并使用构造器创建对象

    创建对象:

    public class Animal {
        private  String name;
        private String sex;
        private Integer age;
        //无参构造器
        Animal(){}
        //有参构造
        Animal(String name, String sex, Integer age) {
            this.name = name;
            this.sex = sex;
            this.age = age;
        }
    }
    /**
         * 通过获取类的构造器来实例化对象
         */
        public static void main(String[] args) throws Exception {
            Class<?> animal = Class.forName("constructure.Animal");
            Constructor[] constructors = animal.getDeclaredConstructors();
            /**
             * 输出:
             * constructure.Animal  0
             * ----------------
             * constructure.Animal  3
             * class java.lang.String
             * class java.lang.String
             * class java.lang.Integer
             * ----------------
             */
            for (Constructor<?> constructor : constructors){
                //获取类中的构造器名称与构造器个数
                System.out.println(constructor.getName()+"  "+constructor.getParameterCount());
                //获取构造器的参数类型
                Class<?>[] paramType = constructor.getParameterTypes();
                for (Class<?> param : paramType){
                    System.out.println(param);
                }
                System.out.println("----------------");
            }
            //通过反射取得构造器并实例化对象
            //参数个数代表想要获取的那个构造器
            Constructor<?> constructor = animal.getDeclaredConstructor();
            //调用了无构造器!
            //Animal{name='null', sex='null', age=null}
            Object animalInstance = constructor.newInstance();
            System.out.println(animalInstance);
            //调用有参构造实例化对象,先获取有参构造器
            Constructor<?> constructor1 = animal.getDeclaredConstructor(String.class,String.class,Integer.class);
            //Animal{name='小猪', sex='公', age=10}
            Object object = constructor1.newInstance("小猪","公",10);
            System.out.println(object);
        }

    想获取什么构造器就根据需要的参数类型传入getDeclaredConstructor

    注意:getDeclaredConstructor 与getConstructor的区别

    getDeclaredConstructor 代表获取该class对象下所有的构造器,而getConstructor只能获取被public声明的构造器,有时候idea会提示多余代码,代码洁癖可能就将public去掉了,这样使用getConstructor就无法获取Construct

    例如:

        //无参构造器
        public Animal(){
            System.out.println("调用了无构造器!");
        }
        Animal(){
            System.out.println("调用了无构造器!");
        }

    这两个无参构造器,没有被public修饰的就无法通过getConstructor方法获取他的构造器

  • 相关阅读:
    痞子衡嵌入式:并行NAND接口标准(ONFI)及SLC Raw NAND简介
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(6)- Bootable image格式与加载(elftosb/.bd)
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(5)- 再聊eFUSE及其烧写方法
    痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(4)- Flashloader初体验(blhost)
    Hystrix完整配置列表
    使用Redis实现UA池
    使用Redis实现延时任务(二)
    使用Redis实现延时任务(一)
    一文彻底理解Redis序列化协议,你也可以编写Redis客户端
    一个低级错误引发Netty编码解码中文异常
  • 原文地址:https://www.cnblogs.com/huanghuanghui/p/10164528.html
Copyright © 2020-2023  润新知