程式語言中的各種反射
在最剛開始接觸設計模式的時候,那時候還是用的vb.net,對於反射只是懵懵懂懂,只知道把dll放到指定地方,就可以通過Assembly載入並獲取裡面的類。對於那個階段的反射,只是停留在了類的層次。
後來,學習了JavaScript,這門指令碼語言,是弱型別、基於面向物件的(到現在依然還是懵懵懂懂)。這個裡面的反射,確切的說,JavaScript沒有完整的反射體系。但是在編碼中可以通過程式碼設計來完成類似反射的基本功能。還是比較簡單的吧。
再後來,就是現在了,接觸了Java的反射。可以說真正接觸反射,就是從這裡開始的。
說到底,反射到底是什麼?我們來看看百度百科怎麼說:程式執行時,允許改變程式結構或變數型別,這種語言稱為動態語言。Java不是動態語言,但Java有非常突出的動態相關機制,可以載入一個執行時才知名稱的class,獲悉其完整構造,並生成其物件實體、或對其fields設值、或喚起其methods。
而對fields、methods的使用可以遍歷,可以通過字串進行指定,這也就使得不同類,可以呼叫相同的方法。例如:我現在有一個方法,接收一個Object型別的引數,我確定傳進來的Object都會有一個Id的屬性,我要獲取這個屬性,並對其進一步操作。
現在該怎麼辦?在學了今天這篇博文後,你可以用commons-beanutils.jar包裡面的BeanUtils和PropertyUtils工具類。
這個方法,只能獲取到指定類自身內部的屬性,對於父類裡面的屬性,是獲取不到的
更多的時候,如果說傳入的Object都會有,那麼我們一般考慮將其抽象到父類裡,那麼上面的方法就不適合了。下面這個方法,可以獲取自身及父類裡面的所有屬性,思路其實很簡單,就是在當前類裡面獲取該欄位,如果沒有,就去其父類裡面獲取:public Object getFieldValue(Object object, String propertyName) throws IllegalAccessException, NoSuchFieldException { //獲取Object的指定property Field field = object.getClass().getDeclaredField(propertyName); //設定其可見性,若不設定,則無法獲取其值,如果獲取的屬性為public,則可以不加這句 field.setAccessible(true); return field.get(object); }
屬性反射完了,那麼現在來看一下方法應該怎樣動態呼叫呢?我只列一個不帶引數的方法呼叫,需要傳引數的同學自己研究一下~~public Object getFieldValueInAllSuper(Object object, String propertyName) throws IllegalAccessException, NoSuchFieldException { //獲取當前Object的class Class claszz=object.getClass(); Field field = null; do{ try{ //從類裡面獲取指定屬性 field = claszz.getDeclaredField(propertyName); } catch(NoSuchFieldException e){//如果沒有獲取到,則設定為null field=null; } //設定當前class為父class claszz=claszz.getSuperclass(); } while(field==null&&claszz!=null); //當field為空且class不為空時,進行下次迴圈 //如果field為空,說明沒有此欄位,返回空 if(field==null) return null; //如果不為空,設定可見性,然後返回 field.setAccessible(true); return field.get(object); }
private String getId(Object o) throws IllegalArgumentException, SecurityException,
IllegalAccessException, InvocationTargetException, NoSuchMethodException {
//從當前類及父類中找
return (String) o.getClass().getMethod("getId").invoke(o);
//只查詢當前類
return (String)o.getClass().getDeclaredMethod("getId").invoke(o);
}
屬性、方法反射完了,那麼應該怎樣去動態的載入一個類呢?這裡就只提一下:用Class.forName()。其實我感覺這裡用的比較多的是傳入介面型別,然後獲取介面的class當作引數。
最近這些日子,對於泛型、反射用的比以前多了很多,也體會到了它們的用處。最近的部落格,應該都會和這些有關,因為封裝,如果離了這兩個,真心感覺那將是惡夢~~