淺談反射

huizhe發表於2019-02-28

反射機制作用

  1. 在執行時分析類的能力
  2. 在執行時檢視物件
  3. 實現通用的陣列操作程式碼

反射機制的實現

採用Class類和java.lang.reflect 類庫一起實現 <反射>機制

  1. Class 類: 代表一個目標類。
  2. Field 類: 代表目標類的成員變數。
  3. Method 類: 代表目標類的方法。
  4. Constructor 類: 代表目標類的構造方法。
  5. Array 類: 提供了動態建立陣列,以及訪問陣列的元素的靜態方法。

Class 類

​ 在程式執行期間,Java執行時系統始終為所有的物件維護一個被稱為執行時的型別表示。這個資訊跟蹤著每個物件所屬的類。虛擬機器利用執行時型別資訊選擇相應的方法執行。

​ 然而,可以通過專門的Java類訪問這些資訊。儲存這些資訊的類被成為Class,這個名字很容易讓人混淆。Object 類中的getClass() 方法將返回一個Class型別的例項。

獲取Class物件的三種方法

  • 如果類在一個包裡,包的名字也作為做類名的一部分。
Random generator = new Random();
Class cl = generator.getClass();
System.out.println(cl.getName());//java.util.Random
複製程式碼
  • 如果類名儲存字串中,並可在執行時該變,即可使用這種方法。forName() 方法會爆ClassNotFoundException 異常,所以需要進行異常處理。

    Class.forName() 內部通過反射API根據目標類名將類手動載入到記憶體中,稱為類載入器載入方法。載入過程中會把目標類的static方法,變數,程式碼塊載入到JVM,注意此時尚未建立物件例項

 String className = "java.util.Random";
 Class cl = class.forName(className);
複製程式碼
  • 如果T是任意的Java型別(或void 關鍵字),T.class 將代表匹配的類物件
 Class cl = Random.class;
複製程式碼

獲取構造器–Constructor 類

  • Constructor[] getConstructors():獲得所有public構造器;
  • Constructor[] getDeclaredConstructors():獲得所有訪問許可權的構造器
  • Constructor getConstructor(Class[] params):根據指定引數獲得對應構造器;
  • Constructor getDeclaredConstructor(Class[] params):根據指定引數獲得對應構造器;

獲得方法–Method 類

  • Method[] getMethods():獲得所有public方法;
  • Method[] getDeclaredMethods():獲得所有訪問許可權的方法;
  • Method getMethod(String name, Class[] params):根據方法簽名獲取類自身對應public方法,或者從基類繼承和介面實現的對應public方法
  • Method getDeclaredMethod(String name, Class[] params):根據方法簽名獲得對應的類自身宣告方法訪問許可權不限

獲得變數–Field 類

  • Field[] getFields():獲得類中所有public變數
  • Field[] getDeclaredFields():獲得類中所有訪問許可權變數
  • Field getField(String name):根據變數名得到對應的public變數
  • Field getDeclaredField(String name):根據變數名獲得對應的變數,訪問許可權不限

實戰

我們來個例子加深一下印象。

列印一個類的所有建構函式

public static void printConstructors(Class cl)
   {
       //返回包含Constructor 物件的陣列,其中包含了Class物件的所有構造器
       Constructor[] constructors = cl.getDeclaredConstructors();

       for (Constructor c: constructors) {
           //String getName()
           //返回一個用於描述構造器、方法或域名的字串
           String name = c.getName();
           System.out.print("    ");
           String modifiers = Modifier.toString(c.getModifiers());
           if (modifiers.length()>0)
               System.out.print(modifiers+ "  ");
           System.out.print(name+"(");
           //Class[] getParameterTypes() (在Constructor 和 Method 類中)
           //返回一個用於描述引數型別的Class物件陣列
           printParameterType(c.getParameterTypes());
           System.out.println(");");
       }
   }
複製程式碼

列印一個類的所有方法

public static void printMethods(Class cl)
   {
       //返回包含Method 物件的陣列,返回這個類或介面的全部方法,但不包括由超類繼承了的方法
       Method[] methods = cl.getDeclaredMethods();
       for (Method m:methods) {
           //Class getReturnType() (在 Method 類中)
           //返回一個用於描述返回型別的Class物件
           Class retType = m.getReturnType();
           String name = m.getName();

           System.out.print("    ");
           //列印修飾符、返回型別和方法名稱
           String modifiers =Modifier.toString(m.getModifiers());
           if (modifiers.length()>0)
               System.out.print(modifiers+" ");
           System.out.print(retType.getName()+" "+ name + "(");

           printParameterType(m.getParameterTypes());
           System.out.println(");");
       }
   }
複製程式碼

列印一個類的所有欄位

public static void printFields(Class cl)
   {
       //Field[] getDeclaredFields()
       //返回包含Field 物件的陣列,這些物件記錄了這個類的全部域
       Field[] fields = cl.getDeclaredFields();

       for (Field f : fields){
           Class type = f.getType();
           String name = f.getName();
           System.out.print("    ");
           String modifiers = Modifier.toString(f.getModifiers());
           if (modifiers.length()>0)
               System.out.print(modifiers+"  ");
           System.out.println(type.getName()+" "+ name+ ";");
       }
   }
複製程式碼

列印方法的引數型別

public static void printParameterType(Class[] paramTypes)
   {
       for (int j = 0;j<paramTypes.length;j++) {
           if (j>0)
               System.out.print(", ");
           System.out.print(paramTypes[j].getName());
       }
   }
複製程式碼

測試

public static void main(String[] args)
   {
       String name;
       if (args.length>0)
           name = args[0];
       else {
           Scanner in = new Scanner(System.in);
           System.out.println("Enter class name(e.g java.util.Date): ");
           name = in.next();
       }
       try {
           //print class name and superclass name (if != object)
           //呼叫Class 的靜態方法 forName 獲得類名對應的Class 物件
           Class cl = Class.forName(name);
           //獲取父類所對應的Class 物件
           Class supercl = cl.getSuperclass();
           //返回對應modifiers 中位設定的修飾符的字串表示
           String modifiers = Modifier.toString(cl.getModifiers());

           if (modifiers.length()>0)
               System.out.print(modifiers+"  ");
           System.out.print("class "+ name);
           //判斷是否有繼承父類
           if (supercl != null  && supercl != Object.class)
               System.out.print("  extends "+ supercl.getName());

           System.out.print("
{
");
           printConstructors(cl);
           System.out.println();
           printMethods(cl);
           System.out.println();
           printFields(cl);
           System.out.println("}");


       }catch (ClassNotFoundException ex) {
           ex.printStackTrace();
       }
       System.exit(0);
   }
複製程式碼

參考

JAVA核心技術(卷1)原書第10版

反射筆記