




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、Java中的反射在java中反射的是,我們可以在運行時加載、探知、使用編譯期間完全未知的類。換句話說,Java程序可以加載一個運行時才得知名稱的類,獲悉其完整結構,并生成其對象實體、或對其變量設值、或調用其方法。這種“看透類”的能力被稱為Introspection(內省,內觀,反省)。Reflection和Introspection是常被并提的兩個術語。在java中,反射是一種強大的工具。它使你能夠靈活的代碼,這些代碼可以在運行時裝配,無須在組件之前進行源代表連接。反射容許我們在編寫與執行時,使我們的程序代碼能夠接入轉載到JVM中的類的內部信息,而不是源代碼中選定的類協助的代碼。這使反射成為構
2、建靈活應用的主要工具。但需要注意的是,如果使用不當,反射的成本很高。Reflection是Java程序開發語言的特征之一,它允許運行的Java程序對自身進行檢查,或者說“自審”,并能直接操作程序的內部屬性。例如,使用它能獲取得Java類中各成員并顯示出來。Java的這一能力在實際應用中也許用的不是很多,但是在其他程序設計語言中根本不存在這一特性。例如Pascal、C、C+中就沒有辦法在程序中獲得函數定義相關的信息。JavaBean是Reflection的實際應用之一,它能讓一些工具可視化地操作軟件組件。這些工具通過Reflection動態地載入并獲取Java組件(類)的屬性。1、第一個反射的例
3、子public class Test public static void main(String args) throws Exception Class c= Class.forName("java.lang.System");Method m=c.getDeclaredMethods();for(int i=0;i<m.length;i+)System.out.println(mi);該例子列出了java.lang.Systme類的各方法名及它們的限制符和返回類型。這個程序使用Class.forName載入指定的類,然后調用getDeclaredMethods來
4、獲取這個類中定義了的方法列表。java.lang.reflect.Method是用來描述某個類中單個方法的一個類。2、Java反射APIjava提供了一套獨特的反射API來描述類,使得Java程序在運行時可以獲得任何一個類的字節碼信息,包括類的修飾符(public,static等)、基類(超類,父類)、實現的接口、字段和方法等信息,并可以根據字節碼信息來創建該類的實例化對象,改變該對象的字段內容和調用該對象的方法。Class是Java反射中的一個核心類,它代表了內存中的一個Java類。通過它可以獲取類的各種操作屬性,這些屬性是通過java.lang.reflect包中的反射API來描述的。*C
5、onstructor:用來描述一個類的構造方法。*Field:用來描述一個類的成員變量*Method:用來描述一個類的方法*Modifer:用來描述類中各元素的修飾符*Array:用來對數組進行操作它們之間的關系如圖26-1對于任何一類來說,都可以通過Class提供的反射API獲取類信息。除了可以使用newInstance()創建該類的實例外,可以使用Class取得類的包和類名:Package getPackage(); /獲取此類的包String getName();/獲取此類的類名Class最重要的功能是提供了一組反射調用,用以獲取構造函數,變量及方法。2.1獲取類的構造函數提供了3種方法
6、*獲得類的所有構造函數和方法:Constructor<?> getConstructors(); /當前類的構造函數Constouctor<?> getDeclaredConstructors(); /當前類的構造函數*獲得使用特殊的參數類型的公共構造函數:Constructor<T> getConstructor(Class<?>.parameterTypes);/當前類的構造函數Constructor<T> getDeclaredConstructor(Class<?>.parameterTypes) /當前類或接口的
7、構造函數*獲取本地或者匿名類的構造函數Constructor<?> getEnclosingConstructor();Constructor類提供了取得參數類型和創造實例的方法Class<?> getParameterTypes();/取得參數類型T newInstance(Object. initargs); /創建該構造方法的聲明類的新實例2.2 取得變量-返回Field獲得字段信息的Class反射調用不同于那些用于接入構造函數的調用,在參數類型數組中可以使用字段名。*取得指定名稱的變量:Field getField(String name);/類或接口的指定公共
8、成員字段Field getDeclaredField(String name);/類或接口的指定成員字段*取得類或接口所聲明的所有變量:Field getFields();/類或接口的指定公共成員字段Field getDeclaredFields();/類或接口的指定成員字段Field類為變量提供了獲得或修改值的方法/獲取變量值boolean getBoolean(Object obj);byte getByte(Objcet obj);char getChar(Object obj);double getDouble(Object obj);float getFloat(Object obj
9、);int getInt(Object obj);long getLong(Object obj);short getShort(Object obj);/修改變量值void setBoolean(Object obj,boolean z);void setByte(Object obj,byte b);void setChar(Object obj,char c);void setDouble(Object obj,double d);void setFloat(Object obj,float f);void setInt(Object obj,int i);void setLong(Ob
10、ject obj,long l);void setShort(Object obj,short s);2.3 獲取方法-返回類型Method用于獲得方法信息函數如下:*使用特定的參數類型獲取命名的方法Method getMethod(String name,Class<?>. prameterTypes);/類或接口的指定公共成員方法Method getDeclaredMethod(String name,Class<?>. prameterTypes);/類或接口的指定成員方法*獲取所有方法列表Method getMethods(); /類或接口的所有公共成員方法Me
11、thod getDeclaredMethods(); /類或接口的所有成員Method為方法提供了取參數和返回值類型及調用的方法:Class<?>getParameterTypes(); /取得參數類型Class<?>getReturnType();/取得返回值類型Object invoke(Object obj,Object.args); /對帶有指定參數的指定對象調用方法車2.4 Array類提供了動態創建Java數組的方法,這些方法對應了8個Java的基本類型,形如static boolean getBoolean(Object array,int index);
12、static void setBoolean(Object array,int index,boolean z);2.5 Modifier類提供了static方法和常量,對類和成員訪問修飾符進行解碼。修飾符集被表現為整數,用不同的位置(bit position)表示不同的修飾符。static int ABSTRACT;/表示abstract修飾符的int的值static int FINAL;/表示final修飾的int值static int INTERFACE;/表示interface修飾符的int的值static int NARIVE;/表示native修飾符的int的值static int
13、 PRIVATE;/表示private修飾符的int的值static int PUBLIC;/表示public修飾符的int的值static int STATIC;/表示static修飾符的int的值static int STRICT;/表示strictfp修飾符的int的值static int SYNCHRONIZED;/表示synchronized修飾符的int值static int TRANSIENT;/表示transient修飾符的int的值static int VOLATILE; /表示volatile修飾的int值3、Java反射應用-檢測類從上面的API函數可以看出,Java反射
14、功能能實現類的查找、創建與調試。上面的介紹必須抽象,本節將根據實際應用的需求來分類講解Java反射的應用實例。為了演示通過代碼對類的動態加載和調用,我們首先建立一個類MyObject,該類用來計算兩個數的加、減、乘、除,供后面演示調用。public class MyObject private int a,b;public MyObject(int a,int b)this.a=a;this.b=b;int sum()return a+b;private int minus()return a-b;public int multiply()return a*b;int divide()retu
15、rn a/b;該類中包括了變量、構造函數和方法,足夠后面的演示所需。本節的內容包括以下幾項。*獲取類*獲取類的方法*獲取類的構造器*獲取類的變量3.1 標準會話管理器StandardManager在運行的Java程序中,用java.lang.Class類來描述類的接口等下面就是獲得Class對象的方法之一:Class cls = Class.forName("test.MyObject");獲取Class對象的另外一個方法:Class cls = MyObject.class;3.2 獲取類的方法找出一個類中定義了什么方法,這是一個非常有價值其基礎的Reflection用法
16、,下面的代碼就是證實了這一用法。/取得類的方法Method methods=c.getDeclaredMethods();for(Method method:methods)System.out.print("修飾符"+method.getModifiers();System.out.print("方法名"+method.getName();System.out.println("返回值類型"+method.getReturnType();/參數列表Class params= method.getParameterTypes();fo
17、r(int i=0;i<params.length;i+)System.out.println("參數"+paramsi);這個程序調用getDeclaredMethods()來獲取一系列的Method對象,它們分別描述了定義中的每一個方法,包括public方法,protected方法,private方法等。如果你在程序中使用getMethods()來代替getDeclaredMethods(),你還能獲得繼承來的各個方法的信息。取得了Method對象列表之后,要顯示這些方法的參數信息,異常類型和返回值類型等就不難了。這些類型是基本類型還是類類型,都可以由描述類的對象
18、按順序給出。3.3 獲取類的構造器獲取類構造器的用法與上述獲取方法的用法類型:Constructor constrctors=c.getDeclaredConstructors();for(Constructor costrctor:constrctors)System.out.println("方法名:"+costrctor.getName();System.out.println("修飾符:"+costrctor.getModifiers();Class params= costrctor.getParameterTypes();for(int i=
19、0;i<params.length;i+)System.out.println("參數"+paramsi);3.4 獲取類的變量找出一個類中定義了那些數字字段也是可能的,下面的代碼就在進行這個事情:Field flelds = c.getDeclaredFields();for(Field fleld:flelds)System.out.println("變量名:"+fleld.getName();System.out.println("修飾符:"+fleld.getModifiers();System.out.println(
20、"返回值類型"+fleld.getType();和獲取方法的情況一樣,獲取字段的時候也可以只取得在當前類中申請了的字段信息(使用getDeclaredFields()方法),或者也可以獲取父類中定義的字段(使用getFields()方法)。4、Java反射應用-處理對象用反射處理對象的方法*創建新的對象*執行類的方法*修改變量的值*使用數組4.1 創建類的對象A、可以根據構造器來實例化一個對象。由于類MyObject的構造函數有兩個int類型的參數,因此需要先創建一個類型數組,在根據類型數組獲得構造函數:Class paramtypes = new Class2;param
21、types0=int.class;paramtypes1=int.class;Constructor constructor = c.getConstructor(paramtypes);B、然后就可以使用該構造函數constructor來實例化一個對象了。由于該構造函數含有兩個整形參數,因此需要傳遞倆個參數來獲取實例:Object object = constructor.newInstance(6,3);根據指定的參數類型找到相應的構造函數并執行它,以創建一個新的對象實例。使用這個方法可以在程序運行時動態地創建對象,而不是在編譯的時候創建對象,這一點非常有價值。你也可以使用下面的函數來調用
22、午餐的構造函數。/調用默認構造函數Constructor constructor = c.getConstructor();Object object = constructor.newInstance();4.2 改變變量的值Reflection還有一個用處就是改變對象數據字段的值。Reflection可以從正在運行的程序中根據名稱找到對象的字段并改變它,下面的例子可以說明這一點:Field fielda=c.getField("a");System.out.println("修改前:"+fielda.get(object);fielda.setInt
23、(object, 100);System.out.println("修改后:"+fielda.get(object);4.3 執行類的方法到這里,所舉的例子無一例外都與如何獲取類的信息有關。我們也可以使用Reflection來做一些其他的事情,比如執行一個指定了名稱的方法。如下與構造方法的調用過程相似,需要首先根據需要調用方法的參數類型創建一個參數類型數組,然后再創建一個輸入值數組,執行方法的調用。如果函數沒有參數,直接調用即可。下面我們分別調用4個函數來執行加減乘除的運算:/執行加法Method method1 = c.getMethod("sum")
24、;Object sum = method1.invoke(object);System.out.println("加="+sum);/執行減法Method method2 = c.getMethod("minus");Object minus = method2.invoke(object);System.out.println("減="+minus);/執行乘法Method method3 = c.getMethod("multiply");Object multiply = method3.invoke(obj
25、ect);System.out.println("乘="+multiply);/執行除法Method method4 = c.getMethod("divide");Object divide = method4.invoke(object);System.out.println("除="+divide);假如一個程序在執行到某處時才知道需要執行某個方法,這個方法的名稱是在執行程序的運行過程中指定的(例如,JavaBean開發環境中就會出現這樣的事情),上面的程序就演示了如何做到。在上例的加法計算中,getMethod用于查詢一個具有
26、兩個整形參數且名稱為“sum”的方法。找到該方法并創建了相應的Method對象之后,在正確的對象實例中執行它。執行該方法的時候,需要提供一個參數列表,在上例中是分別包裝了整數9和3的個int對象。執行方法的返回同樣是一個Integer對象4.4 使用數組本文介紹的Reflection的最后一種用法就是創建的操作數組。數組在Java語言中是一種特殊的類型,一個數組的引用可以賦值給Object引用。觀察下面的例子看肯數組時怎么工作的:/使用數組Class cls = Class.forName("java.lang.String");Object array = Array.n
27、ewInstance(cls,10);Array.set(array, 5, "String");String str5= (String)Array.get(array,5);System.out.println(str5);例中創建了一個10個單位的String數組,為第5個位置的字符串賦值,最后將這個字符串從數組中獲取并打印出來。下面這段代碼提供了一個更復雜的例子:int dims = new int5,10,15;Object arr = Array.newInstance(Integer.Type,dims);Object arrobj = Array.get(a
28、rr,3);Class cls = arrobj.getClass().getComponentType();System.out.println(cls);arrobj=Array.get(arrobj,5);Array.setInt(arrobj,10,37);int arrcast=(int)arr; System.out.println(arrcast3510);例中創建了一個5X10X15的整形數組,并為處于3510的元素賦值為37。注意,多維數組實際上就是數組的數組,例如,第一個Array.get()之后,arrobj是一個10X15的數組。進而取得其中的一個元素,即長度為15的數
29、組,并使用Array.setInt()為它的第10個元素賦值。Java動態代理為了接管目標類方法的執行,我們應該有一種類型鉤子的機制。例如在windows編程中我們可以利用Hook API來實現對某個Windows API的接管。在Java中同樣也有這樣的一個機制,Java提供了java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口,Proxy類主要用來獲取動態代理對象,InvocationHandler接口用來約束調用者實現,這兩個類都在java.lang.reflect包中。本節我們將首先講解動態代理的機制,并通過實際開
30、發案例講解動態代理的應用。最后講解基于動態代理的AOP實現和字節碼庫。1 動態代理機制所謂動態代理,即通過Proxy的代理,接口和實現類之間可以不直接發生聯系,而可以在運行期(Runtime)實現動態關聯。Java動態代理類位于java.lang.reflect包下,一般主要涉及到以下兩個類。(1)接口InvocationHandle:該接口中僅定義了一個方法Object invoke(Object obj,Method method,Object args);在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,args為該方法的參數數組。(2)Proxy:該類即為動態代
31、理類,作用類實現了InvocationHandler接口的代理類,其中主要包含以下函數。*protected Proxy(InvocationHandler h):構造函數,用于給內部的h賦值。*static Class getProxyClass(ClassLoader loader,Class interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部接口的數組。*static Object newProxyInstance(ClassLoader loader,Class interfaces,InvocationHandler h):返
32、回代理類的一個實例,返回后的代理類可以當做被代理使用。所謂Dynamic Proxy是這樣一種類:它是在運行時生成的class,在生成它時你必須提供一組interface給它,然后該class就宣傳自己實現這些interface。你當然可以把該class的實現當做這些interface中的任何一個來用。當然,這個Dynamic Proxy其實就是一個Proxy,它不會替你做實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實現的工作。2 動態代理應用java1.3引用了名為“動態代理類”(Dynamic Proxy Class)的新特性,利用它可為“已知接口的實現”動態地創建
33、包裝器(wrapper)類。為了更好地演示動態代理的原理,我們從非動態代理的演示開始說起。2.1 定義接口和實現類并直接調用為了實現一系列的不同實現,首先我們定義一個接口類Hello:public interface Hellovoid say();該接口定義了一個say()方法,用來約束其實現函數來實現不同的內容。接下來我們分別定義兩個實現類Hello World和HelloChina,分別執行不同的say()代碼:public class HelloWorld implements Hellopublic void say()System.out.println("Hello W
34、orld!");public class HellowChina implements Hellopublic void say()System.out.println("Hello China!");此時,對應這兩個實現類,我們可以直接創建它們的實例,調用其中的方法:Hello helloWorld = new HelloWorld();Hello chinaWorld = new HelloChina();helloWorld.say();chinaWorld.say();2.2 使用包裝類進行包裝假定我們現在想攔截對HelloWorld或HelloChina
35、類發出的方法say()的調用,比如在調用前和調用后分別輸出字符串“start to say:”和“end say!”,那么就需要定義一個對它們的公共接口類Hello的包裝類HelloWrapper:/包裝類HelloWrapperpublic class HelloWrapper implements Helloprivate Hello wrapped; /包裝對象public HelloWrapper(Hello hello)this.wrapped = hello;/包裝函數public void say()System.out.println("start to say&qu
36、ot;);wrapped.say();System.out.println("end say");在該類中包含一個Hello類的變量wrapped,表示待包裝的實例對象,它通過構造函數寫入,并添加了一個包裝Hello類中say()方法的包裝函數say(),該函數在調用目標對象wrapped的say()函數的前后分別輸出字符串“start to say:”和“end say!”,這就到達了包裝的目的。此時就可以使用該包裝類對上面的實例world和china進行包裝。分別根據這兩個實例創建兩個包裝類,然后直接調用包裝類的方法,即可實現對目標變量world和china的包裝調用:
37、HelloWrapper wrapper1 = new HelloWrapper(world);HelloWrapper wrapper2 = new HelloWrapper(china);wrapper1.say();wrapper2.say();對應這種包裝器風格的HelloWrapper來說,一旦你想修改Hello接口,它的缺陷就會暴露出來。為Hello接口添加方法,就得為HelloWrapper類添加一個包裝器方法。如果我們將HelloWrapper繼承自HelloWorld,即為實現類HelloWorld實現包裝器:public class HelloWrapper extends
38、 HelloWorldprivate Hello wrapped; /包裝對象public HelloWrapper(Hello hello)this.wrapped = hello;/包裝函數public void say()System.out.println("start to say");wrapped.say();System.out.println("end say");這種方式在修改接口方法時不必修改包裝器,但是同時又出現了新的問題,只有HelloWorld對象才能使用包裝器HelloWrapper。而在此之前,實現了Hello接口的任何對
39、象都可以使用HelloWrapper。現在,由Java施加的“線性類出身限制”禁止我們將任意Hello變成一個HelloWrapper。為此,可以使用動態代理。2.3 使用動態代理動態代理則綜合了以上兩種方案的優點。使用動態代理,你創建的包裝器類不要求為所有方法都使用顯式的包裝器。下面的代碼演示了用動態代理來創建一個代理類HelloHandler,我們創建這個HelloHandler不需要實現Hello接口,而是實現了java.lang.reflect.InvocationHandler,只提供了一個invoke()方法。代理對象上的任何方法調用都要通過這一方法進行。觀察invoke()的主體
40、,它包括了被調用函數的反射參數Method,我們可以使用該參數確定當前執行方法的屬性。然而,我們得到的仍然只是一個具有invoke()方法的InvocationHandler,而不是我們真正想要的Hello對象。動態代理真正的魅力要到創建實際的Hello實例時才能反映出來。它通過調用HelloHandler的構造方法初始化了被包裝的對象proxyed.InvocationHandler handler1 = new HelloHandler(world);Hello proxy1= (Hello)Proxy.newProxyInstance(world.getClass().getClassL
41、oader(), world.getClass().getInterfaces(), handler1);proxy1.say();InvocationHandler handler2 = new HelloHandler(china);Hello proxy2= (Hello)Proxy.newProxyInstance(world.getClass().getClassLoader(), world.getClass().getInterfaces(), handler2);proxy2.say();這段代碼實現了world和china的代理調用。*首先根據被代理對象world創建一個代理
42、類handler1,此處是HelloHandler對象。*創建動態代理對象proxy1,它的第一個參數為world類的加載器,第二個參數為該類的接口,第三個對象為代理對象handler1.*通過動態代理對象proxy1調用say()方法,此時會在原始對象HelloWorld.say()方法前后輸入運行這段代碼的輸出結果。上面的代碼表面上很復雜,但作用很簡單,就是告訴Proxy類用一個指定的類加載器來動態創建一個對象,該對象要實現指定的接口(本例為Hello),并用提供的InvocationHandler來代替傳統的方法主體。結果對象在一個instanceof Hello測試返回true,提供了
43、在實現了Hello接口的任何類中都能找到的方法。有趣的是,在HelloHandler類的invoke()方法中,完全不存在對Hello接口的引用。在本例中我們以一個構造函數的形式,為HelloHandler提供了Hello的一個實例。代理Hello實例上的任何方法調用最終都由HelloHandler委托給這個包裝的Hello.但是,雖然這是最常見的設計,但你必須了解,InvocationHandler不一定非要委托給被代理的接口的另外一個實例。事實上,InvocationHandler完全能自行提供方法主體,而無須一個委托目標。最后要注意,如果Hello接口中發生改變,那么Hellohandler中的invoke方法將仍然可以移植,假定say()方法被重命名,那么新的方法名依然會被攔截。3 基于動態代理的AOP實現動態代理有一個很好的用處就是生成調用st
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 【正版授權】 ISO/IEC 18584-2:2025 EN Information technology - Test methods for on-card biometric comparison applications - Part 2: Work-sharing mechanism
- GB/T 6826-2025礦山機械洗選設備系列型譜
- 2025至2030中國益生菌補充品市場供應渠道與未來銷售渠道研究報告
- 2025至2030中國電子相冊軟件行業市場發展趨勢及有效策略與實施路徑評估報告
- 2025至2030中國電動牙鉆機行業深度研究及發展前景投資評估分析
- 2025至2030中國珩磨油行業深度研究及發展前景投資評估分析
- 2025至2030中國特殊需要的嬰兒車行業發展趨勢分析與未來投資戰略咨詢研究報告
- 教育心理學學生情感教育的核心
- 遼寧省沈文新高考研究聯盟2024-2025學年高一下學期7月期末質量監測地理試卷(含答案)
- 華邦健康知識培訓
- 2024年09月年中國農業發展銀行江蘇省分行秋季校園招聘(86人)筆試歷年參考題庫附帶答案詳解
- 2025年江蘇省揚州市中考作文4篇范文:“尊重”“誠實”“創造性”“美好生活”
- 2025年輔警招聘考試試題庫含完整答案
- 2025年吉林省中考語文試卷及答案
- 2024-2025學年度天津鐵道職業技術學院單招《語文》真題附答案詳解(突破訓練)
- 快遞行業市場發展分析及投資前景研究報告2025-2028版
- 《基本樂理》師范與學前教育專業基本樂理相關知識全套教學課件
- 2025年安徽省中考物理試題(原卷版)
- 2025-2026年中國臺球產業消費趨勢報告
- 2025年高考英語全國新高考II卷深度解讀及答案詳解
- 2025年第十屆“學憲法、講憲法”網絡知識競賽題庫(含答案)
評論
0/150
提交評論