Java反射由淺入深
分享一下學習反射的筆記,瞭解反射之前要先了解一下java的class類。
1. Class類
- Java除了基本型別外其它都是
Class
類(包括了interface
)。
String s = "Hello"; Runnable runnable = new Thread(); .......
- Class(包括
interface
)的本質是資料型別(Type);
一個物件的例項,賦值給一個數據型別變數的時候,嚴格按照資料型別來賦值。
- 無繼承關係的資料型別無法賦值。
-
class/interface
的資料型別是Class
。
每載入一個class
,JVM
為其建立一個Class
型別的例項,並關聯起來。
public final class Class{ //Class例項是JVM內部建立的 pirvate Class () {} }
JVM
在載入 Stirng
類的時候,讀取 String.class
檔案,為 String
類建立一個 Class
例項, Class class = new Class(Stirng);
。
JVM
持有的每個 Class
例項都指向了一個數據型別( class
或 interface
)。

一個 Class
例項包含了該 class
的完整資訊。

-
JVM
為每個載入的class
建立對應的Class
例項,並在例項中儲存了該class
的所有信息。 - 如果獲取了某個
Class
例項,則可以獲取到該例項對應的class
的所有資訊。 - 通過
CLass
例項獲取class
資訊的方法稱為反射(Refection)。
//獲取一個class的Class例項 Class clas = String.class; String s = "hello"; Class cals = s.getClass(); Class class = Class.forName("java.lang.String");
-
Class
例項在JVM
中是唯一的:可以用==比較兩個
Class
例項。
Class cla1 = String.class; String s = "hello"; Class cal2 = s.getClass(); Class cla3 = Class.forName("java.lang.String"); boolean b1 = cls1 ==cls2; // true boolean b1 = cls2 ==cls3; // true
-
Class
例項比較和instanceof
的差別;用
instanceof
比較的時候,不但匹配當前型別,還匹配當前型別的子類。==
只匹配當前型別。通常情況下用instanceof
判斷是資料型別,只有精確判斷某個例項是否是某個型別的時候才用==
來判斷。
Integer n = new Integer(123); boolean b1 = n instanceof Integer ; // true boolean b2 = n instanceof Number;// true boolean b3 = n.getClass() == Integer.class;// true boolean b4 = n.getClass() == Number.class;// false
- 反射的目的是獲得某個
Object
例項時,我們可以獲取該Object
的class
資訊。 - 從
Class
例項判斷class
型別。
Runnable.class.isInterface(); //true String[].class.isArray(); //true
- 利用
JVM
動態載入class
的特性可以在執行其根據條件載入不同的實現類。
//commonsLogging優先使用Log4j LogFactFactory factory; if (isClassPresent("org.apache.logging.log4j.Logger")) { factory = createLog4j(); } else { factory = createJdkLog(); } boolean isClassPresent(String name) { try { Class.forName(name); return true; } cath (Exception e) { return false; } }
2. 訪問欄位(field)
- 通過
Class
例項獲取field
資訊:
getField(name):獲取某個public的field(包括父類)。
getDeclaredField(name):獲取當前的某個類的field(不包括父類)。
gerFields:獲取所有public的field(包括父類)。
getDeclaredFileds():獲取當前類的所有filed(不包括父類)。 - Filed物件包含一個field的所有資訊:
getName(); getType(); getModifiers()
; - 獲取一個filed值:
get(Object)
獲取一個例項的該欄位的值。 - 設定一個filed值:
set(Object)
設定一個例項的該欄位的值。 - 通過
setAccessible(true)
來訪問非public欄位。
注意: 設定setAccessible(true)的時候可以訪問private欄位 。但是這個方法可能會失敗。如果定義了SecurityManManager,它的規則阻止了對該field設定Accessible就會丟擲異常 。例如:把規則應用於所有的java和javax開頭的package的類,那麼對於java的核心類就不能訪問它們的private欄位。通常情況下自己寫的類和第三方的類是沒有這個限制的。
3. 呼叫方法(method)
- 通Clsss例項獲取methood資訊:
getMethod(...):獲取某個public的method(包括父類)。
getDeclaredMethod(...):獲取當前類的某個method(不包括父類)。
getMethods():獲取所有public的method(包括父類)。
getDeclaredMethods():獲取當前類的所有method (不包括父類)。 - Method物件包含一個method的所有資訊:
getName():返回一個名稱。
getReturnType():返回一個型別。
getParParameterTypesTypes():返回一個引數型別。
getModifiers():返回方法的修飾符。 - 呼叫無引數的Method
Object invoke(Object obj)
Integer n = new Integer(123); Class cls = n.getClass(); Method m = cls.getMethod("toString"); String s = (String) m. invoke(n); //"123",相當於String s = n.toString();
-
呼叫有引數Method
Object invoke(Object obj,Object...args)
Integer n = new Integer(123); Class cls = n.getClass(); Method m = cls.getMethod("compareTo",Intefer.class); int i = (Integer) m. invoke(n,456);//相當於int i = n.toCompareTo(456);
4. 獲取繼承關係
-
獲取父類的Class:
Class getSuperclass()
Object的父類是null
interface的父類是null
Class sup = Integer.class.getSuperclass();// Number.class
- getInterface();
- 通過Class物件的isAssignableFrom()方法可以判斷一個向上轉型是否正確。