1. 程式人生 > >Java反射由淺入深全面解析

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例項都指向了一個數據型別(classinterface)。


一個 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例項時,我們可以獲取該Objectclass資訊。
  • Class例項判斷class型別。
Runnable.class.isInterface(); //true
String[].class.isArray(); //true
  • 利用JVM動態載入class的特性可以在執行其根據條件載入不同的實現類。
//commons  Logging優先使用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()方法可以判斷一個向上轉型是否正確。