Java 反射Reflection总结一

一、反射的概念

反射,英文Reflection,是指运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。简单理解就是,通过反射机制,我们可以获取到类的全部信息(方法、字段属性、构造方法等)。要实现反射机制,就必须获取到类对应的Class对象,即类对应的字节码文件信息,每一个类只有一个Class,也只对应一个字节码文件

 

二、反射的本质

要理解反射的本质,先要熟悉类的加载顺序过程:

【a】当一个类XXX被编译之后,编译器就会生成该类对应的后缀为XXX.class的二进制字节码文件;

【b】当我们通过构造方法new实例XXX对象的时候,JVM虚拟机就会加载XXX.class字节码文件;

【c】JVM会在我们本地磁盘中查找该类对应的XXX.class字节码信息,并且加载到jvm内存中;

【d】JVM将XXX.class字节码读入内存并且根据它生成xxx.Class对象,这个class对象是有虚拟机自动创建的,一个类只有一个class对象信息,当第二次加载同样的类时,该Class对象还是同一个。

以上是类加载的大概顺序,所谓反射,就是根据上面JVM帮我们自动生成的Class类对象,反过来拿到该类的全部信息,包括字段属性、方法、构造方法等。

 

三、Class对象获取的三种方式

【a】Class类中的静态方法forName方法: 指定类的全限定名(即全路径包名+类名).

Class<?> clazz = Class.forName("com.wsh.reflection01.User");

【b】XXX.class方法:通过类名.class方式获取

Class<?> cls = User.class;

【c】实例对象.getClass()方法:通过实例对象获取

Class<?> cls2 = new User().getClass();

以下是一个示例,说明Class对象的三种获取方式以及证明一个类只有一个.class字节码文件信息:

