JAVA反射機制定義培訓資料_第1頁
JAVA反射機制定義培訓資料_第2頁
JAVA反射機制定義培訓資料_第3頁
JAVA反射機制定義培訓資料_第4頁
JAVA反射機制定義培訓資料_第5頁
已閱讀5頁,還剩41頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、Good is good, but better carries it.精益求精,善益求善。JAVA反射機制定義-JAVA反射機制定義:JAVA反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法;這種動態(tài)獲取的信息以及動態(tài)調用對象的方法的功能稱為java語言的反射機制。Java反射機制主要提供了以下功能:在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具有的成員變量和方法;在運行時調用任意一個對象的方法;生成動態(tài)代理。有時候我們說某個語言具有很強的動態(tài)性,有時候我們會區(qū)分動態(tài)和靜態(tài)的不同

2、技術與作法。我們朗朗上口動態(tài)綁定(dynamicbinding)、動態(tài)鏈接(dynamiclinking)、動態(tài)加載(dynamicloading)等。然而“動態(tài)”一詞其實沒有絕對而普遍適用的嚴格定義,有時候甚至像對象導向當初被導入編程領域一樣,一人一把號,各吹各的調。一般而言,開發(fā)者社群說到動態(tài)語言,大致認同的一個定義是:“程序運行時,允許改變程序結構或變量類型,這種語言稱為動態(tài)語言”。從這個觀點看,Perl,Python,Ruby是動態(tài)語言,C+,Java,C#不是動態(tài)語言。盡管在這樣的定義與分類下Java不是動態(tài)語言,它卻有著一個非常突出的動態(tài)相關機制:Reflection。這個字的意思

