鶴壁網(wǎng)站推廣公司seo資訊網(wǎng)
Java反射機(jī)制是 Java 語(yǔ)言的一個(gè)重要特性。
在學(xué)習(xí) Java 反射機(jī)制前,大家應(yīng)該先了解兩個(gè)概念,編譯期和運(yùn)行期。
編譯期是指把源碼交給編譯器編譯成計(jì)算機(jī)可以執(zhí)行的文件的過(guò)程。在 Java 中也就是把 Java 代碼編成 class 文件的過(guò)程。編譯期只是做了一些翻譯功能,并沒(méi)有把代碼放在內(nèi)存中運(yùn)行起來(lái),而只是把代碼當(dāng)成文本進(jìn)行操作,比如檢查錯(cuò)誤。
運(yùn)行期是把編譯后的文件交給計(jì)算機(jī)執(zhí)行,直到程序運(yùn)行結(jié)束。所謂運(yùn)行期就把在磁盤中的代碼放到內(nèi)存中執(zhí)行起來(lái)。
反射機(jī)制是什么?
Java反射機(jī)制是指在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法。
對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性,這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語(yǔ)言的反射機(jī)制。
用一句話總結(jié)就是反射可以實(shí)現(xiàn)在運(yùn)行時(shí)可以知道任意一個(gè)類的屬性和方法。
為什么要用反射?
Java 反射機(jī)制主要提供了以下功能,這些功能都位于java.lang.reflect
包比如:
Java 反射機(jī)制主要包括以下幾個(gè)方面:
- 獲取類的信息:反射機(jī)制允許程序在運(yùn)行時(shí)獲取一個(gè)類的信息,例如:類的名稱、修飾符、父類、接口、屬性和方法等。
- 創(chuàng)建對(duì)象:通過(guò)反射機(jī)制,程序可以在運(yùn)行時(shí)創(chuàng)建一個(gè)類的實(shí)例對(duì)象,而不需要在編譯時(shí)知道這個(gè)類的類型。
- 訪問(wèn)屬性:通過(guò)反射機(jī)制,程序可以在運(yùn)行時(shí)訪問(wèn)一個(gè)對(duì)象的屬性,例如:獲取屬性值、設(shè)置屬性值、獲取屬性的類型和修飾符等。
- 調(diào)用方法:通過(guò)反射機(jī)制,程序可以在運(yùn)行時(shí)調(diào)用一個(gè)對(duì)象的方法,例如:獲取方法的參數(shù)、調(diào)用方法、獲取方法的返回值等。
- 修改訪問(wèn)權(quán)限:通過(guò)反射機(jī)制,程序可以在運(yùn)行時(shí)修改對(duì)象的訪問(wèn)權(quán)限,例如:設(shè)置屬性或方法的訪問(wèn)權(quán)限為 public、private 或 protected。
Java 反射機(jī)制在實(shí)際應(yīng)用中有廣泛的用途,例如:框架、動(dòng)態(tài)代理、注解處理器等。反射機(jī)制雖然功能強(qiáng)大,但是也有一些缺點(diǎn),例如:性能較低、安全性較差等,因此在使用時(shí)需要注意。
要想知道一個(gè)類的屬性和方法,必須先獲取到該類的字節(jié)碼文件對(duì)象。獲取類的信息時(shí),使用的就是 Class 類中的方法。所以先要獲取到每一個(gè)字節(jié)碼文件(.class)對(duì)應(yīng)的 Class 類型的對(duì)象。
眾所周知,所有 Java 類均繼承了 Object 類,在 Object 類中定義了一個(gè) getClass() 方法,該方法返回同一個(gè)類型為 Class 的對(duì)象。
表列出了通過(guò)反射可以訪問(wèn)的信息。
類型 | 訪問(wèn)方法 | 返回值類型 | 說(shuō)明 |
---|---|---|---|
包路徑 | getPackage() | Package?對(duì)象 | 獲取該類的存放路徑 |
類名稱 | getName() | String 對(duì)象 | 獲取該類的名稱 |
繼承類 | getSuperclass() | Class 對(duì)象 | 獲取該類繼承的類 |
實(shí)現(xiàn)接口 | getlnterfaces() | Class 型數(shù)組 | 獲取該類實(shí)現(xiàn)的所有接口 |
構(gòu)造方法 | getConstructors() | Constructor?型數(shù)組 | 獲取所有權(quán)限為 public 的構(gòu)造方法 |
getDeclaredContruectors() | Constructor?對(duì)象 | 獲取當(dāng)前對(duì)象的所有構(gòu)造方法 | |
方法 | getMethods() | Methods 型數(shù)組 | 獲取所有權(quán)限為 public 的方法 |
getDeclaredMethods() | Methods?對(duì)象 | 獲取當(dāng)前對(duì)象的所有方法 | |
成員變量 | getFields() | Field 型數(shù)組 | 獲取所有權(quán)限為 public 的成員變量 |
getDeclareFileds() | Field 對(duì)象 | 獲取當(dāng)前對(duì)象的所有成員變量 | |
內(nèi)部類 | getClasses() | Class 型數(shù)組 | 獲取所有權(quán)限為 public 的內(nèi)部類 |
getDeclaredClasses() | Class 型數(shù)組 | 獲取所有內(nèi)部類 | |
內(nèi)部類的聲明類 | getDeclaringClass() | Class 對(duì)象 | 如果該類為內(nèi)部類,則返回它的成員類,否則返回 null |
反射機(jī)制的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 能夠運(yùn)行時(shí)動(dòng)態(tài)獲取類的實(shí)例,大大提高系統(tǒng)的靈活性和擴(kuò)展性。
- 與 Java 動(dòng)態(tài)編譯相結(jié)合,可以實(shí)現(xiàn)無(wú)比強(qiáng)大的功能。
- 對(duì)于 Java 這種先編譯再運(yùn)行的語(yǔ)言,能夠讓我們很方便的創(chuàng)建靈活的代碼,這些代碼可以在運(yùn)行時(shí)裝配,無(wú)需在組件之間進(jìn)行源代碼的鏈接,更加容易實(shí)現(xiàn)面向?qū)ο蟆?/li>
缺點(diǎn):
- 反射會(huì)消耗一定的系統(tǒng)資源,因此,如果不需要?jiǎng)討B(tài)地創(chuàng)建一個(gè)對(duì)象,那么就不需要用反射;
- 反射調(diào)用方法時(shí)可以忽略權(quán)限檢查,獲取這個(gè)類的私有方法和屬性,因此可能會(huì)破壞類的封裝性而導(dǎo)致安全問(wèn)題。
?反射機(jī)制API
java.lang.Class 類
java.lang.Class 類是實(shí)現(xiàn)反射的關(guān)鍵所在,Class 類的一個(gè)實(shí)例表示 Java 的一種數(shù)據(jù)類型,包括類、接口、枚舉、注解(Annotation)、數(shù)組、基本數(shù)據(jù)類型和 void。Class 沒(méi)有公有的構(gòu)造方法,Class 實(shí)例是由 JVM 在類加載時(shí)自動(dòng)創(chuàng)建的。
在程序代碼中獲得 Class 實(shí)例可以通過(guò)如下代碼實(shí)現(xiàn):
// 1. 通過(guò)類型class靜態(tài)變量
Class clz1 = String.class;
String str = "Hello";
// 2. 通過(guò)對(duì)象的getClass()方法
Class clz2 = str.getClass();
每一種類型包括類和接口等,都有一個(gè) class 靜態(tài)變量可以獲得 Class 實(shí)例。另外,每一個(gè)對(duì)象都有 getClass() 方法可以獲得 Class 實(shí)例,該方法是由 Object 類提供的實(shí)例方法。
Class 類提供了很多方法可以獲得運(yùn)行時(shí)對(duì)象的相關(guān)信息,下面的程序代碼展示了其中一些方法。
public class ReflectionTest01 {public static void main(String[] args) {// 獲得Class實(shí)例// 1.通過(guò)類型class靜態(tài)變量Class clz1 = String.class;String str = "Hello";// 2.通過(guò)對(duì)象的getClass()方法Class clz2 = str.getClass();// 獲得int類型Class實(shí)例Class clz3 = int.class;// 獲得Integer類型Class實(shí)例Class clz4 = Integer.class;System.out.println("clz2類名稱:" + clz2.getName());System.out.println("clz2是否為接口:" + clz2.isInterface());System.out.println("clz2是否為數(shù)組對(duì)象:" + clz2.isArray());System.out.println("clz2父類名稱:" + clz2.getSuperclass().getName());System.out.println("clz2是否為基本類型:" + clz2.isPrimitive());System.out.println("clz3是否為基本類型:" + clz3.isPrimitive());System.out.println("clz4是否為基本類型:" + clz4.isPrimitive());}
}
運(yùn)行結(jié)果如下:
clz2類名稱:java.lang.String
clz2是否為接口:false
clz2是否為數(shù)組對(duì)象:false
clz2父類名稱:java.lang.Object
clz2是否為基本類型:false
clz3是否為基本類型:true
clz4是否為基本類型:false
java.lang.reflect 包
java.lang.reflect 包提供了反射中用到類,主要的類說(shuō)明如下:
- Constructor 類:提供類的構(gòu)造方法信息。
- Field 類:提供類或接口中成員變量信息。
- Method 類:提供類或接口成員方法信息。
- Array 類:提供了動(dòng)態(tài)創(chuàng)建和訪問(wèn) Java 數(shù)組的方法。
- Modifier 類:提供類和成員訪問(wèn)修飾符信息。
示例代碼如下:
public class ReflectionTest02 {public static void main(String[] args) {try {// 動(dòng)態(tài)加載xx類的運(yùn)行時(shí)對(duì)象Class c = Class.forName("java.lang.String");// 獲取成員方法集合Method[] methods = c.getDeclaredMethods();// 遍歷成員方法集合for (Method method : methods) {// 打印權(quán)限修飾符,如public、protected、privateSystem.out.print(Modifier.toString(method.getModifiers()));// 打印返回值類型名稱System.out.print(" " + method.getReturnType().getName() + " ");// 打印方法名稱System.out.println(method.getName() + "();");}} catch (ClassNotFoundException e) {System.out.println("找不到指定類");}}
}
通過(guò)反射訪問(wèn)構(gòu)造方法
為了能夠動(dòng)態(tài)獲取對(duì)象構(gòu)造方法的信息,首先需要通過(guò)下列方法之一創(chuàng)建一個(gè) Constructor
類型的對(duì)象或者數(shù)組。
- getConstructors()
- getConstructor(Class<?>…parameterTypes)
- getDeclaredConstructors()
- getDeclaredConstructor(Class<?>...parameterTypes)
如果是訪問(wèn)指定的構(gòu)造方法,需要根據(jù)該構(gòu)造方法的入口參數(shù)的類型來(lái)訪問(wèn)。例如,訪問(wèn)一個(gè)入口參數(shù)類型依次為 int 和 String 類型的構(gòu)造方法,下面的兩種方式均可以實(shí)現(xiàn)。
1、objectClass.getDeclaredConstructor(int.class,String.class);
2、objectClass.getDeclaredConstructor(new Class[]{int.class,String.class});
創(chuàng)建的每個(gè) Constructor 對(duì)象表示一個(gè)構(gòu)造方法,然后利用 Constructor 對(duì)象的方法操作構(gòu)造方法。Constructor 類的常用方法如表所示。
方法名稱 | 說(shuō)明 |
---|---|
isVarArgs() | 查看該構(gòu)造方法是否允許帶可變數(shù)量的參數(shù),如果允許,返回 true,否則返回 false |
getParameterTypes() | 按照聲明順序以 Class 數(shù)組的形式獲取該構(gòu)造方法各個(gè)參數(shù)的類型 |
getExceptionTypes() | 以 Class 數(shù)組的形式獲取該構(gòu)造方法可能拋出的異常類型 |
newInstance(Object … initargs) | 通過(guò)該構(gòu)造方法利用指定參數(shù)創(chuàng)建一個(gè)該類型的對(duì)象,如果未設(shè)置參數(shù)則表示 采用默認(rèn)無(wú)參的構(gòu)造方法 |
setAccessiable(boolean flag) | 如果該構(gòu)造方法的權(quán)限為 private,默認(rèn)為不允許通過(guò)反射利用?netlnstance() 方法創(chuàng)建對(duì)象。如果先執(zhí)行該方法,并將入口參數(shù)設(shè)置為?true,則允許創(chuàng)建對(duì) 象 |
getModifiers() | 獲得可以解析出該構(gòu)造方法所采用修飾符的整數(shù) |
通過(guò) java.lang.reflect.Modifier 類可以解析出 getMocMers() 方法的返回值所表示的修飾符信息。在該類中提供了一系列用來(lái)解析的靜態(tài)方法,既可以查看是否被指定的修飾符修飾,還可以字符串的形式獲得所有修飾符。表列出了 Modifier 類的常用靜態(tài)方法。
靜態(tài)方法名稱 | 說(shuō)明 | |
---|---|---|
isStatic(int mod) | 如果使用 static 修飾符修飾則返回 true,否則返回 false | |
isPublic(int mod) | 如果使用 public 修飾符修飾則返回 true,否則返回 false | |
isProtected(int mod) | 如果使用 protected 修飾符修飾則返回 true,否則返回 false | |
isPrivate(int mod) | 如果使用 private 修飾符修飾則返回 true,否則返回 false | |
isFinal(int mod) | 如果使用 final 修飾符修飾則返回 true,否則返回 false | |
toString(int mod) | 以字符串形式返回所有修飾符 |
例如,下列代碼判斷對(duì)象 con 所代表的構(gòu)造方法是否被 public 修飾,以及以字符串形式獲取該構(gòu)造方法的所有修飾符。
int modifiers = con.getModifiers(); // 獲取構(gòu)造方法的修飾符整數(shù)
boolean isPublic = Modifier.isPublic(modifiers); // 判斷修飾符整數(shù)是否為public
string allModifiers = Modifier.toString(modifiers);
通過(guò)反射執(zhí)行方法(獲取方法)
要動(dòng)態(tài)獲取一個(gè)對(duì)象方法的信息,首先需要通過(guò)下列方法之一創(chuàng)建一個(gè) Method
類型的對(duì)象或者數(shù)組。
- getMethods()
- getMethods(String name,Class<?> …parameterTypes)
- getDeclaredMethods()
- getDeclaredMethods(String name,Class<?>...parameterTypes)
如果是訪問(wèn)指定的構(gòu)造方法,需要根據(jù)該方法的入口參數(shù)的類型來(lái)訪問(wèn)。例如,訪問(wèn)一個(gè)名稱為 max,入口參數(shù)類型依次為 int 和 String 類型的方法。
下面的兩種方式均可以實(shí)現(xiàn):
1、objectClass.getDeclaredConstructor("max",int.class,String.class);
2、objectClass.getDeclaredConstructor("max",new Class[]{int.class,String.class});
Method 類的常用方法如表 3 所示。
靜態(tài)方法名稱 | 說(shuō)明 |
---|---|
getName() | 獲取該方法的名稱 |
getParameterType() | 按照聲明順序以 Class 數(shù)組的形式返回該方法各個(gè)參數(shù)的類型 |
getReturnType() | 以 Class 對(duì)象的形式獲得該方法的返回值類型 |
getExceptionTypes() | 以 Class 數(shù)組的形式獲得該方法可能拋出的異常類型 |
invoke(Object obj,Object...args) | 利用 args 參數(shù)執(zhí)行指定對(duì)象 obj 中的該方法,返回值為 Object 類型 |
isVarArgs() | 查看該方法是否允許帶有可變數(shù)量的參數(shù),如果允許返回 true,否則返回 false |
getModifiers() | 獲得可以解析出該方法所采用修飾符的整數(shù) |
通過(guò)反射訪問(wèn)成員變量?
通過(guò)下列任意一個(gè)方法訪問(wèn)成員變量時(shí)將返回 Field 類型的對(duì)象或數(shù)組。
- getFields()
- getField(String name)
- getDeclaredFields()
- getDeclaredField(String name)
上述方法返回的 Field 對(duì)象代表一個(gè)成員變量。例如,要訪問(wèn)一個(gè)名稱為 price 的成員變量,示例代碼如下:
object.getDeciaredField("price");
Field 類的常用方法如表所示
方法名稱 | 說(shuō)明 |
---|---|
getName() | 獲得該成員變量的名稱 |
getType() | 獲取表示該成員變量的 Class 對(duì)象 |
get(Object obj) | 獲得指定對(duì)象 obj 中成員變量的值,返回值為 Object 類型 |
set(Object obj, Object value) | 將指定對(duì)象 obj 中成員變量的值設(shè)置為 value |
getlnt(0bject obj) | 獲得指定對(duì)象 obj 中成員類型為 int 的成員變量的值 |
setlnt(0bject obj, int i) | 將指定對(duì)象 obj 中成員變量的值設(shè)置為 i |
setFloat(Object obj, float f) | 將指定對(duì)象 obj 中成員變量的值設(shè)置為 f |
getBoolean(Object obj) | 獲得指定對(duì)象 obj 中成員類型為 boolean 的成員變量的值 |
setBoolean(Object obj, boolean b) | 將指定對(duì)象 obj 中成員變量的值設(shè)置為 b |
getFloat(Object obj) | 獲得指定對(duì)象 obj 中成員類型為 float 的成員變量的值 |
setAccessible(boolean flag) | 此方法可以設(shè)置是否忽略權(quán)限直接訪問(wèn) private 等私有權(quán)限的成員變量 |
getModifiers() | 獲得可以解析出該方法所采用修飾符的整數(shù) |