JAVA反射机制

我们知道JAVA中一个比较重要的特性就是反射机制,那么反射究竟是什么,我们又能用反射做些什么呢?今天我们就来一探究竟。

反射的概念

反射:将类的各个组成部分封装为其他对象,这就是反射机制。

反射的作用:

1、在程序运行时操作这些对象;

应用示例:IDEA中定义一个list对象,在使用“list.”时,会弹出对应的方法,这个就是利用反射,在IDEA运行时,就能知道对象list的成员方法;
image-20200516201709262.png

2、可以解耦,提高程序的可扩展性;

讲到这里,我们可能还很迷惑,JAVA的反射机制究竟是什么?在进行下面的讲解前,我们先了解一下JAVA代码在计算机中的三个阶段,来帮助我们理解反射机制。

JAVA程序在计算机中的三个阶段

image-20200516194635658.png

1、第一个阶段:源代码阶段也就是我们写的JAVA代码,通过编译器编译成字节码文件这个过程,我们写的类含有成员变量,构造方法,成员方法;

2、第二个阶段:类加载器将我们的字节码文件加载到内存,生成Class类对象(Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建)。反射定义中的”封装为其他对象“,这里的对象便是指,成员变量封装为Filed对象,构造方法封装为Constructor对象以及成员方法封装为Method对象

3、第三个阶段:该阶段就可以创建对象和调用对象里的方法。通过new Student()创建对象。

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。简单来说就是得到Class对象后反向获取Student对象的各种信息。

获取Class对象的方式:

对应java代码在程序中的三个阶段:
image-20200516211903426.png

1、Class.forName("全类名"):将字节码文件加载进内存,返回Class对象;

使用场景:多用于配置文件,将全类名配置到配置文件中,通过读取配置文件加载类;

2、类名.class :通过类名的属性Class获取;

使用场景:多用于参数传递;

3、对象.getClass():通过类的getClass方法获取,getClass()方法在Object类中定义,所有的类都有该方法;

使用场景:已经得到类对象实例的情况;

示例代码如下:

public class Student {
    private String name;
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void goToSchool(){
        System.out.println("go to school");
    }
    public static void main(String[] args) throws Exception {
        //1、Class.forName("全类名")
        Class aClass = Class.forName("com.sean.Student");
        System.out.println(aClass);

        //2、类名.class
        Class bClass = Student.class;
        System.out.println(bClass);

        //3、类对象.getClass()
        Student student = new Student();
        Class cClass = student.getClass();
        System.out.println(cClass);
    }
}

PS:同一个字节码文件(*.class),再一次程序运行中,只会被加载一次,三种方式获取的Class类对象是同一个,可以通过比较aClass,bClass和cClass来验证;

Class对象的使用

下面我们就简单列出几个常用的功能,其他功能使用可以参见JAVA的API文档中的Class类。

这里我们创建一个Student类,便于下面我们使用反射操作Student类对象。

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    public void goToSchool(){
        System.out.println("go to school");
    }

    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;
    }

}

1、获取成员变量对象(Filed),操作类的成员变量

方法 用途
getField(String name) 获得某个公有的属性对象
getFields() 获得所有公有的属性对象
getDeclaredField(String name) 获得某个属性对象
getDeclaredFields() 获得所有属性对象

Filed类具体的用法可以查看JAVA的API文档,这里我们只演示了利用获取到的Filed对象获取以及设置成员变量的值。

public static void main(String[] args) throws Exception {
        Class bClass = Student.class;
        //获取Student对象的所有public属性成员变量Filed对象
        Field[] fields = bClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        //获取Student对象中name属性成员变量,因为name为私有,这里会抛java.lang.NoSuchFieldException异常
        Field name = bClass.getField("name");
        System.out.println(name);

        //获取Student对象中name属性成员变量Filed对象
        Field name1 = bClass.getDeclaredField("name");
        System.out.println(name1);
        /**
         * setAccessible(true)暴力反射
         * 这里由于name属性是私有成员变量,不加这一句,
         * 后面的获取以及设置成员变量会抛java.lang.IllegalAccessException异常
         */
        name1.setAccessible(true);

        //获取name成员变量的值
        Student student = new Student();
        Object o = name1.get(student);
        System.out.println(o);

        //设置name成员变量的值
        name1.set(student,"zhangsan");
        System.out.println(student.getName());

   }

2、获取构造方法对象(Constructor),操作类的构造函数

方法 用途
getConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
getConstructors() 获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获得该类所有构造方法

Constructor类具体的用法可以查看JAVA的API文档,这里我们只演示了利用获取到的Constructor对象创建Student对象。

public static void main(String[] args) throws Exception {

        Class bClass = Student.class;
        //获取Student对象的无参构造函数Constructor对象
        Constructor constructor = bClass.getConstructor();

        //利用获取到的Constructor对象创建Student对象
        Object o = constructor.newInstance();
        System.out.println(o);

}

3、获取成员方法对象(Method),操作类的成员方法

方法 用途
getMethod(String name, Class...<?> parameterTypes) 获得该类某个公有的方法
getMethods() 获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes) 获得该类某个方法
getDeclaredMethods() 获得该类所有方法

Method类具体的用法可以查看JAVA的API文档,这里我们只演示了利用获取到的Method对象执行执行Student类的成员方法。

public static void main(String[] args) throws Exception {

        Class bClass = Student.class;
        //获取Student对象的goToSchool成员方法Method对象
        Method goToSchool = bClass.getMethod("goToSchool");

        //利用获取到的Method对象执行Student类中的成员方法
        Student student = new Student();
        goToSchool.invoke(student);

}

4、获取类名

方法 用途
getName() 获得该类的类名
public static void main(String[] args) throws Exception {

        Class bClass = Student.class;
        //获取Student对象的类名
        String name = bClass.getName();
        System.out.println(name);
}

总结

JAVA中有很多开源的框架(框架是半成品的软件,可以在框架的基础上进行编码),使用这些框架能极大的简化我们的开发过程。而反射是框架的灵魂。当然不了解反射我们也可以使用这些框架进行开发,但了解反射的机制,能让我们知其然,知其所以然,在使用这些框架的时候更加得心应手。


sean
7 声望0 粉丝

我在学JAVA