3、是“反射、映象、倒影”,用在Java身上指的是我們可以于運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),并生成其對象實體、或對其fields設值、或喚起其methods1。這種“看透class”的能力(theabilityoftheprogramtoexamineitself)被稱為introspection(內省、內觀、反?。?。Reflection和introspection是常被并提的兩個術語。Java如何能夠做出上述的動態(tài)特性呢?這是一個深遠話題,本文對此只簡單介紹一些概

4、念。整個篇幅最主要還是介紹ReflectionAPIs,也就是讓讀者知道如何探索class的結構、如何對某個“運行時才獲知名稱的class”生成一份實體、為其fields設值、調用其methods。本文將談到java.lang.Class,以及HYPERLINK/view/1007969.htmt_blankjava.lang.reflect中的Method、Field、Constructor等等classes。HYPERLINK/view/1865203.htm編輯本段“Class”cla眾所周知Java有個Objectclass,是所有Javaclasses的繼承根源,其內聲明了數(shù)個應該在

5、所有Javaclass中被改寫的methods:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一個Classobject。Classclass十分特殊。它和一般classes一樣繼承自Object,其實體用以表達Java程序運行時的classes和interfaces,也用來表達enum、array、primitiveJavatypes(boolean,byte,char,short,int,long,float,double)以及關鍵詞void。當一個class被加載,或當加載器(classloader)的d

6、efineClass()被JVM調用,JVM便自動產生一個Classobject。如果您想借由“修改Java標準庫源碼”來觀察Classobject的實際生成時機(例如在Class的constructor內添加一個println()),不能夠!因為Class并沒有publicconstructor(見圖1)。本文最后我會撥一小塊篇幅順帶談談Java標準庫源碼的改動辦法。Class是Reflection故事起源。針對任何您想探勘的class,唯有先為它產生一個Classobject,接下來才能經由后者喚起為數(shù)十多個的ReflectionAPIs。這些APIs將在稍后的探險活動中一一亮相。#001

7、publicfinal#002classClassimplementsSerializable,#003java.lang.reflect.GenericDeclaration,#004java.lang.reflect.Type,#005java.lang.reflect.AnnotatedElement#006privateClass()#007publicStringtoString()#008return(isInterface()?interface:#009(isPrimitive()?:class)#010+getName();#011.圖1:Classclass片段。注意它的p

8、rivateemptyctor,意指不允許任何人經由編程方式產生Classobject。是的,其object只能由JVM產生。HYPERLINK/view/1865203.htm編輯本段“Class”object的取得途徑Java允許我們從多種管道為一個class生成對應的Classobject。圖2是一份整理。Classobject誕生管道示例運用getClass()注:每個class都有此函數(shù)Stringstr=abc;Classc1=str.getClass();運用Class.getSuperclass()2Buttonb=newButton();Classc1=b.getClass(

9、);Classc2=c1.getSuperclass();運用staticmethodClass.forName()(最常被使用)Classc1=Class.forName(java.lang.String);Classc2=Class.forName(java.awt.Button);Classc3=Class.forName(HYPERLINK/view/1008011.htmt_blankjava.util.LinkedList$Entry);Classc4=Class.forName(I);Classc5=Class.forName(.class;運用primitivewrapperc

10、lasses的TYPE語法Classc1=Boolean.TYPE;Classc2=Byte.TYPE;Classc3=Character.TYPE;Classc4=Short.TYPE;Classc5=Integer.TYPE;Classc6=Long.TYPE;Classc7=Float.TYPE;Classc8=Double.TYPE;Classc9=Void.TYPE;圖2:Java允許多種管道生成Classobject。Javaclasses組成分析首先容我以圖3的java.util.LinkedList為例,將Javaclass的定義大卸八塊,每一塊分別對應圖4所示的Reflect

11、ionAPI。圖5則是“獲得class各區(qū)塊信息”的程序示例及執(zhí)行結果,它們都取自本文示例程序的對應片段。packagejava.util;/(1)importjava.lang.*;/(2)publicclassLinkedList/(3)(4)(5)extendsAbstractSequentialList/(6)implementsList,Queue,Cloneable,.Serializable/(7)privatestaticclassEntry/(8)publicLinkedList()/(9)publicLinkedList(Collectionc)publicEgetFirs

12、t()/(10)publicEgetLast()privatetransientEntryheader=;/(11)privatetransientintsize=0;圖3:將一個Javaclass大卸八塊,每塊相應于一個或一組ReflectionAPIs(圖4)。HYPERLINK/view/1865203.htm編輯本段各成份所對應的API圖3的各個Javaclass成份,分別對應于圖4的ReflectionAPI,其中出現(xiàn)的Package、Method、Constructor、Field等等classes,都定義于java.lang.reflect。Javaclass內部模塊(參見圖3)

13、Javaclass內部模塊說明相應之ReflectionAPI,多半為Classmethods。返回值類型(returntype)(1)packageclass隸屬哪個packagegetPackage()Package(2)importclass導入哪些classes無直接對應之API。解決辦法見圖5-2。(3)modifierclass(或methods,fields)的屬性intgetModifiers()Modifier.toString(int)Modifier.isInterface(int)intStringbool(4)classnameorinterfacenameclass

14、/interface名稱getName()String(5)typeparameters參數(shù)化類型的名稱getTypeParameters()TypeVariable(6)baseclassbaseclass(只可能一個)getSuperClass()Class(7)implementedinterfaces實現(xiàn)有哪些interfacesgetInterfaces()Class(8)innerclasses內部classesgetDeclaredClasses()Class(8)outerclass如果我們觀察的class本身是innerclasses,那么相對它就會有個outerclass。

15、getDeclaringClass()Class(9)constructors構造函數(shù)getDeclaredConstructors()不論public或private或其它accesslevel,皆可獲得。另有功能近似之取得函數(shù)。Constructor(10)methods操作函數(shù)getDeclaredMethods()不論public或private或其它accesslevel,皆可獲得。另有功能近似之取得函數(shù)。Method(11)fields字段(成員變量)getDeclaredFields()不論public或private或其它accesslevel,皆可獲得。另有功能近似之取得函數(shù)

16、。Field圖4:Javaclass大卸八塊后(如圖3),每一塊所對應的ReflectionAPI。本表并非ReflectionAPIs的全部。JavaReflectionAPI運用示例圖5示范圖4提過的每一個ReflectionAPI,及其執(zhí)行結果。程序中出現(xiàn)的tName()是個輔助函數(shù),可將其第一自變量所代表的“Javaclass完整路徑字符串”剝除路徑部分,留下class名稱,儲存到第二自變量所代表的一個hashtable去并返回(如果第二自變量為null,就不儲存而只是返回)。#001Classc=null;#002c=Class.forName(args0);#003#004Pack

17、agep;#005p=c.getPackage();#006#007if(p!=null)#008System.out.println(package+p.getName()+;);執(zhí)行結果(例):packagejava.util;圖5-1:找出class隸屬的package。其中的c將繼續(xù)沿用于以下各程序片段。#001ff=c.getDeclaredFields();#002for(inti=0;iff.length;i+)#003x=tName(ff.getType().getName(),classRef);#004#005cn=c.getDeclaredConstructors();#

18、006for(inti=0;icn.length;i+)#007Classcx=cn.getParameterTypes();#008for(intj=0;jcx.length;j+)#009x=tName(cxj.getName(),classRef);#010#011#012mm=c.getDeclaredMethods();#013for(inti=0;imm.length;i+)#014x=tName(mm.getReturnType().getName(),classRef);#015Classcx=mm.getParameterTypes();#016for(intj=0;jcx.

19、length;j+)#017x=tName(cxj.getName(),classRef);#018#019classRef.remove(c.getName();/不必記錄自己(不需import自己)執(zhí)行結果(例):importjava.util.ListIterator;importjava.lang.Object;importjava.util.LinkedList$Entry;importjava.util.Collection;importObjectOutputStream;import.ObjectInputStream;圖5-2:找出導入的classes,動作細節(jié)詳見內文說明。

20、#001intmod=c.getModifiers();#002System.out.print(Modifier.toString(mod);/整個modifier#003#004if(Modifier.isInterface(mod)#005System.out.print();/關鍵詞interface已含于modifier#006else#007System.out.print(class);/關鍵詞class#008System.out.print(tName(c.getName(),null);/class名稱執(zhí)行結果(例):publicclassLinkedList圖5-3:找出

21、class或interface的名稱,及其屬性(modifiers)。#001TypeVariabletv;#002tv=c.getTypeParameters();/warning:uncheckedconversion#003for(inti=0;itv.length;i+)#004x=tName(tv.getName(),null);/例如E,K,V.#005if(i=0)/第一個#006System.out.print();#011執(zhí)行結果(例):publicabstractinterfaceMap或publicclassLinkedList圖5-4:找出parameterizedty

22、pes的名稱#001ClasssupClass;#002supClass=c.getSuperclass();#003if(supClass!=null)/如果有superclass#004System.out.print(extends+#005tName(supClass.getName(),classRef);執(zhí)行結果(例):publicclassLinkedListextendsAbstractSequentialList,圖5-5:找出baseclass。執(zhí)行結果多出一個不該有的逗號于尾端。此非本處重點,為簡化計,不多做處理。#001Classcc;#002Classctmp;#00

23、3/找出所有被實現(xiàn)的interfaces#004cc=c.getInterfaces();#005if(cc.length!=0)#006System.out.print(,rn+implements);/關鍵詞#007for(Classcite:cc)/JDK1.5新式循環(huán)寫法#008System.out.print(tName(cite.getName(),null)+,);執(zhí)行結果(例):publicclassLinkedListextendsAbstractSequentialList,implementsList,Queue,Cloneable,Serializable,圖5-6:找

24、出implementedinterfaces。執(zhí)行結果多出一個不該有的逗號于尾端。此非本處重點,為簡化計,不多做處理。#001cc=c.getDeclaredClasses();/找出innerclasses#002for(Classcite:cc)#003System.out.println(tName(cite.getName(),null);#004#005ctmp=c.getDeclaringClass();/找出outerclasses#006if(ctmp!=null)#007System.out.println(ctmp.getName();執(zhí)行結果(例):LinkedList$

25、EntryLinkedList$ListItr圖5-7:找出innerclasses和outerclass#001Constructorcn;#002cn=c.getDeclaredConstructors();#003for(inti=0;icn.length;i+)#004intmd=cn.getModifiers();#005System.out.print(+Modifier.toString(md)+#006cn.getName();#007Classcx=cn.getParameterTypes();#008System.out.print();#009for(intj=0;jcx

26、.length;j+)#010System.out.print(tName(cxj.getName(),null);#011if(j(cx.length-1)System.out.print(,);#012#013System.out.print();#014執(zhí)行結果(例):publicjava.util.LinkedList(Collection)publicjava.util.LinkedList()圖5-8a:找出所有constructors#004System.out.println(cn.toGenericString();執(zhí)行結果(例):publicjava.util.Linked

