Java 反射機制
一、概念
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)); } }