今天看到了有關Java運行時方面的一個方法getSomeValue(String propertyName,Object obj),大體上功能是根據一個對象的欄位名稱和該對象去獲得該欄位實際運行時的值。裡面除了一些邏輯操作外,主要是用到了java.lang.reflect.Method類的invoke(Object obj, Object... args)方法,看了一下jdk的說明,還是一頭霧水,決定自己寫個小例子研究一下~
首先簡單介紹一下Java的運行時機制,程式運行時,java運行時系統一直對所有的對象進行所謂的運行時類型識別(RTTI),以確定每個對象所屬的類。JVM通常使用運行時類型資訊來選擇正確的方法去執行 。
*在java中,儲存運行時類型資訊的類叫Class類。
*對於每一個被裝載的類型(不管是類還是介面), JVM都會相應地為它建立一個Class對象。在你的應用程式中,你可以得到並使用指向Class對象的引用。
*在運行期,一旦我們想產生一個類對象,JVM首先就會檢查那個類型的Class對象是否已經載入。若尚未載入,JVM就會尋找同名的.class檔案,並將其載入。所以Java程式啟動時並不是完全載入的,這一點與許多傳統語言都不同。 一旦那個類型的Class對象進入記憶體,就用它建立那一類型的所有對象。
我們通常會用getClass(),Class.forName(),obj.class等等方法來操作運行時類,還有一個機制就是反射了,下面來看
如果不知道一個對象的準確類型,RTTI會協助我們調查。但卻有一個限制:類型必須是在編譯期間已知的。
而反射使我們能在運行期間探察一個類,RTTI和“反射”之間唯一的區別就是:對RTTI來說,編譯器會在編譯期開啟和檢查.class檔案。但對“反射”來說,.class檔案在編譯期間是不可使用的,而是由運行時環境開啟和檢查 ,我們利用反射機制一般是使用java.lang.reflect包提供給我們的類和方法
開始轉向正題,寫一個簡單的例子來學習掌握Invoke方法
[code][br]1 public class Player [br]2 { [br]3 protected Object getSomeValue(String propertyName,Object obj) throws NoSuchMethodException [br]4 { [br]5 Object rtnVal=null; [br]6 try [br]7 { [br]8 Method method=obj.getClass().getMethod("get"+Character.toUpperCase(propertyName.charAt(0))+propertyName.substring(1) ); [br]9 rtnVal=method.invoke(obj); [br]10 } [br]11 catch(NoSuchMethodException e) [br]12 { [br]13 throw e; [br]14 } [br]15 catch(Exception ex) [br]16 { [br]17 ex.printStackTrace(); [br]18 } [br]19 return rtnVal; [br]20 } [br]21 }[/code]
方法的2個參數,一個是欄位的名稱,第二個就是你要獲得對應欄位值的運行時對象了,首先定義了1個Object類型傳回值rtnVal,然後通過getClass(),getMethod()方法獲得對應欄位的get方法,括弧裡面的字串操作就是是為了這個,這時,關鍵的來了,我們調用了剛才獲得method對象的invoke(obj)方法,obj是你需要定位的運行時對象,後面Object... args省略了,因為get欄位的方法不帶參數嘛,所以這樣寫
接著我們從Player類派生出一個類Runner來測試上面的方法
[code][br]1 class Runner extends Player [br]2 { [br]3 private String playingType="400m"; [br]4 [br]5 [br]6 public String getPlayingType() { [br]7 return playingType; [br]8 } [br]9 [br]10 [br]11 public void runnerTest(Object obj) throws NoSuchMethodException [br]12 { [br]13 System.out.println(getSomeValue("playingType", obj)); [br]14 } [br]15 }[/code]
我們設定了1個欄位表示跑步運動員參加的比賽項目,然後相應產生了get方法,然後有個測試方法,輸出欄位的值,補充一點,上面的getSomeValue方法我們讓他拋出了NoSuchMethod異常,所以在主方法調用時注意捕獲
接下來可以在Player類中寫個主方法來測試
[code][br]1 public static void main(String[] args) [br]2 { [br]3 Runner r=new Runner(); [br]4 try [br]5 { [br]6 r.runnerTest(r); [br]7 } [br]8 catch(Exception e) [br]9 { [br]10 e.printStackTrace(); [br]11 } [br]12 }[/code]
r是我們運行時建立的對象,然後運行測試的方法中也傳入了運行時對象r,輸出了對應欄位的值"400m",當然,為了證實這一點,我們也可以為Runner類寫個帶playingType欄位的建構函式來運行時改變它的值,輸出也會是你當前建立對象的即時值
小結一下,invoke(Object obj, Object... args)這個方法就是調用此 Method對象表示的底層方法,
如果底層方法是靜態,那麼可以忽略指定的 obj 參數。該參數可以為 null。
如果底層方法所需的形參數為 0,則所提供的 args 數組長度可以為 0 或 null。
如果底層方法是執行個體方法,則使用動態方法尋找來調用它,
如果底層方法是靜態,並且尚未初始化聲明此方法的類,則會將其初始化。