27、List(java.util.Collection)publicjava.util.LinkedList()圖5-8b:找出所有constructors。本例在for循環(huán)內使用toGenericString(),省事。#001Methodmm;#002mm=c.getDeclaredMethods();#003for(inti=0;imm.length;i+)#004intmd=mm.getModifiers();#005System.out.print(+Modifier.toString(md)+#006tName(mm.getReturnType().getName(),null)+#0

28、07mm.getName();#008Classcx=mm.getParameterTypes();#009System.out.print();#010for(intj=0;jcx.length;j+)#011System.out.print(tName(cxj.getName(),null);#012if(j(cx.length-1)System.out.print(,);#013#014System.out.print();#015執(zhí)行結果(例):publicObjectget(int)publicintsize()圖5-9a:找出所有methods#004System.out.prin

29、tln(mm.toGenericString();publicEjava.util.LinkedList.get(int)publicintjava.util.LinkedList.size()圖5-9b:找出所有methods。本例在for循環(huán)內使用toGenericString(),省事。#001Fieldff;#002ff=c.getDeclaredFields();#003for(inti=0;iff.length;i+)#004intmd=ff.getModifiers();#005System.out.println(+Modifier.toString(md)+#006tName

30、(ff.getType().getName(),null)+#007ff.getName()+;);#008執(zhí)行結果(例):privatetransientLinkedList$Entryheader;privatetransientintsize;圖5-10a:找出所有fields#004System.out.println(G:+ff.toGenericString();privatetransientjava.util.LinkedList.java.util.LinkedList$Entry?java.util.LinkedList.headerprivatetransientintj

31、ava.util.LinkedList.size圖5-10b:找出所有fields。本例在for循環(huán)內使用toGenericString(),省事。找出class參用(導入)的所有classes沒有直接可用的ReflectionAPI可以為我們找出某個class參用的所有其它classes。要獲得這項信息,必須做苦工,一步一腳印逐一記錄。我們必須觀察所有fields的類型、所有methods(包括constructors)的參數(shù)類型和回返類型,剔除重復,留下唯一。這正是為什么圖5-2程序代碼要為tName()指定一個hashtable(而非一個null)做為第二自變量的緣故:hashtable

32、可為我們儲存元素(本例為字符串),又保證不重復。本文討論至此,幾乎可以還原一個class的原貌(唯有methods和ctors的定義無法取得)。接下來討論Reflection的另三個動態(tài)性質:(1)運行時生成instances,(2)執(zhí)行期喚起methods,(3)運行時改動fields。運行時生成instances欲生成對象實體,在Reflection動態(tài)機制中有兩種作法,一個針對“無自變量ctor”,一個針對“帶參數(shù)ctor”。圖6是面對“無自變量ctor”的例子。如果欲調用的是“帶參數(shù)ctor“就比較麻煩些,圖7是個例子,其中不再調用Class的newInstance(),而是調用Con

33、structor的newInstance()。圖7首先準備一個Class做為ctor的參數(shù)類型(本例指定為一個double和一個int),然后以此為自變量調用getConstructor(),獲得一個專屬ctor。接下來再準備一個Object做為ctor實參值(本例指定3.14159和125),調用上述專屬ctor的newInstance()。#001Classc=Class.forName(DynTest);#002Objectobj=null;#003obj=c.newInstance();/不帶自變量#004System.out.println(obj);圖6:動態(tài)生成“Classobj

34、ect所對應之class”的對象實體;無自變量。#001Classc=Class.forName(DynTest);#002ClasspTypes=newClassdouble.class,int.class;#003Constructorctor=c.getConstructor(pTypes);#004/指定parameterlist,便可獲得特定之ctor#005#006Objectobj=null;#007Objectarg=newObject3.14159,125;/自變量#008obj=ctor.newInstance(arg);#009System.out.println(obj

35、);圖7:動態(tài)生成“Classobject對應之class”的對象實體;自變量以Object表示。運行時調用methods這個動作和上述調用“帶參數(shù)之ctor”相當類似。首先準備一個Class做為ctor的參數(shù)類型(本例指定其中一個是String,另一個是Hashtable),然后以此為自變量調用getMethod(),獲得特定的Methodobject。接下來準備一個Object放置自變量,然后調用上述所得之特定Methodobject的invoke(),如圖8。知道為什么索取Methodobject時不需指定回返類型嗎?因為methodoverloading機制要求signature(署名

36、式)必須唯一,而回返類型并非signature的一個成份。換句話說,只要指定了method名稱和參數(shù)列,就一定指出了一個獨一無二的method。#001publicStringfunc(Strings,Hashtableht)#002#003System.out.println(funcinvoked);returns;#004#005publicstaticvoidmain(Stringargs)#006#007Classc=Class.forName(Test);#008Classptypes=newClass2;#009ptypes0=Class.forName(java.lang.St

