什麼是反射?
反射 (Reflection) 是 Java 的特徵之一,它允許執行中的 Java 程式獲取自身的資訊,並且可以操作類或物件的內部屬性 簡而言之,通過反射,我們可以在執行時獲得程式或程式集中每一個型別的成員和成員的資訊。程式中一般的物件的型別都是在編譯期就確定下來的,而 Java 反射機制可以動態地建立物件並呼叫其屬性,這樣的物件的型別在編譯期是未知的。所以我們可以通過反射機制直接建立物件,即使這個物件的型別在編譯期是未知的。 反射的核心是 JVM 在執行時才動態載入類或呼叫方法/訪問屬性,它不需要事先(寫程式碼的時候或編譯期)知道執行物件是誰。 Java 反射主要提供以下功能:
- 在執行時判斷任意一個物件所屬的類;
- 在執行時構造任意一個類的物件;
- 在執行時判斷任意一個類所具有的成員變數和方法(通過反射甚至可以呼叫private方法);
- 在執行時呼叫任意一個物件的方法 重點:是執行時而不是編譯時
反射的運用
獲得 Class 物件
- 使用 Class 類的
forName
靜態方法
public static Class<?> forName(String className)
複製程式碼
- 直接獲取某一個物件的 class
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;
複製程式碼
- 呼叫某個物件的
getClass()
方法
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();
複製程式碼
判斷是否為某個類的例項
一般地,我們用 instanceof
關鍵字來判斷是否為某個類的例項。同時我們也可以藉助反射中 Class 物件的 isInstance()
方法來判斷是否為某個類的例項,它是一個 native 方法:
public native boolean isInstance(Object obj);
複製程式碼
建立例項
通過反射來生成物件主要有兩種方式。
- 使用Class物件的newInstance()方法來建立Class物件對應類的例項。
Class<?> c = String.class;
Object str = c.newInstance();
複製程式碼
- 先通過Class物件獲取指定的Constructor物件,再呼叫Constructor物件的newInstance()方法來建立例項。這種方法可以用指定的構造器構造類的例項。
//獲取String所對應的Class物件
Class<?> c = String.class;
//獲取String類帶一個String引數的構造器
Constructor constructor = c.getConstructor(String.class);
//根據構造器建立例項
Object obj = constructor.newInstance("23333");
System.out.println(obj);
複製程式碼
獲取構造器資訊
得到構造器的方法
Constructor getConstructor(Class[] params) -- 獲得使用特殊的引數型別的公共建構函式,
Constructor[] getConstructors() -- 獲得類的所有公共建構函式
Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定引數型別的建構函式(與接入級別無關)
Constructor[] getDeclaredConstructors() -- 獲得類的所有建構函式(與接入級別無關)
複製程式碼
獲取類構造器的用法與上述獲取方法的用法類似。主要是通過Class類的getConstructor方法得到Constructor類的一個例項,而Constructor類有一個newInstance方法可以建立一個物件例項:
public T newInstance(Object ... initargs)
複製程式碼
獲取類的成員變數(欄位)資訊
獲得欄位資訊的方法
Field getField(String name) -- 獲得命名的公共欄位
Field[] getFields() -- 獲得類的所有公共欄位
Field getDeclaredField(String name) -- 獲得類宣告的命名的欄位
Field[] getDeclaredFields() -- 獲得類宣告的所有欄位
複製程式碼
呼叫方法
獲得方法資訊的方法
Method getMethod(String name, Class[] params) -- 使用特定的引數型別,獲得命名的公共方法
Method[] getMethods() -- 獲得類的所有公共方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的引數型別,獲得類宣告的命名的方法
Method[] getDeclaredMethods() -- 獲得類宣告的所有方法
複製程式碼
當我們從類中獲取了一個方法後,我們就可以用 invoke()
方法來呼叫這個方法。invoke
方法的原型為:
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
複製程式碼
利用反射建立陣列
陣列在Java裡是比較特殊的一種型別,它可以賦值給一個Object Reference 其中的Array類為java.lang.reflect.Array類。我們通過Array.newInstance()建立陣列物件,它的原型是:
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
複製程式碼
而 newArray
方法是一個 native 方法,它在 HotSpot JVM 裡的具體實現我們後邊再研究,這裡先把原始碼貼出來:
private static native Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException;
複製程式碼
注意事項
- 反射會額外消耗一定的系統資源,因此如果不需要動態地建立一個物件,那麼就不需要用反射
- 反射呼叫方法時可以忽略許可權檢查,因此可能會破壞封裝性而導致安全問題
最後
讀到這的朋友覺得不錯可以點贊關注下,感謝您的支援,以後會不停更新更多精選乾貨及資訊分享,歡迎大家在評論區留言討論!
歡迎關注享學課堂online微信公眾號,每天會持續更新技術乾貨,熱點,吐槽等文章,還有免費的Android架構視訊資料和麵試專題資料免費領取分享,後臺回覆關鍵字【Android資料】,免費獲取Android架構面試專題文件資料、電子書及更多架構進階視訊資料(視訊+筆記)