Java 反射機制

1、概念

    Java 反射(Reflection)就是 Java 程序在運行時能夠加載一個才知道類名的類,得到類的完整構造方法,並實例化出對象,給對象屬性設定值或者調用對象的方法。這種在運行時動態獲取類的信息以及動態調用對象的方法的功能稱爲 Java 的反射機制。java

2、Class 類

    Class 類繼承自 Object 類,是 Java 反射機制的入口,封裝了一個類或接口的運行時信息,經過調用 Class 類的方法能夠獲取到這些信息。怎麼理解這個 Class 類呢?若是說普通類是全部對象方法、屬性的集合,那就能夠把這個 Class 類理解成是全部普通類的集合。ide

    下面列舉了獲取 Class 類的幾種方法:函數

public class TestClass {
    
    public static void main(String[] args) throws ClassNotFoundException {
        // 一、 Class.forName();
        Class<?> aClass0 = Class.forName("java.lang.Object");
        // 二、類名.Class
        Class<Integer> aClass1 = Integer.class;
        // 三、包裝類.TYPE —— 返回基本類型的 Class 引用,基本類型在虛擬機運行時就已經加載了它的Class
        Class<Integer> aClass2 = Integer.TYPE;
        // 四、對象名.getClass()
        String str = "Hello, World";
        Class<? extends String> aClass3 = str.getClass();
        // 五、Class類.getSuperClass() —— 得到父類的 Class 對象
        Class<?> aClass4 = aClass3.getSuperclass();

        System.out.println(aClass0.getName());
        System.out.println(aClass1.getName());
        System.out.println(aClass2.getName());
        System.out.println(aClass3.getName());
        System.out.println(aClass4.getName());
    }
}
  1. aClass.isPrimitive(); //判斷 aClass 是否爲基本數據類型
  2. aClass.isAssignableFrom(ArrayList.class);  //判斷 aClass 是不是 ArrayList 類的父類
  3. aClass.getGenericType(); //獲得泛型類型

3、獲取類信息

    爲了測試 Java 的反射機制,我新建了一對父子類,其中涵蓋了四種封裝屬性,以儘量的測試多種類信息的獲取:測試

vpublic class Vehicle {

    private String color;
    protected Integer seat;
    int year;
    public Date createdOn;

    private String getColor() {
        return color;
    }

    protected Integer getSeat() {
        return seat;
    }

    int getYear() {
        return year;
    }

    public Date getCreatedOn() {
        return createdOn;
    }
}
Vehicle.java
public class Car extends Vehicle {

    private String brand;
    protected Integer a;
    int b;
    public Date updatedOn;

    public Car(){}

    private Car(String brand, Integer a, int b, Date updatedOn) {
        this.brand = brand;
        this.a = a;
        this.b = b;
        this.updatedOn = updatedOn;
    }

    private String getBrand() {
        return brand;
    }

    protected Integer getA() {
        return a;
    }

    int getB() {
        return b;
    }

    public Date getUpdatedOn() {
        return updatedOn;
    }
}
Car.java

    一、獲取方法

        Class 類對方法的獲取主要經過如下兩種方式:this

    Method[] getMethods() 返回該類或接口的全部可訪問公共方法(含繼承的公共方法)。spa

    Method[] getDeclaredMethods() 返回該類或接口的全部方法(不含繼承的方法)。code

public class TestMethod {

    public static void main(String[] args) {
        Class<Car> carClass = Car.class;
        Method[] methods = carClass.getMethods();
        Method[] declaredMethods = carClass.getDeclaredMethods();

        for (Method method : methods) {
        //for (Method method : declaredMethods) {
            System.out.println("方法名:" + method.getName());
            System.out.println("該方法所在的類或接口:" + method.getDeclaringClass());
            System.out.println("該方法的參數列表:" + method.getParameterTypes());
            System.out.println("該方法的異常列表:" + method.getExceptionTypes());
            System.out.println("該方法的返回值類型:" + method.getReturnType());
        }
    }
}

    二、獲取屬性

        Class 類對屬性的獲取主要經過如下兩種方式:對象

    Field[] getFields() :存放該類或接口的全部可訪問公共屬性(含繼承的公共屬性)。blog

    Field[] getDeclaredFields():存放該類或接口的全部屬性(不含繼承的屬性)。繼承

public class TestField {

    public static void main(String[] args) {
        Class<Car> carClass = Car.class;
        Field[] fields = carClass.getFields();
        Field[] declaredFields = carClass.getDeclaredFields();
        //for (Field field : fields) {
        for (Field field : declaredFields) {
            System.out.println("屬性名稱是:" + field.getName());
            System.out.println("該屬性所在的類或接口是:" + field.getDeclaringClass());
            System.out.println("該屬性的類型是:" + field.getType());
            // field.getModifiers() 以整數形式返回由此 Field 對象表示的屬性的 Java 訪問權限修飾符
            System.out.println("該屬性的修飾符是:" + Modifier.toString(field.getModifiers()));
        }
    }
}

    三、獲取構造函數

         Class 類對構造方法的獲取主要經過如下兩種方式:

    Constructor<?>[] getConstructors() :返回該類或接口的全部的公共構造方法

    Constructor<?>[] getDeclaredConstructors():返回該類或接口的全部構造方法

public class TestConstructor {

    public static void main(String[] args) throws NoSuchMethodException {
        Class<Car> carClass = Car.class;
        Constructor<?>[] constructors = carClass.getConstructors();
        Constructor<?>[] declaredConstructors = carClass.getDeclaredConstructors();
        Constructor<Car> carConstructor = carClass.getDeclaredConstructor(String.class, Integer.class, Integer.TYPE, Date.class);

        //for (Constructor constructor : declaredConstructors) {
        for (Constructor constructor : constructors) {
            System.out.println("該構造器的名稱是:" + constructor.getName());
            System.out.println("該構造器所在的類或接口是:" + constructor.getDeclaringClass());
            //返回構造方法的參數類型
            constructor.getParameterTypes();
        }
    }
}

4、動態調用

    到目前爲止,咱們都是經過 Class 類的方法獲取對應類屬性、方法和構造函數的詳細信息。接下來咱們將經過這些信息,來動態建立對象、修改屬性和動態調用方法。

public class Test {

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class<Car> carClass = Car.class;
        // 一、實例化對象
        // 調用 Class 類的newInstance();要求對應類必須有無參構造函數,至關於 Car car = new Car()
        Car car = carClass.newInstance();
        // 調用構造器的newInstance(Object ... initargs);
        Constructor<Car> declaredConstructor = carClass.getDeclaredConstructor(String.class, Integer.class, Integer.TYPE, Date.class);
        // 取消訪問權限控制,即便是 private 權限也能夠訪問
        declaredConstructor.setAccessible(true);
        Car car1 = declaredConstructor.newInstance("brand", 21, 21, new Date());
        System.out.println(car1.getUpdatedOn());

        // 二、修改屬性
        Field brand = carClass.getDeclaredField("brand");
        brand.setAccessible(true);
        System.out.println("取消訪問權限控制後的值:" + brand.get(car1));
        brand.set(car1, "dnarb");
        System.out.println("修改屬性後的值是:" + brand.get(car1));

        // 三、調用方法
        Method getBrand = carClass.getDeclaredMethod("getBrand");
        getBrand.setAccessible(true);
        System.out.println("調用反射方法獲得的值是:" + getBrand.invoke(car1));
    }
}
相關文章
相關標籤/搜索