反射java_Java反射原理
點擊上方藍色“Panda誠”,選擇“設為星標”
有了反射,我們的Java世界才變得更加多姿多彩。
Java反射是什么
Java的反射(reflection)機制是指在程序的運行狀態中,可以構造任意一個類的對象,可以了解任意一個對象所屬的類,可以了解任意一個類的成員變量和方法,可以調用任意一個對象的屬性和方法。這種動態獲取程序信息以及動態調用對象的功能稱為Java語言的反射機制。反射被視為動態語言的關鍵。
我理解的Java反射的原理就是獲取Class對象然后使用java.lang.reflect里提供的方法操作Class對象,Class與java.lang.reflect構成了java的反射技術。
基礎理論
A類 -> A.class字節碼文件 -> 加載到JVM后的A字節碼文件對象(Class對象)
A的Class對象 -> A的實例
Class是反射的基礎
當new一個新對象或者引用靜態成員變量等時機時,JVM類加載器系統會將對應Class對象加載到JVM中,然后JVM根據Class對象創建實例對象或者提供靜態變量的引用值。
每個類,無論創建多少個實例,在JVM中都對應同一個Class對象(類被不同的類加載器加載除外)。
對于字節碼文件的加載時機,《Java虛擬機規范》中并沒有進行強制約束,這點可以交給虛擬機的具體實現來自由把握。但是對于初始化階段,《Java虛擬機規范》則是嚴格規定了有且只有六種情況必須立即對類進行“初始化”(而加載、驗證、準備自然需要在此之前開始):
遇到new、getstatic、putstatic或invokestatic這四條字節碼指令時,如果類型沒有進行過初始化,則需要先觸發其初始化階段。
使用new關鍵字實例化對象的時候。
讀取或設置一個類型的靜態字段(被final修飾、已在編譯期把結果放入常量池的靜態字段除外)的時候。
調用一個類型的靜態方法的時候。
使用java.lang.reflect包的方法對類型進行反射調用的時候,如果類型沒有進行過初始化,則需要先觸發其初始化。
當初始化類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
當使用JDK 7新加入的動態語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的解析結果為REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四種類型的方法句柄,并且這個方法句柄對應的類沒有進行過初始化,則需要先觸發其初始化。
當一個接口中定義了JDK 8新加入的默認方法(被default關鍵字修飾的接口方法)時,如果有這個接口的實現類發生了初始化,那該接口要在其之前被初始化。
這六種場景中的行為稱為對一個類型進行主動引用。除此之外,所有引用類型的方式都不會觸發初始化,稱為被動引用。
被動引用不會導致初始化,但往往也是需要加載的,我們舉一些例子:
使用類加載器的loadClass()方法,不做類的初始化工作
類型.class字面量
子類訪問父類的靜態字段(不會導致子類初始化,會導致父類初始化)
通過數組定義來引用類,不會觸發此類的初始化
常量在編譯階段會存入調用類的常量池中,本質上沒有直接引用到定義常量的類,因此不會觸發定義常量的類的初始化
對于HotSpot虛擬機來說,可通過-XX:+TraceClassLoading參數觀察到類是否會加載。而初始化時執行的是()方法,我們可以編寫靜態代碼塊來驗證此類是否初始化了。
初始化階段就是執行類構造器()方法的過程。()并不是程序員在Java代碼中直接編寫的方法,它是Javac編譯器的自動生成物。()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊(static{}塊)中的語句合并產生的,編譯器收集的順序是由語句在源文件中出現的順序決定的。
這里我們簡單舉例訪問類型.class字面量引發此類被加載而沒有引發初始化。
package?com.myspring.service.impl;public?class?A?{
????static?{
????????System.out.println("初始化A類");
????}
}
package?com.myspring.service.impl;
public?class?ClassLoadTest?{
????public?static?void?main(String?args[]){
????????System.out.println("===================");
????????System.out.println("===================");
????????Class?c1?=?A.class;
????????System.out.println("===================");
????????System.out.println("===================");
????}
}
編譯后,在相應的目錄(比如target/classes)下執行如下命令
java?-XX:+TraceClassLoading?-cp?.?com.myspring.service.impl.ClassLoadTest輸出結果如下
......省略[Loaded?java.security.BasicPermissionCollection?from?/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded?com.myspring.service.impl.ClassLoadTest?from?file:/Users/zhangcheng/IdeaProjects/SpringSourceCode/Spring-Framework/mySpring/target/classes/]
[Loaded?sun.launcher.LauncherHelper$FXHelper?from?/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded?java.lang.Class$MethodArray?from?/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded?java.lang.Void?from?/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
===================
===================
[Loaded?com.myspring.service.impl.A?from?file:/Users/zhangcheng/IdeaProjects/SpringSourceCode/Spring-Framework/mySpring/target/classes/]
===================
===================
[Loaded?java.lang.Shutdown?from?/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded?java.lang.Shutdown$Lock?from?/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar]
看控制臺輸出可以看到,只加載了A類型,而沒有初始化它。
如何獲取一個Class對象
我們先新建一個Java Bean,作為要獲取的CLass對象的類型。
public?class?MyTestBean?{????public?String?testStr?=?"testStr";
????public?String?getTestStr()?{
????????return?testStr;
????}
????public?void?setTestStr(String?testStr)?{
????????this.testStr?=?testStr;
????}
}
對象.getClass()
new 一個MyTestBean實例對象,通過對象.getClass()獲得Class對象
@Testpublic?void?test(){
????MyTestBean?myTestBean1?=?new?MyTestBean();
????Class?c1?=?myTestBean1.getClass();
????System.out.print("獲得的Class對象:");
????System.out.println(c1);
}
獲得的Class對象:class?com.myspring.service.impl.MyTestBean
類型.class字面量
@Testpublic?void?test(){
????Class?c1?=?MyTestBean.class;
????System.out.print("獲得的Class對象:");
????System.out.println(c1);
}
獲得的Class對象:class?com.myspring.service.impl.MyTestBean
Class類的forName方法
@Testpublic?void?test()?throws?ClassNotFoundException?{
????Class?c1?=?Class.forName("com.myspring.service.impl.MyTestBean");
????System.out.print("獲得的Class對象:");
????System.out.println(c1);
}
獲得的Class對象:class?com.myspring.service.impl.MyTestBean
類加載器的loadClass方法
這是反射里基本上不會用到的獲取Class對象的方法。
@Testpublic?void?test()?throws?ClassNotFoundException?{
????Class?c1?=?this.getClass().getClassLoader().loadClass("com.myspring.service.impl.MyTestBean");
????System.out.print("獲得的Class對象:");
????System.out.println(c1);
}
獲得的Class對象:class?com.myspring.service.impl.MyTestBean
如何操作一個Class對象
想要掌握反射技術,我們至少還要了解 Class``Constructor Method Field四個類及其常用方法
Class類的常用方法
最好的教材就是源碼,以下方法是從源碼摘取出來,需要重點學習和關注的。
/***?使用給定的字符串名稱返回與類或接口關聯的Class對象。?調用此方法等效于:Class.forName(className, true, currentLoader)currentLoader表示當前類的定義類加載器。
*?調用forName“X”)會導致初始化名為X的類。
*?@param????? className ??所需類的完全限定名稱。
*?@return?????具有指定名稱的類的Class對象。
*?......
*/
public?static?Class>?forName(String?className)?throws?ClassNotFoundException?{
Class>?caller?=?Reflection.getCallerClass();
return?forName0(className,?true,?ClassLoader.getClassLoader(caller),?caller);
}
/**
*?使用給定的類加載器返回與具有給定字符串名稱的類或接口關聯的Class對象。
*?給定類或接口的完全限定名稱(采用getName返回的相同格式),此方法嘗試查找,加載和鏈接該類或接口。指定的類加載器用于加載類或接口。??
*?如果參數loader為null,則通過引導類加載器加載該類。僅當initialize參數為true且之前尚未初始化時,才初始化該類。
*?如果name表示原始類型或void,則將嘗試在名稱為name的未命名包中定位用戶定義的類。?因此,該方法不能用于獲取表示原始類型或void的任何Class對象。
*?如果name表示數組類,則該數組類的組件類型已加載但未初始化。
*?請注意,此方法會引發與加載,鏈接或初始化有關的錯誤
*?請注意,此方法不會檢查其調用者是否可以訪問所請求的類。
*?如果loader為null,并且存在安全管理器,并且調用方的類加載器不為null,則此方法使用RuntimePermission(“getClassLoader”)權限,以確保可以訪問引導程序類加載器。
*?@param?initialize 如果true,則將初始化該類。
*?@param?loader?????類加載器
*?@return???????????代表所需類的類對象
*?@exception?LinkageError?ExceptionInInitializerError?ClassNotFoundException
*/
public?static?Class>?forName(String?name,?boolean?initialize,?ClassLoader?loader)?throws?ClassNotFoundException{
......
return?forName0(name,?initialize,?loader,?caller);
}
/**
*?創建此Class對象表示的類的新實例。就像通過帶有空參數列表的new表達式實例化該類一樣。如果尚未初始化該類,則將其初始化。
*?請注意,此方法傳播由null構造函數引發的任何異常,包括已檢查的異常。使用此方法有效地繞過了編譯時異常檢查,否則該檢查將由編譯器執行。
*?(java.lang.reflect.Constructor)Constructor.newInstance方法通過將構造函數拋出的所有異常包裝在java.lang.reflect.InvocationTargetException中從而避免了此問題。
*?@return??該對象表示的類的新分配實例。
*?@throws? IllegalAccessException ?如果該類或其無效構造函數不可訪問。
*?@throws? InstantiationException ?如果此Class表示抽象類,接口,數組類,原始類型或void;或如果類沒有空構造函數;或者或實例化由于其他原因而失敗。
*?@throws? ExceptionInInitializerError 如果此方法引發的初始化失敗。
*?@throws? SecurityException 如果存在安全管理器,并且調用者的類加載器與當前類的調用者的類加載器不同,并且調用SecurityManager#checkPackageAccess拒絕訪問此類的程序包。
*/
public?T?newInstance()?throws?InstantiationException,?IllegalAccessException{
......
if?(cachedConstructor?==?null)?{
????if?(this?==?Class.class)?{
????????throw?new?IllegalAccessException("Can?not?call?newInstance()?on?the?Class?for?java.lang.Class");
????}
????try?{
????????Class>[]?empty?=?{};
????????final?Constructor?c?=?getConstructor0(empty,?Member.DECLARED);
????????.......
????????cachedConstructor?=?c;
????}?catch?(NoSuchMethodException?e)?{throw?(InstantiationException)new?InstantiationException(getName()).initCause(e);
????}
}
Constructor?tmpConstructor?=?cachedConstructor;//?安全性檢查(與java.lang.reflect.Constructor中的相同)
......//?運行構造函數try?{return?tmpConstructor.newInstance((Object[])null);
}?catch?(InvocationTargetException?e)?{
????......
}
}/**
*?返回一個包含Class對象的數組,這些對象表示此Class對象表示的類的所有public類和接口成員。這包括從超類繼承的public類和接口成員,以及由該類聲明的公共類和接口成員。如果此Class對象沒有public成員類或接口,則此方法返回長度為0的數組。如果此Class對象表示原始類型,數組類或void,則此方法還返回長度為0的數組。
*/public?Class>[]?getClasses()?{......}/**
*?返回一組Class對象的數組,這些對象反映了聲明為該Class對象表示的類的成員的所有類和接口。這包括public, protected, default (package) access,private類和接口,但不包括繼承的類和接口。如果類未聲明任何類或接口作為成員,或者此Class對象表示原始類型,數組類或void,則此方法返回長度為0的數組。
*/public?Class>[]?getDeclaredClasses()?throws?SecurityException?{......}/**
*?返回一個包含Field對象的數組,這些對象反映了此Class對象表示的類或接口的所有可訪問public字段。
*?如果此Class對象表示沒有可訪問的public字段的類或接口,則此方法返回長度為0的數組。
*?如果此Class對象表示一個類,則此方法返回該類及其所有超類的public字段。
*?如果此對象表示一個接口,則此方法返回該接口及其所有超級接口的字段。
*?如果此Class對象表示數組類型,原始類型或void,則此方法返回長度為0的數組。
*?返回數組中的元素未排序,并且沒有任何特定順序。
*/public?Field[]?getFields()?throws?SecurityException?{......}/**
*?返回Field對象的數組,該數組反映由這個class對象表示的類或接口聲明的所有字段。這包括public、protected、default(package)access和private字段,但不包括繼承的字段。
*?如果這個Class對象表示沒有聲明字段的類或接口,則此方法返回長度為0的數組。
*?如果這個Class對象表示數組類型、基元類型或void,則此方法返回長度為0的數組。
*?返回數組中的元素沒有排序,并且沒有任何特定的順序。
*/public?Field[]?getDeclaredFields()?throws?SecurityException?{......}/**
*?返回一個Field對象,該對象反映由這個class對象表示的類或接口的指定public成員字段。name參數是一個String,指定所需字段的簡單名稱。
*?要反射的場由下面的算法確定。設C為該對象表示的類或接口:
* 1.如果C用指定的名稱聲明了一個public字段,則該字段就是要反映的字段。
* 2.如果在上面的步驟1中找不到任何字段,則此算法遞歸地應用于C的每個直接上接口。直接上接口按聲明的順序進行搜索。
* 3.如果在上面的步驟1和2中找不到字段,并且C有超類S,則此算法將在S上遞歸調用。如果C沒有超類,則拋出NoSuchFieldException。
*?如果這個Class對象表示數組類型,則此方法找不到數組類型的length字段。
*/public?Field?getField(String?name)?throws?NoSuchFieldException,?SecurityException?{......}/**
*?返回一個Field對象,該對象反映此Class對象表示的類或接口的指定聲明字段。name參數是一個String,它指定所需字段的簡單名稱。
*?如果此Class對象表示數組類型,則此方法找不到數組類型的length字段。
*/public?Field?getDeclaredField(String?name)throws?NoSuchFieldException,?SecurityException?{......}/**
*?返回一個數組,該數組包含Method對象,這些對象反映由該class對象表示的類或接口的所有public方法,包括由類或接口聲明的方法以及從超類和超接口繼承的那些方法。
*?如果這個Class對象表示一個類型,該類型具有多個具有相同名稱和參數類型但返回類型不同的public方法,則返回的數組對這些每個方法都有一個Method對象。
*?如果這個Class對象表示一個具有類初始化方法的類型,則返回的數組沒有與之()相應的method對象。
*?如果這個Class對象表示數組類型,那么返回的數組對于數組類型從object繼承的每個public方法都有一個Method對象。它不包含clone()的Method對象。
*?如果這個Class對象表示接口,則返回的數組不包含object隱式聲明的任何方法。因此,如果此接口或其任何一個超接口中沒有顯式聲明方法,則返回的數組的長度為0。(請注意,表示類的Class對象始終具有從object繼承的public方法。)
*?如果這個Class對象表示基元類型或void,則返回的數組的長度為0。
*?在此class對象表示的類或接口的上層接口中聲明的靜態方法不被視為類或接口的成員。
*?返回數組中的元素沒有排序,并且沒有任何特定的順序。
*/public?Method[]?getMethods()?throws?SecurityException?{......}/**
*?返回一個數組,該數組包含Method對象,這些對象反映由該class對象表示的類或接口的所有已聲明方法,包括public, protected, default (package) access, and private方法,但不包括繼承的方法。
*?如果這個Class對象表示一個類型,該類型具有多個聲明的方法,這些方法具有相同的名稱和參數類型,但返回類型不同,則返回的數組對這些方法每個都有一個Method對象。
*?如果這個Class對象表示一個具有類初始化方法的類型,則返回的數組沒有與之()相應的method對象。
*?如果這個Class對象表示沒有聲明方法的類或接口,則返回的數組的長度為0。
*?如果此Class對象表示數組類型,原始類型或void,則返回的數組長度為0。
*?返回數組中的元素沒有排序,并且沒有任何特定的順序。
*/public?Method[]?getDeclaredMethods()?throws?SecurityException?{......}/**
*?返回一個Method對象,該對象反映由該class對象表示的類或接口的指定public成員方法。name參數是一個String,指定所需方法的簡單名稱。parameterTypes參數是一個Class對象的數組,這些對象按聲明的順序標識方法的形式參數類型。如果parameterTypes是null,則將其視為空數組。
*?如果name是“}”或“}”,則引發NoSuchMethodException。否則,要反映的方法由下面的算法確定。設C為該對象表示的類或接口:
* 1. C搜索匹配方法,如下所述。如果找到匹配的方法,它將被反映出來。
* 2. 如果在步驟1中未找到匹配方法,則:
*?2.1如果C是Object以外的類,那么這個算法將在C的超類上遞歸調用.
* 2.2如果C是類Object,或者C是接口,那么將搜索C的上層接口(如果有)以查找匹配的方法。如果找到任何這樣的方法,它就會被反映出來。
*
*?要在類或接口C中查找匹配的方法,請執行以下操作:如果C聲明了一個具有指定名稱和完全相同形式參數類型的public方法,則該方法就是所反映的方法。如果在C中發現了不止一個這樣的方法,并且其中一個方法的返回類型比其他任何方法都更具體,則會反映該方法;否則任意選擇其中一個方法。
*?請注意,一個類中可能有多個匹配的方法,因為盡管Java語言禁止一個類聲明具有相同簽名但返回類型不同的多個方法,而Java虛擬機是不禁止的,這增加了虛擬機的靈活性,可用于實現各種語言功能。
*?如果這個Class對象表示數組類型,則此方法找不到clone方法。
*?在此class對象表示的類或接口的上層接口中聲明的靜態方法不被視為類或接口的成員。
*/public?Method?getMethod(String?name,?Class>...?parameterTypes)throws?NoSuchMethodException,?SecurityException?{......}/**
*?返回一個Method對象,該對象反映此Class對象表示的類或接口的指定聲明方法。name參數是一個String,用于指定所需方法的簡單名稱,而parameterTypes參數是一個Class對象的數組,這些對象標識該方法的形式參數類型,按聲明的順序。如果在一個類中聲明了一個以上具有相同參數類型的方法,并且其中一個方法的返回類型比其他方法更具體,則返回該方法。否則,可以選擇其中一種方法。如果名稱是“?”或“?”,則會引發{@code?NoSuchMethodException}。
*?如果此{@code?Class}對象表示數組類型,則此方法找不到{@code?clone()}方法。
*/public?Method?getDeclaredMethod(String?name,?Class>...?parameterTypes)throws?NoSuchMethodException,?SecurityException?{......}/**
*?返回一個數組,其中包含Constructor對象,這些對象反映由這個class對象表示的類的所有public構造函數。如果類沒有public構造函數,或者類是數組類,或者類反映基元類型或void,則返回長度為0的數組。
*?請注意,雖然此方法返回Constructor對象的數組(即該類中的構造函數數組),但該方法的返回類型是Constructor[]而不是Constructor[]。這種信息較少的返回類型是必需的,因為從該方法返回后,可以修改數組以保存不同類的Constructor對象,這將違反Constructor[]的類型保證。
*/public?Constructor>[]?getConstructors()?throws?SecurityException?{......}/**
*?返回Constructor對象的數組,該數組反映由這個class對象表示的類聲明的所有構造函數。它們是public、protected、default(package)access和private構造函數。
*?返回的數組中的元素沒有排序,并且沒有任何特定的順序。
*?如果類具有默認構造函數,則它將包含在返回的數組中。
*?如果class對象表示接口、基元類型、數組類或void,則此方法返回長度為0的數組。
*/public?Constructor>[]?getDeclaredConstructors()?throws?SecurityException?{......}/**
*?返回一個Constructor對象,該對象反映由這個class對象表示的類的指定public構造函數。parameterTypes參數是一個Class對象的數組,這些對象按聲明的順序標識構造函數的形式參數類型。
*?如果這個Class對象表示在非靜態上下文中聲明的內部類,則形式參數類型包括顯式封閉實例作為第一個參數。
*?要反映的構造函數是由這個class對象表示的類的public構造函數,該對象的形式參數類型與parameterTypes指定的參數類型匹配。
*/public?Constructor?getConstructor(Class>...?parameterTypes)throws?NoSuchMethodException,?SecurityException?{......}/**
*?返回一個Constructor對象,該對象反映此Class對象表示的類或接口的指定構造函數。parameterTypes參數是Class對象的數組,這些對象按聲明的順序標識構造函數的形式參數類型。
*?如果此Class對象表示在非靜態上下文中聲明的內部類,則形式參數類型包括顯式的封閉實例作為第一個參數。
*/public?Constructor?getDeclaredConstructor(Class>...?parameterTypes)throws?NoSuchMethodException,?SecurityException?{......}
通過上面的代碼注解,我們總結一下Class類
forName方法可以根據類的完全限定名稱獲取Class對象。會加載和連接,根據initialize參數決定是否初始化。(我們常用的不指定initialize就是默認初始化)
newInstance 創建此Class對象表示的類的新實例 內部實現是調用的Constructor.newInstance方法
getClasses 獲取在此Class對象對應的類型中聲明的public類或接口成員的Class對象數組,包括從超類繼承的public類和接口成員
getDeclaredClasses 獲取在此Class對象對應的類型中聲明的類或接口成員的Class對象數組,包括public, protected, default (package) access,private類和接口,但不包括繼承的類和接口
getFields 獲取此Class對象表示的類或接口的所有可訪問public字段Field數組 包括繼承的
getDeclaredFields 獲取此Class對象表示的類或接口的所有 public、protected、default(package)access和private字段Field數組 不包括繼承的
getField 獲取此Class對象表示的類或接口的指定的可訪問public字段Field對象 會遞歸向父類、父接口查
getDeclaredField 獲取此Class對象表示的類或接口的指定的public、protected、default(package)access和private字段Field對象 不包括繼承的
getMethods 獲取該class對象表示的類或接口的所有public方法Method數組 包括繼承的
getDeclaredMethods 獲取該class對象表示的類或接口的public, protected, default (package) access,private方法Method數組 不包括繼承的
getMethod 獲取該class對象表示的類或接口的指定的public方法Method數組 會遞歸向父類、父接口查
getDeclaredMethod 獲取該class對象表示的類或接口的指定的public方法Method數組 不包括繼承的
getConstructors 獲取這個class對象表示的類的所有public構造函數Constructor數組
getDeclaredConstructors 獲取這個class對象表示的類的所有public、protected、default(package)access和private 構造函數Constructor數組
getConstructor 獲取這個class對象表示的類的指定的public構造函數Constructor對象
getDeclaredConstructor 獲取這個class對象表示的類的指定的public、protected、default(package)access和private 構造函數Constructor對象
Constructor類的常用方法
/***?使用此Constructor對象表示的構造函數,使用指定的初始化參數創建和初始化構造函數的聲明類的新實例。各個參數將自動解包以匹配原始形式參數,并且原始參數和引用參數都必須根據需要進行方法調用轉換。
*?如果基礎構造函數所需的形式參數數量為0,則提供的initargs數組的長度可以為0或為null。
*?如果構造函數的聲明類是非靜態上下文中的內部類,則構造函數的第一個參數必須是封閉實例
*?如果所需的訪問和參數檢查成功,并且實例化將繼續,則構造函數的聲明類(如果尚未初始化)將被初始化。
*?如果構造函數正常完成,則返回新創建并初始化的實例。
*?@param?initargs 作為參數傳遞給構造函數調用的對象數組;基元類型的值包裝在適當類型的包裝對象中(例如float->java.lang.Float})
*?@return?通過調用一個新的構造函數來表示這個對象
*/
public?T?newInstance(Object?...?initargs)
Method的常用方法
/***?在具有指定參數的指定對象上調用此method對象表示的基礎方法。單個參數會自動展開以匹配原始形式參數,并且基本參數和引用參數都會根據需要進行方法調用轉換。
*?如果基礎方法是靜態的,那么指定的obj參數將被忽略,可以為null。
*?如果基礎方法所需的形式參數數為0,則提供的args數組的長度可能為0或null。
*?如果基礎方法是一個實例方法,則使用Java語言規范第二版第15.12.4.4節中所述的動態方法查找來調用它;特別是,將根據目標對象的運行時類型進行重寫。
*?如果基礎方法是靜態的,則聲明該方法的類在尚未初始化的情況下被初始化。
*?如果方法正常完成,它返回的值將返回給invoke的調用方;如果該值具有基元類型,則首先將其適當地包裝在對象中。但是,如果值的類型為基元類型的數組,返回一個基元類型的數組。如果基礎方法返回類型為void,則調用返回null。
*?@param?obj??從中調用基礎方法的對象
*?@param?args?用于方法調用的參數
*?@return?使用參數args在obj上分派此對象表示的方法的結果
*/
public?Object?invoke(Object?obj,?Object...?args)
Field的常用方法
Filed里主要是一些get set方法,比如set(Object obj, Object value) setBoolean(Object obj, boolean z) get(Object obj) getBoolean(Object obj)等等,obj是實例對象。
總結
我們先獲取到類型A的Class對象,通過Class對象的newInstance方法可以得到A的實例。通過Class對象可以獲取到Constructor對象,進一步可以使用Constructor對象來得到A的實例。通過Class對象可以獲取到Method對象,通過Method的invoke方法我們可以調用一些方法。通過Class對象可以獲取到Field對象,我們可以對這個實例的一些字段進行賦值取值操作。這樣我們就基本掌握了反射的使用方法了。
文章有幫助的話,小手一抖點擊在看,并轉發吧。
謝謝支持喲 (*^__^*)
總結
以上是生活随笔為你收集整理的反射java_Java反射原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 故障转移集群无法连接到节点_Redis集
- 下一篇: 产品开发专业认证_食品招生季食品科学与工