public class User {
    private int id;
    private String name;
    private int age;

    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }
}
public class TestReflection {
    public static void main(String[] args) {

        try {
            /**
             * 说明:
             * 类被加载后,java虚拟机会创建一个对应这个类的Class对象,整个类的结构信息都会存放在clazz类对象中
             * 通过Class对象,可以获取到类的全部信息.(反射)
             * 一个类只有一个Class对象
             */
            Class<?> clazz = Class.forName("com.wsh.reflection01.User");
            Class<?> clazz2 = Class.forName("com.wsh.reflection01.User");

            //clazz = class com.wsh.reflection01.User
            System.out.println("clazz = " + clazz);

            //true
            System.out.println(clazz == clazz2);

            /**
             * 获取Class类对象的其他方式
             */
            Class<?> cls = User.class;
            Class<?> cls2 = new User().getClass();
            //true
            System.out.println(cls.hashCode() == cls2.hashCode());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

运行结果如下图:

 

四、反射API介绍

【a】获取类的名字信息:

/**
 * @Description: 获取类的相关信息
 * @author: weishihuai
 * @Date: 2018/12/19 14:53
 */
public class GetClassInfo {
    public static void main(String[] args) {

        try {
            /**
             * 获取User.Class对象
             */
            Class<?> clazz = Class.forName("com.wsh.reflection02.User");

            /**
             * 获取类的全路径名字
             * com.wsh.reflection02.User
             */
            System.out.println("类的全路径名字信息-->" + clazz.getName());

            /**
             * 获取类的简易名字
             * User
             */
            System.out.println("类的简易名字-->" + clazz.getSimpleName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

运行结果如下:

 

【b】获取类的字段属性信息:

  •  getFields() : 只能获取全部public修饰的公开属性
  •  getDeclaredFields() : 获取类声明的全部属性
  •  getField() : 只能获取public修饰的属性
  •  getDeclaredField(String name) : 根据字段名称获取声明的属性
/**
 * @Description: 获取类的字段信息
 * @Auther: weishihuai
 * @Date: 2018/12/19 15:03
 */
public class GetFieldInfo {
    public static void main(String[] args) {
        /**
         * 获取User.Class对象
         */
        Class<?> clazz;
        try {
            clazz = Class.forName("com.wsh.reflection02.User");

            /**
             * 获取类的字段属性
             * getFields() : 只能获取全部public修饰的公开属性
             * getDeclaredFields() : 获取类声明的全部属性
             *
             * getField() : 只能获取public修饰的属性
             * getDeclaredField(String name) : 根据字段名称获取声明的属性
             */
            Field[] fields = clazz.getFields();
            //0
            System.out.println("类中public修饰的公开属性的字段数量为-->" + fields.length);


            Field[] fields1 = clazz.getDeclaredFields();
            //3
            System.out.println("类声明的字段数量为-->" + fields1.length);


            /**
             * 根据字段名称获取字段信息
             * private java.lang.String com.wsh.reflection02.User.name
             */
            System.out.println(clazz.getDeclaredField("name"));

        } catch (ClassNotFoundException | NoSuchFieldException e) {
            e.printStackTrace();
        }

    }
}

注意:如果使用getField("name")获取的公开属性不存在的时候,会报NoSuchFieldException异常:

System.out.println(clazz.getField("name"));

 

【c】获取类的普通方法信息:

  • getDeclaredMethods() : 获取类声明的全部方法
  • getDeclaredMethod(String name, Class<?>... parameterTypes) : 根据方法名称以及参数类型获取类声明的方法(第二个参数: 可变参数,指定参数类型,避免方法重载,获取不了相应的方法)
  • getMethod(String name, Class<?>... parameterTypes) : 根据方法名称以及参数类型获取public公开属性的方法
  • getMethods() : 获取类声明的全部public修饰的方法
/**
 * @Description: 获取类的普通方法信息
 * @Auther: weishihuai
 * @Date: 2018/12/19 15:16
 */
public class GetMethodInfo {
    public static void main(String[] args) {
        /**
         * 获取User.Class对象
         */
        Class<?> clazz = null;
        try {
            clazz = Class.forName("com.wsh.reflection02.User");

            /**
             * getDeclaredMethods() : 获取类声明的全部方法
             * getDeclaredMethod(String name, Class<?>... parameterTypes) : 根据方法名称以及参数类型获取类声明的方法(第二个参数: 可变参数,指定参数类型,避免方法重载,获取不了相应的方法)
             *
             * getMethod(String name, Class<?>... parameterTypes) : 根据方法名称以及参数类型获取public公开属性的方法
             * getMethods() : 获取类声明的全部public修饰的方法
             */

            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("方法名称-->:" + method);
            }

            Method method1 = clazz.getDeclaredMethod("getName");
            System.out.println(method1);

            Method method2 = clazz.getDeclaredMethod("setName", String.class);
            System.out.println(method2);

            Method method3 = clazz.getMethod("setName", String.class);
            System.out.println(method3);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        }


    }
}

运行结果如下:

 

【d】获取类的构造方法信息

  •  getDeclaredConstructors() : 获取类声明的全部构造方法
  •  getDeclaredConstructor(Class<?>... parameterTypes) : 根据构造方法类型获取对应的构造方法(必须指定构造方法的参数.Class类型对象)
  •  getConstructors() : 获取public修饰的公开构造方法
  •  getConstructor(Class<?>... parameterTypes) : 根据构造方法类型获取public修饰的公开构造方法(必须指定构造方法的参数.Class类型对象)
/**
 * @Description: 获取类的构造方法信息
 * @author: weishihuai
 * @Date: 2018/12/19 15:20
 */
public class GetConstructor {
    public static void main(String[] args) {

        /**
         * 获取User.Class对象
         */
        Class<?> clazz;
        try {
            clazz = Class.forName("com.wsh.reflection02.User");


            /**
             * getDeclaredConstructors() : 获取类声明的全部构造方法
             * getDeclaredConstructor(Class<?>... parameterTypes) : 根据构造方法类型获取对应的构造方法(必须指定构造方法的参数.Class类型对象)
             *
             * getConstructors() : 获取public修饰的公开构造方法
             * getConstructor(Class<?>... parameterTypes) : 根据构造方法类型获取public修饰的公开构造方法(必须指定构造方法的参数.Class类型对象)
             */

            Constructor[] constructors = clazz.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }

            /**
             * 获取无参构造方法
             */
            Constructor constructor1 = clazz.getDeclaredConstructor(null);
            System.out.println(constructor1);

            /**
             * 获取有参构造方法
             */
            Constructor constructor2 = clazz.getDeclaredConstructor(int.class, String.class, int.class);
            System.out.println(constructor2);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        }


    }
}

运行结果:

 

五、总结

以上就是关于如何使用反射机制获取类的字段、方法以及构造方法信息的方法,仅仅对反射机制的概念以及如何通过反射动态获取类信息的实践,本文是笔者对反射的总结第一部分,下篇文章将会讲解如何通过反射动态调用类的方法、动态创建对象以及一些示例等,仅供大家学习参考,希望对大家有所帮助。

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付 19.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值