一、概念 Java 反射(Reflection)就是 Java 程式在運行時可以載入一個才知道類名的類,獲得類的完整構造方法,並實例化出對象,給對象屬性設定值或者調用對象的方法。這種在運行時動態獲取類的信息以及動態調用對象的方法的功能稱為 Java 的反射機制。 二、Class 類 Class 類繼 ...
一、概念
Java 反射(Reflection)就是 Java 程式在運行時可以載入一個才知道類名的類,獲得類的完整構造方法,並實例化出對象,給對象屬性設定值或者調用對象的方法。這種在運行時動態獲取類的信息以及動態調用對象的方法的功能稱為 Java 的反射機制。
二、Class 類
Class 類繼承自 Object 類,是 Java 反射機制的入口,封裝了一個類或介面的運行時信息,通過調用 Class 類的方法可以獲取到這些信息。怎麼理解這個 Class 類呢?如果說普通類是所有對象方法、屬性的集合,那就可以把這個 Class 類理解成是所有普通類的集合。
下麵列舉了獲取 Class 類的幾種方法:
public class TestClass { public static void main(String[] args) throws ClassNotFoundException { // 1、 Class.forName(); Class<?> aClass0 = Class.forName("java.lang.Object"); // 2、類名.Class Class<Integer> aClass1 = Integer.class; // 3、包裝類.TYPE —— 返回基本類型的 Class 引用,基本類型在虛擬機運行時就已經載入了它的Class Class<Integer> aClass2 = Integer.TYPE; // 4、對象名.getClass() String str = "Hello, World"; Class<? extends String> aClass3 = str.getClass(); // 5、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()); } }
三、獲取類信息
為了測試 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
1、獲取方法
Class 類對方法的獲取主要通過以下兩種方式:
Method[] getMethods() 返回該類或介面的所有可訪問公共方法(含繼承的公共方法)。
Method[] getDeclaredMethods() 返回該類或介面的所有方法(不含繼承的方法)。
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()); } } }
2、獲取屬性
Class 類對屬性的獲取主要通過以下兩種方式:
Field[] getFields() :存放該類或介面的所有可訪問公共屬性(含繼承的公共屬性)。
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())); } } }
3、獲取構造函數
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(); } } }
四、動態調用
到目前為止,我們都是通過 Class 類的方法獲取對應類屬性、方法和構造函數的詳細信息。接下來我們將通過這些信息,來動態創建對象、修改屬性和動態調用方法。
public class Test { public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class<Car> carClass = Car.class; // 1、實例化對象 // 調用 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()); // 2、修改屬性 Field brand = carClass.getDeclaredField("brand"); brand.setAccessible(true); System.out.println("取消訪問許可權控制後的值:" + brand.get(car1)); brand.set(car1, "dnarb"); System.out.println("修改屬性後的值是:" + brand.get(car1)); // 3、調用方法 Method getBrand = carClass.getDeclaredMethod("getBrand"); getBrand.setAccessible(true); System.out.println("調用反射方法得到的值是:" + getBrand.invoke(car1)); } }