37、ring);#010ptypes1=Class.forName(java.util.Hashtable);#011Methodm=c.getMethod(func,ptypes);#012Testobj=newTest();#013Objectargs=newObject2;#014arg0=newString(Hello,world);#015arg1=null;#016Objectr=m.invoke(obj,arg);#017Integerrval=(String)r;#018System.out.println(rval);#019圖8:動態(tài)喚起method運行時變更fields內容與

38、先前兩個動作相比,“變更field內容”輕松多了,因為它不需要參數(shù)和自變量。首先調用Class的getField()并指定field名稱。獲得特定的Fieldobject之后便可直接調用Field的get()和set(),如圖9。#001publicclassTest#002publicdoubled;#003#004publicstaticvoidmain(Stringargs)#005#006Classc=Class.forName(Test);#007Fieldf=c.getField(d);/指定field名稱#008Testobj=newTest();#009System.out.p

39、rintln(d=+(Double)f.get(obj);#010f.set(obj,12.34);#011System.out.println(d=+obj.d);#012#013圖9:動態(tài)變更field內容HYPERLINK/view/1865203.htm編輯本段Java源碼改動辦法先前我曾提到,原本想借由“改動Java標準庫源碼”來測知Classobject的生成,但由于其ctor原始設計為private,也就是說不可能透過這個管道生成Classobject(而是由classloader負責生成),因此“在ctor中打印出某種信息”的企圖也就失去了意義。這里我要談點題外話:如何修改Ja

40、va標準庫源碼并讓它反應到我們的HYPERLINK/view/330120.htmt_blank應用程序來。假設我想修改java.lang.Class,讓它在某些情況下打印某種信息。首先必須找出標準源碼!當你下載JDK套件并安裝妥當,你會發(fā)現(xiàn)jdk150srcjavalang目錄(見圖10)之中有Class.java,這就是我們此次行動的標準源碼。備份后加以修改,編譯獲得Class.class。接下來準備將.class搬移到jdk150jrelibendorsed(見圖10)。這是一個十分特別的目錄,classloader將優(yōu)先從該處讀取內含classes的.jar文件成功的條件是.jar內的classes壓縮路徑必須和Java標準庫的路徑完全相同。為此,我們可以將剛才做出的Class.class先搬到一個為此目的而刻意做出來的javalang目錄中,壓縮為foo.zip(任意命名,唯需夾帶路徑javalang),再將這個foo

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論