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

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

一、构造器:

构造方法的名字必须和所在类的名字一致,没有返回值,但不能声明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方法获取他的构造器

posted @ 2018-12-23 15:48  huanghaunghui  阅读(2287)  评论(0编辑  收藏  举报