Java 反射机制

Java 反射机制

概述

通俗易懂的来说,反射是可以理解为 Java 运行时的一本说明书。这本说明书里面,记录着被加载到 Java 虚拟机中 Class 对象的名字、域、方法信息,无论这些信息是私有的还是公有的。我们可以通过这本说明书,来在运行时了解一个类的信息,然后根据这些信息,再进行动态的调用。

作用

  • 可以在运行时探明、加载和使用编译期间完全未知的类
  • 可以根据类的名称,动态加载一个类,而不需要在编码期间就确定调用哪个类
  • 如果一个类已经加载,可以利用反射知道这个类的所有属性和方法
  • 对于任意一个对象,都能调用它的方法和属性,甚至可以动态修改它的访问权限

作用机制

Java虚拟机加载完类之后,会在堆内存中产生一个 ==Class== 类型的对象(每个类只有一个Class对象),这个对象包含了完整的类的结构信息。这也是 Java 反射得名的原因——这个Class包含了完整的类的结构信息,就像一面镜子。

p.s. Java 的八种原始类型以及关键字 void 也会被视为 Class 对象。

获取 Class 对象

假设有一个类 Car,一个类型为 Car 的对象 car,我们有以下三种方法获取 Class 对象:

  • 通过调用对象的 ==getClass()== 方法 car.getClass()
  • 通过类的 ==.class==属性 (最安全且性能最佳) Car.class
  • 通过 ==Class.forName(String className)== 动态加载 Class.forName("Car")

怎么利用 Class 类

==java.lang.reflect== 包中有三个类 Field 、 Method 和 Constructor,分别用于描述类的域、方法和构造器。

我们可以从==Class==类中获取包括构造函数、方法、属性、修饰符等信息,下面是常用的方法列表:

方法的作用 方法的签名 方法的包
返回一个类的新实例 Object newInstance() java.lang.Class 1.0
构造一个这个构造器所属类的新实例 Object newInstance(Object[] args) java.lang.reflect.Constructor 1.1
返回这个类和其超类的公有域 Field[] getFields() java.lang.Class 1.0
返回这个类的全部域 Field[] getDeclaredFields() java.lang.Class 1.0
返回这个类和其超类的公有方法 Method[] getMethods() java.lang.Class 1.0
返回这个类的全部方法 Method[] getDeclaredMethods() java.lang.Class 1.0
返回这个类的公有构造器 Constructor[] getConstructors() java.lang.Class 1.0
返回这个类的所有构造器 Constructor[] getDeclaredConstructors() java.lang.Class 1.0
调用一个对象所描述的方法 Object invoke(Object implicitParameter, Object[] explicitParameters) java.lang.reflect.Method 1.1
... ... ...

更多方法请查阅 java.lang.reflect 文档

创建对象

通过反射来生成对象的方式有两种:

  • 使用 ==Class== 对象的 ==newInstance()== 方法来创建该 ==Class== 对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器).
  • 先使用 ==Class== 对象获取指定的 ==Constructor== 对象, 再调用Constructor对象的 ==newInstance()== 方法来创建该 Class 对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例).

调用方法

当获取到某个类对应的 Class 对象之后, 就可以通过该 Class 对象的 ==getMethod== 来获取一个 Method 数组或 Method 对象。每个 Method 对象对应一个方法,在获得 Method 对象之后,就可以通过调用 ==invoke== 方法来调用该 Method 对象对应的方法.

访问成员变量

通过 ==Class== 对象的的 ==getField()== 方法可以获取该类所包含的全部或指定的成员变量 Field ,Field 提供了如下两组方法来读取和设置成员变量值.

  • ==getXxx(Object obj)==: 获取obj对象的该成员变量的值, 此处的Xxx对应8中基本类型,如果该成员变量的类型是引用类型, 则取消get后面的Xxx;
  • ==setXxx(Object obj, Xxx val)==: 将obj对象的该成员变量值设置成val值.此处的Xxx对应8种基本类型, 如果该成员类型是引用类型, 则取消set后面的Xxx;

具体应用场景

利用反射实现工厂模式,从此,我们将对象的实例化操作,分离到了工厂类中去实现。我们可以利用反射实现的工厂模式,对编程进行解耦操作。如下,将 Car 对象的实例化操作,交给了 CarFactory 实现,我们只需要将所需要实例化的对象的类名,以 String 形式传入工厂即可:

package Reflect;

interface Car {
    public void accelerate();
    public void brake();
}

class Cadillac implements Car {
    public void accelerate() {
        System.out.println("凯迪拉克加速");
    }
    public void brake() {
        System.out.println("凯迪拉克刹车");
    }
}

class Buick implements Car {
    public void accelerate() {
        System.out.println("别克加速");
    }
    public void brake() {
        System.out.println("别克刹车");
    }
}

class CarFactory {
    public static Car getInstance(String CarClassName) {
        Car car = null;
        try {
            car = (Car) Class.forName(CarClassName).newInstance();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        return car;
    }
}

class Test {
    public static void main(String[] args) {
        Car car = CarFactory.getInstance("Reflect.Cadillac");
        if(car != null){
            car.accelerate();
            car.brake();
        }
        car = CarFactory.getInstance("Reflect.Buick");
        if(car != null){
            car.accelerate();
            car.brake();
        }
    }
}

刘丰璨 写于 2018.04.06 21:00 Github: https://github.com/EdwardLiu-Aurora

参考目录:

  1. 《Java 核心技术 卷 I 》
  2. 《Java 反射》
  3. 《利用反射机制实现工厂模式》
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容