JAVA核心技術學習筆記--反射

davidtim發表於2021-09-09

反射

反射庫(reflection library)提供了一個非常豐富且精心設計的工具集,以便編寫能夠動態操縱Java程式碼的程式。
使用反射,能在設計或者在新增新類的時候,能夠快速應用開發工具動態的查詢新新增類的能力。

能夠分析類能力的程式稱為 反射 反射功能極其強大可以用來

  • 在執行時分析類的能力

  • 在執行時檢視物件

  • 實現通用的陣列來操作程式碼

  • 利用Method物件,此物件很類似C++中的函式指標

1,前提介紹: Class 類

在程式執行期間,Java執行時系統始終為所有物件維護一個被稱為執行時的型別標識,這個資訊跟蹤著每個物件專屬的類。
Class類就是用來專門儲存這些類的資訊,透過Object類中的getClass()方法得到每個類的Class類例項

Person p;
Class cl = p.getClass();

一個Class類可以用來儲存一個類的屬性,如:

p.setName("ZhangSan");System.out.print(cl.getName()+" "+p.getName());

此時程式會輸出

Person ZhangSan

Class的getName()方法可以得到這個類的類名。

獲得類的Class例項的第2種方法:Class.forName(類名)

String className = "com.henu.Person";
Class cl = Class.forName(className);

第三種方法,直接 類名.class

Class class1 = Person.class;
Class class2 = Random.class;

透過獲得的Class例項再建立類的例項

Class cl = p.getClass();
Perons p = cl.newInstance();

以上程式碼在使用時要放在try catch程式碼塊裡,來處理捕獲的異常


2,利用反射分析類的能力,檢查類的結構

java.lang.reflect包中有三個類

  • Field 描述類的域  getType()方法可以返回描述域所屬型別的Class物件

  • Method 描述類的方法

  • Constructor 描述類的構造器

這三個類都有一個getModifiers的方法,描述各自的訪問修飾符,public private final...等,
再透過Modifier類中的isPublic,isPrivate,isXxx方法對其進行判斷.

  • getFields

  • getMethods

  • getConstructors
    這三個方法分別返回類提供的public域,方法,構造器陣列,其中包括超類的共有成員.

  • getDeclareFields

  • getDeclareMethods

  • getDeclaredConstructors
    這三個方法將返回類中宣告的全部域,方法和構造器,包括private,protect,但不包括超類的共有成員

在執行時使用反射分析物件

接下來進一步檢視資料域的實際內容給

Person p = new Person("張三",20);
Class cl = p.getClass();
Field name = cl.getDeclaredField("name");//獲得Person 類的name域;Object value = name.get(p); // 此時value = "張三"

值的注意的一點是,若name域在Person類中是私有的,程式碼會丟擲異常,我們需要對獲得的資料域進行設定

Person p = new Person("張三",20);
Class cl = p.getClass();
Field name = cl.getDeclaredField("name");//獲得Person 類的name域;name.setAccessible(true);Object value = name.get(p); // 此時value = "張三"

setAccessible方法是AccessibleObject類中的一個方法,它是Field,Method,Constructor類的共有超類,是為了除錯,持久儲存和相似除錯提供的.

若資料域是基本資料型別,可以使用域.getDouble(類的例項),getInt(類的例項),也可以繼續使用get( )方法,反射機制會自動的將這個域值裝箱,變成Double,Integer。

.set(obj,value)方法可以將obj物件的指定域進行賦值

name.set(p,"李四")//此時p物件的name為李四

3,使用反射編寫泛型陣列程式碼

java.lang.reflect包中的Array類允許動態的建立陣列
以前我們學過Arrays類中有個copyOf()方法,可以用來擴充已經滿了的陣列.

Person[ ] team = new Person[10];

team = Arrays.copyOf(team,2*team.length);

我們可以用反射寫一個通用的擴充陣列長度的方法,能擴充任意型別的陣列。
首先我們寫一個錯誤的例子

public static Object[] badCopyOf(Object[] a,int newLength) {    Object[] newArray = new Object[newLength];
    System.arrayCopy(a,0,newArray,0,Math.min(a.length,newLength));//第四個引數是要從原陣列複製元素的個數
    return newArray;
}

為什麼這個程式碼是錯的呢,因為這個函式返回的陣列型別是物件陣列型別(Object [])
一個物件陣列不能轉化成特定的類的陣列(Object[]->person[]),這樣做會產生異常,其實,當我們把Person[]轉化成 Object[] 再轉化成Person[]是可以的,但是一個一開始就是Object[]的陣列是不能轉的,我們可以透過下面這個方法

Object newArray = Array.newInstance(componentType,int newLength);

java.lang.reflect.Array中的靜態方法newInstance方法能夠構造新的陣列,第一個引數是元素的型別,第二個是陣列長度。所以現在我們需要獲得新陣列的元素型別和陣列長度。

  • 獲得陣列的長度可以透過.getLength()或者Array.getLength(陣列)獲得

  • 獲得陣列的元素型別可以透過Class類的getComponentType()獲得

所以我們現在要實現一個通用的擴充任意型別的方法要經過3步

  1. 首先獲得陣列的Class物件

  2. 確認它是一個陣列型別(透過Class類的isArray()方法)

  3. 使用Class類的getComponentType()方法確定陣列的型別

public static Object goodCopyOf(Object a,int newLength) {
    Class cl = p.getClass();    if (cl.isArray()) return null;//如果不是陣列型別就結束
    
    Class componentType = cl.getComponentType();//獲得陣列的元素型別
    int length = a.getLength();    Object newArray = Array.newInstance(componentType,length);
    System.arrayCopy(a,0,newArray,0,Math.min(length,newLength));    return newArray;
}

這樣這個方法就可以用了來擴充任意型別的陣列了

int[] a = {1,2,3,4,5};
a = (int [])goodCopyOf(a,10);

透過Method的invoke方法呼叫類的任意方法

兩個方法
Method method = Person.getClass().getMethod(方法名,方法的引數型別.class)
method.invoke(方法隸屬的類的例項,方法的引數值)

//動態構造InvokeTest類的例項Class<?> classType = InvokeTest.class;Object invokeTest = classType.newInstance();//動態構造InvokeTest類的add(int num1, int num2)方法,標記為addMethod的Method物件Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});//動態構造的Method物件invoke委託動態構造的InvokeTest物件,執行對應形參的add方法Object result = addMethod.invoke(invokeTest, new Object[]{1, 2});



作者:Limmerence
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3349/viewspace-2816815/,如需轉載,請註明出處,否則將追究法律責任。

相關文章