详解Java反射机制
一、什么是JAVA的反射機制
Java反射是Java被視為動態(tài)(或準動態(tài))語言的一個關鍵性質。這個機制允許程序在運行時透過Reflection APIs取得任何一個已知名稱的類的內部信息,包括其方法(諸如public, static 等)、父類(例如Object)、實現接口(例如Cloneable),也包括屬性和方法的所有信息,并可于運行時改變屬性值或調用方法。
Java反射機制容許程序在運行時加載、探知、使用編譯期間完全未知的類。Java可以加載一個運行時才得知名稱的類,獲得其完整結構。
簡言之,JAVA反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法;這種動態(tài)獲取的信息以及動態(tài)調用對象的方法的功能稱為java語言的反射機制。
二、JDK中提供的Reflection API
Java反射相關的API在包java.lang.reflect中,JDK 1.6.0的reflect包如下圖:
該類下的幾個主要類和接口
三、JAVA反射機制提供了什么功能
=========================================
Java反射機制提供如下功能(用途):
在運行時判斷任意一個對象所屬的類
在運行時構造任意一個類的對象
在運行時判段任意一個類所具有的成員變量和方法
在運行時調用任一個對象的方法
在運行時創(chuàng)建新類對象
=======================================
一個對象的創(chuàng)建分三步完成:
1、加載類到內存;
2、靜態(tài)資源初始化;
3、創(chuàng)建對象;
一個類對應一個字節(jié)碼文件*.class,反射研究的就是字節(jié)碼文件;一個字節(jié)碼文件中主要包含類的成員變量、方法、構造方法;通過該字節(jié)碼文件可以獲取該類的相關信息。同一個字節(jié)碼文件可以構建多個對象;
//類的字節(jié)碼文件 MyA a1=new MyA(); //獲得該類的字節(jié)碼文件 Class clazz=a1.getClass();對象類型有字節(jié)碼文件,基礎的數據類型也有相應的字節(jié)碼文件。
int.class void.class在使用Java的反射功能時,基本首先都要獲取類的Class字節(jié)碼對象,再通過Class對象獲取其他的對象。
1、獲取類的Class字節(jié)碼對象
Class 類的實例表示正在運行的 Java 應用程序中的類和接口。獲取類的Class對象有多種方式:
2、獲取類的屬性
可以通過反射機制得到某個類的某個屬性,然后可以改變對應于這個類的某個實例的該屬性值。JAVA 的Class<T>類提供了幾個方法獲取類的屬性。
public Field getField(String name)返回某類一個public修飾的成員變量(public,包括父類中定義);
public Field[] getFields()返回一個數組,數組中包含某類下的public修飾的所有成員變量(public,包括父類中定義);
public Field getDeclaredField(String name)返回某類一個private修飾的成員變量(private,不包括父類中定義);
public Field[] getDeclaredFields()返回一個數組,數組中包含某類下的所有private修飾的成員變量(private,不包括父類中定義);
3、獲取類的方法
通過反射機制得到某個類的某個方法,然后可以調用對應于這個類的某個實例的該方法,Class<T>類提供了幾個方法獲取類的方法。
public Method getMethod(String name,Class<?>... parameterTypes)通過方法名和方法的入參得到一個某類下的public修飾的Method方法對象(public,包括父類中定義);
public Method[] getMethods()得到某類下的public修飾的所有Method方法對象(public,包括父類定義的)
public MethodgetDeclaredMethod(Stringname,Class<?>... parameterTypes)通過方法名和方法的入參得到一個某類下的private修飾的Method方法對象(private,不包括父類中定義);;
public Method[] getDeclaredMethods()得到某類下的private修飾的所有Method方法對象(private,不包括父類定義的)
通過反射獲得類的方法(public)MyA m1=new MyA();Class clazz=m1.getClass();//方法名,入參 clazz.getMethod("setAge",int.class);//獲得方法名之后,如何調用該方法(對象,入參) setAge.invoke(m1,23);//調用m1下的該方法m1.getAge();//23注意: setAge.invoke(null,23);當前類下的不依賴對象的方法(靜態(tài)方法)要被執(zhí)行;4、獲取類的Constructor構造方法
通過反射機制得到某個類的構造器,然后可以調用該構造器創(chuàng)建該類的一個實例Class<T> 類提供了幾個方法獲取類的構造器。
public Constructor<T> getConstructor(Class<?>... parameterTypes)
通過構造方法的入參返回一個有相應入參的 Constructor 對象(public,包括父類中定義);
返回一個數組,該數組中包含該類下的所有構造方法(public,包括父類中定義);
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)通過構造方法的入參返回一個有相應入參的 Constructor 對象(private,不包括父類中定義);
public Constructor<?>[] getDeclaredConstructors()返回一個數組,該數組中包含該類下的所有構造方法(private,不包括父類中定義);
5、創(chuàng)建類的實例
通過反射機制創(chuàng)建類的實例,有幾種方法可以創(chuàng)建
調用無自變量的構造器
1、調用類的Class對象的newInstance方法,該方法會調用對象的默認構造器,如果沒有默認構造器,會調用失敗(所以,在實際應用中用到這種方法時,如果要重寫一個類的帶參數的構造方法,就一定要把不帶參數的構造方法顯式定義出來).
Class<?> classType = ExtendType.class; Object inst = classType.newInstance();System.out.println(inst); 輸出: Type:Default Constructor ExtendType:Default Constructor com.quincy.ExtendType@d80be32、調用默認Constructor對象的newInstance方法
Class<?> classType = ExtendType.class; Constructor<?> constructor1 = classType.getConstructor(); Object inst = constructor1.newInstance(); System.out.println(inst); 輸出: Type:Default Constrructor ExtendType:Default Constru com.quincy.ExtendType@1006d756d75調用帶參數構造器
3、調用帶參數Constructor對象的newInstance方法
Constructor<?> constructor2 =classType.getDeclaredConstructor(int.class, String.class);Object inst = constructor2.newInstance(1, "123");System.out.println(inst);輸出:Type:Default Constructor ExtendType:Constructor with parameters com.quincy.ExtendType@15e83f96、調用類的函數
通過反射獲取類Method方法對象,調用Field的Invoke方法調用函數(方法)。
參見上面獲取類的方法;
7、設置/獲取類的屬性值
通過反射獲取類的成員變量,調用成員變量的方法設置或獲取值;
MyA m1=new MyA("zhangsan"); MyA m2=new MyA("lisi");Class clazz=m1.getClass(); //獲得MyA類的所有成員變量 Field[] fields=clazz.getFields();//獲得MyA下名為"name"的成員變量 Field nameField=clazz.getField("name"); 以上適用于用public修飾的成員變量,如果成員變量使用private修飾的則拿不到;要用以下方法://nameField是一個引用(地址) Field nameField=clazz.getDeclaredField("name"); //如果該成員變量使用private修飾的,要加下面一句 nameField.setAccessible("true");------------------要指定獲得哪一個對象上的數據 //獲得m1中nameField對應的成員變量的名字 String name=nameField.get(m1);//獲得m2中nameField對應的成員變量的名字 String name=nameField.get(m2);--------------------為m1中的成員變量設值 nameField.set(m1,"張三");//覆蓋掉zhangsan //獲得nameField對應的成員變量的類型 nameField.getType();四、動態(tài)創(chuàng)建代理類
代理模式:代理模式的作用=為其他對象提供一種代理以控制對這個對象的訪問。
代理模式的角色:
抽象角色:聲明真實對象和代理對象的共同接口
代理角色:代理角色內部包含有真實對象的引用,從而可以操作真實對象。
真實角色:代理角色所代表的真實對象,是我們最終要引用的對象。
動態(tài)代理::
java.lang.reflect.Proxy
Proxy 提供用于創(chuàng)建動態(tài)代理類和實例的靜態(tài)方法,它還是由這些方法創(chuàng)建的所有動態(tài)代理類的超類
InvocationHandler
是代理實例的調用處理程序實現的接口,每個代理實例都具有一個關聯的調用處理程序。對代理實例調用方法時,將對方法調用進行編碼并將其指派到它的調用處理程序的 invoke 方法。
動態(tài)Proxy是這樣的一種類:
它是在運行生成的類,在生成時你必須提供一組Interface給它,然后該class就宣稱它實現了這些interface。你可以把該class的實例當作這些interface中的任何一個來用。當然,這個Dynamic Proxy其實就是一個Proxy,它不會替你作實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。
在使用動態(tài)代理類時,我們必須實現ndler接口
有關代理模式的具體了解請參看本人設計模式相關文章。
補充:
1、
`MyA a1=new MyA();
Class clazz=a1.getClass();
MyA a2=new MyA();
if(a1 == a2)…//不相等,比較的是引用(地址)
if(clazz == a2.getClass())…//相等(是由同一個字節(jié)碼文
件構建的,都相當于:new MyA().getClass())
`
2、拿到一個字節(jié)碼文件之后,如何判斷它是一個對象類型還是普通的數據類型?
//基礎數據類型返回true,對象類型返回false clazz.isPrimitive();//falseint.class.inPrimitive();//true總結
以上是生活随笔為你收集整理的详解Java反射机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Spring学习】Spring的AOP
- 下一篇: 【Java线程】Thread Runn