java反射:获取Class实例、创建运行时类对象、调用指定结构

1. 获取Class实例(运行时类)

public void test1() throws ClassNotFoundException {
        //1.调用运行时类的静态属性:class
        Class clazz1 = User.class;
        System.out.println(clazz1);

        //2. 调用运行时类的对象的getClass()
        User u1 = new User();
        Class clazz2 = u1.getClass();

        //3. 调用Class的静态方法forName(String className)
        String className = "com.hongshan.User"; //全类名
        Class clazz3 = Class.forName(className);

        System.out.println(clazz1 == clazz2);//true
        System.out.println(clazz1 == clazz3);//true

        //4. 使用类的加载器的方式 (了解)
        Class clazz4 = ClassLoader.getSystemClassLoader().loadClass("com.hongshan.User");
        System.out.println(clazz1 == clazz4);//true
    }

上述四种方式中,第三种调用Class静态方法forName获取Class实例,一般在框架中运用得比较多,因为这种方法是动态获取实例的,不需要在编译阶段就定好具体哪一个类。第四种方式和第三种雷同。

2. 通过运行时类创建对象

通过反射创建对象,是通过第一章节中获取的Class实例(运行时类)来创建对象的。在java8及之前通过运行时类创建对象推荐调用Class实例的newInstance()方法。此方法需要满足以下条件方可使用:
条件1:要求运行时类中必须提供一个空参的构造器
条件2:要求提供的空参的构造器的权限要足够。

    public void test1() throws InstantiationException, IllegalAccessException {

        Class clazz = Person.class;

        //创建Person类的实例
        Person per = (Person) clazz.newInstance();

        System.out.println(per);
    }
}

由于上述方式创建运行时类对象受限于两个条件,在java8之后,推荐使用Class实例获取构造器,通过构造器返回一个运行时类的实例对象。

public void test2() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        Class clazz = Person.class;

        //1.通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器
        Constructor constructor = clazz.getDeclaredConstructor();
        
        //2.setAccessible(true):确保此构造器是可以访问的
        constructor.setAccessible(true);
        
        //3.通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例。
        Person person = (Person) constructor.newInstance();
        
        System.out.println(person);
    }

3. 通过运行时类调用指定结构

3.1 调用指定的属性

    public void test2() throws Exception {
        Class clazz = Person.class;

        //
        Person per = (Person) clazz.newInstance();

        //1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性
        Field nameField = clazz.getDeclaredField("name");

        //2. setAccessible(true):确保此属性是可以访问的
        nameField.setAccessible(true);

        //3. 通过Filed类的实例调用get(Object obj) (获取的操作)
        // 或 set(Object obj,Object value) (设置的操作)进行操作。
        nameField.set(per,"Tom");
        System.out.println(nameField.get(per));
    }

步骤1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性
步骤2. setAccessible(true):确保此属性是可以访问的
步骤3. 通过Filed类的实例调用get(Object obj) (获取的操作)或 set(Object obj,Object value) (设置的操作)进行操作。

3.2 调用指定的方法

  public void test3() throws Exception {
        Class clazz = Person.class;

        Person per = (Person) clazz.newInstance();

        //1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法
        Method showNationMethod = clazz.getDeclaredMethod("showNation",String.class,int.class);

        //2. setAccessible(true):确保此方法是可访问的
        showNationMethod.setAccessible(true);

        //3.通过Method实例调用invoke(Object obj,Object ... objs),即为对Method对应的方法的调用。
        //invoke()的返回值即为Method对应的方法的返回值
        //特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
        Object returnValue = showNationMethod.invoke(per,"CHN",10);
        System.out.println(returnValue);
    }

步骤1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法
步骤2. setAccessible(true):确保此方法是可访问的
步骤3.通过Method实例调用invoke(Object obj,Object ... objs),即为对Method对应的方法的调用。
     invoke()的返回值即为Method对应的方法的返回值
     特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null

3.3 调用指定的构造器

public void test4() throws Exception {

        Class clazz = Person.class;

        //1.通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器
        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);

        //2.setAccessible(true):确保此构造器是可以访问的
        constructor.setAccessible(true);

        //3.通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例。
        Person per = (Person) constructor.newInstance("Tom", 12);

        System.out.println(per);

    }

步骤1.通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器
步骤2.setAccessible(true):确保此构造器是可以访问的
步骤3.通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例。