一、簡(jiǎn)介

1、概述
Java的反射是指在程序運(yùn)行過(guò)程中, 可以構(gòu)造任意一個(gè)類的對(duì)象, 獲取任意一個(gè)類的的所有屬性和方法, 可以調(diào)用任意一個(gè)類的屬性和方法。 這種動(dòng)態(tài)獲取程序信息和動(dòng)態(tài)調(diào)用程序?qū)ο?/strong>的功能稱為Java的反射機(jī)制。反射被視為動(dòng)態(tài)語(yǔ)言關(guān)鍵。
通常情況下, 我們想調(diào)用一個(gè)Java類的屬性和方法, 必須先實(shí)例化這個(gè)Java類的對(duì)象, 然后通過(guò)對(duì)象去調(diào)用該類的屬性和方法; 通過(guò)Java的反射機(jī)制, 我們可以在程序運(yùn)行時(shí), 動(dòng)態(tài)獲取類的信息, 調(diào)用類的屬性和方法, 完成對(duì)象的實(shí)例化等操作。
如圖所示, 介紹什么是反射:

-
什么是程序運(yùn)行時(shí)刻:
Hello.java經(jīng)過(guò)編譯器編譯,生成字節(jié)碼文件Hello.Class, 想要運(yùn)行該程序, 就需要JVM的類加載器加載Hello.class,然后JVM來(lái)運(yùn)行Hello.class, 程序的運(yùn)行時(shí)刻就是此時(shí)刻。 -
什么是反射, 它的功能: 反射就是在程序運(yùn)行期間動(dòng)態(tài)獲取
Hello。class的屬性和方法, 構(gòu)造它的實(shí)例, 這個(gè)功能就是反射。
2、反射的使用場(chǎng)景
反射的應(yīng)用場(chǎng)景主要有:
開(kāi)發(fā)通用框架: 反射最重要的用途就是開(kāi)發(fā)各種通用框架。 很多框架(比如Spring)都是配置化的, 通過(guò)XML、Yaml等配置JavaBean、Filter等,為了保證框架的通用性, 它們可能需要通過(guò)配置文件加載不同的對(duì)象或類, 調(diào)用不同的方法,這個(gè)時(shí)候就必須用到反射-運(yùn)行時(shí)動(dòng)態(tài)加載需要加載的對(duì)象。
動(dòng)態(tài)代理: 在切面編程(AOP)中, 需要攔截特定的方法, 通常會(huì)選擇動(dòng)態(tài)代理方式, 此時(shí)需要反射來(lái)實(shí)現(xiàn)。
注解: 注解本身只起到標(biāo)記作用, 他需要利用反射機(jī)制,根據(jù)注解標(biāo)記去調(diào)用注解解釋器, 執(zhí)行相應(yīng)行為。
可擴(kuò)展功能: 應(yīng)用程序可以通過(guò)使用完全可限定類名創(chuàng)建可擴(kuò)展性對(duì)象實(shí)例來(lái)使用外部的用戶定義類剋。
3、 反射的缺點(diǎn)
性能開(kāi)銷: 由于反射涉及動(dòng)態(tài)解析的類型,因此無(wú)法執(zhí)行某些 Java 虛擬機(jī)優(yōu)化。因此,反射操作的性能要比非反射操作的性能要差,應(yīng)該在性能敏感的應(yīng)用程序中頻繁調(diào)用的代碼段中避免。
破壞封裝性: 反射調(diào)用方法時(shí)可以忽略權(quán)限檢查,因此可能會(huì)破壞封裝性而導(dǎo)致安全問(wèn)題。
內(nèi)部曝光: * 由于反射允許代碼執(zhí)行在非反射代碼中非法的操作,例如訪問(wèn)私有字段和方法,所以反射的使用可能會(huì)導(dǎo)致意想不到的副作用,這可能會(huì)導(dǎo)致代碼功能失常并可能破壞可移植性。反射代碼打破了抽象,因此可能會(huì)隨著平臺(tái)的升級(jí)而改變行為。
二、原理
1、類加載過(guò)程

類加載的完整過(guò)程如下:
在編譯時(shí),Java 編譯器編譯好 .java 文件之后,在磁盤(pán)中產(chǎn)生 .class 文件。.class 文件是二進(jìn)制文件,內(nèi)容是只有 JVM 能夠識(shí)別的機(jī)器碼。
JVM 中的類加載器讀取字節(jié)碼文件,取出二進(jìn)制數(shù)據(jù),加載到內(nèi)存中,解析.class 文件內(nèi)的信息。類加載器會(huì)根據(jù)類的全限定名來(lái)獲取此類的二進(jìn)制字節(jié)流;然后,將字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);接著,在內(nèi)存中生成代表這個(gè)類的 java.lang.Class 對(duì)象。
加載結(jié)束后,JVM 開(kāi)始進(jìn)行連接階段(包含驗(yàn)證、準(zhǔn)備、初始化)。經(jīng)過(guò)這一系列操作,類的變量會(huì)被初始化。
2、 Class對(duì)象
使用反射,必須先獲得帶操作對(duì)象的Class對(duì)象。Java中, 無(wú)論生成某個(gè)類的多少個(gè)對(duì)象, 這些對(duì)象都對(duì)應(yīng)于同一個(gè)Class對(duì)象。這個(gè)Class對(duì)象有JVM生成, 通過(guò)它能夠獲悉整個(gè)類的所有結(jié)構(gòu)。所以, java.lang.Class可以視為所有反射API的入口點(diǎn)。
反射的本質(zhì)就是, 在Java運(yùn)行時(shí), 將Java類的各種成分映射成一個(gè)個(gè)的Java對(duì)象。
舉例來(lái)說(shuō), 加入定義了以下代碼:
User user = new User();
步驟說(shuō)明:
- JVM加載方法的時(shí)候, 遇到
new User(), JVM會(huì)根據(jù)User的全限定類名去加載User.class; - JVM回去從本地或網(wǎng)絡(luò)等尋找
User.class文件并加載到內(nèi)存中。 - JVM通過(guò)類加載器自動(dòng)創(chuàng)建這個(gè)類的
Class對(duì)象, 并存儲(chǔ)在JVM的方法區(qū),注意一個(gè)類有且僅有一個(gè)Class對(duì)象。
3、方法的反射調(diào)用
方法的反射調(diào)用, 也就是Method.invoke()方法。
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
Method.invoke()方法實(shí)際委派給MethodAccessor執(zhí)行。MethodAccessor接口有2個(gè)具體的實(shí)現(xiàn)類:
-
NativeMethodAccessorImpl: 本地方法來(lái)實(shí)現(xiàn)反射調(diào)用 -
DelegatingMethodAccessorImpl: 委派方法來(lái)實(shí)現(xiàn)委托調(diào)用
每個(gè)Method實(shí)例第一次調(diào)用都會(huì)生成一個(gè)委派實(shí)現(xiàn)(DelegatingMethodAccessorImpl), 他所委派的具體實(shí)現(xiàn)便是一個(gè)本地實(shí)現(xiàn)(NativeMethodAccessorImpl)。本地實(shí)現(xiàn)非常容易理解, 當(dāng)進(jìn)入Java虛擬機(jī)內(nèi)部后, 我們便擁有了Method實(shí)例所指向方法的具體地址, 這個(gè)時(shí)候反射無(wú)非是將準(zhǔn)備好的參數(shù), 然后調(diào)用目標(biāo)方法。
- 生成MethodAccessor
private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it
// if so
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
return tmp;
}
public MethodAccessor newMethodAccessor(Method var1) {
checkInitted();
if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
} else {
NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
// 生成DelegatingMethodAccessorImpl, 實(shí)際委派的是NativeMethodAccessorImpl
DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
var2.setParent(var3);
return var3;
}
}
-
DelegatingMethodAccessorImpl源碼:
// 一個(gè)典型的靜態(tài)代理模式
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
private MethodAccessorImpl delegate;
DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {
this.setDelegate(var1);
}
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
return this.delegate.invoke(var1, var2);
}
void setDelegate(MethodAccessorImpl var1) {
this.delegate = var1;
}
}
Method實(shí)例的每次調(diào)用都是先調(diào)用DelegatingMethodAccessorImpl的invoke方法, 在調(diào)用NativeMethodAccessorImpl的invoke方法。
其實(shí),Java 的反射調(diào)用機(jī)制還設(shè)立了另一種動(dòng)態(tài)生成字節(jié)碼的實(shí)現(xiàn)(下稱動(dòng)態(tài)實(shí)現(xiàn)),直接使用 invoke 指令來(lái)調(diào)用目標(biāo)方法。之所以采用委派實(shí)現(xiàn),便是為了能夠在本地實(shí)現(xiàn)以及動(dòng)態(tài)實(shí)現(xiàn)中切換。動(dòng)態(tài)實(shí)現(xiàn)和本地實(shí)現(xiàn)相比,其運(yùn)行效率要快上 20 倍。這是因?yàn)閯?dòng)態(tài)實(shí)現(xiàn)無(wú)需經(jīng)過(guò) Java 到 C++ 再到 Java 的切換,但由于生成字節(jié)碼十分耗時(shí),僅調(diào)用一次的話,反而是本地實(shí)現(xiàn)要快上 3 到 4 倍。
考慮到許多反射調(diào)用僅會(huì)執(zhí)行一次,Java 虛擬機(jī)設(shè)置了一個(gè)閾值 15(可以通過(guò) -Dsun.reflect.inflationThreshold 來(lái)調(diào)整),當(dāng)某個(gè)反射調(diào)用的調(diào)用次數(shù)在 15 之下時(shí),采用本地實(shí)現(xiàn);當(dāng)達(dá)到 15 時(shí),便開(kāi)始動(dòng)態(tài)生成字節(jié)碼,并將委派實(shí)現(xiàn)的委派對(duì)象切換至動(dòng)態(tài)實(shí)現(xiàn),這個(gè)過(guò)程我們稱之為 Inflation。
源碼如下:
class NativeMethodAccessorImpl extends MethodAccessorImpl {
private final Method method;
private DelegatingMethodAccessorImpl parent;
private int numInvocations;
NativeMethodAccessorImpl(Method var1) {
this.method = var1;
}
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
this.parent.setDelegate(var3);
}
return invoke0(this.method, var1, var2);
}
void setParent(DelegatingMethodAccessorImpl var1) {
this.parent = var1;
}
private static native Object invoke0(Method var0, Object var1, Object[] var2);
}
public class ReflectionFactory {
private static boolean initted = false;
private static final Permission reflectionFactoryAccessPerm = new RuntimePermission("reflectionFactoryAccess");
private static final ReflectionFactory soleInstance = new ReflectionFactory();
private static volatile LangReflectAccess langReflectAccess;
private static volatile Method hasStaticInitializerMethod;
private static boolean noInflation = false;
private static int inflationThreshold = 15;
static int inflationThreshold() {
return inflationThreshold;
}
}
4、反射調(diào)用的開(kāi)銷
方法的反射會(huì)帶來(lái)不小的開(kāi)銷, 原有如下:
- 變長(zhǎng)參數(shù)方法導(dǎo)致的Object數(shù)組
- 基本類型的自動(dòng)裝箱拆箱
- 最重要的方法內(nèi)聯(lián)
Class.forName會(huì)調(diào)用本地方法,Class.getMethod會(huì)遍歷該類的所有公共方法, 如果沒(méi)有匹配到, 還會(huì)遍歷父類的所有公共方法, 可想而知這兩個(gè)操作都非常費(fèi)時(shí)。
注意,以
getMethod為代表的查找方法操作, 會(huì)返回查找結(jié)果的一份拷貝。因此我們應(yīng)當(dāng)在熱點(diǎn)代碼中避免使用返回Method數(shù)組的getMethods或getDeclareMethods方法,以減少不必要的堆空間消耗。在實(shí)踐中, 往往會(huì)在應(yīng)用中緩存Class.forName和Class.getMethod的結(jié)果。
反射調(diào)用自身的性能開(kāi)銷:
第一,由于Method.invoke方法自身是一個(gè)可變長(zhǎng)參數(shù)方法,在字節(jié)碼層面它的最后一個(gè)參數(shù)是一個(gè)Object數(shù)組。Java編譯器會(huì)在調(diào)用處生成一個(gè)長(zhǎng)度為傳入?yún)?shù)數(shù)量的Object數(shù)組。
第二,Object不能存儲(chǔ)基本數(shù)據(jù)類型, Java編譯器會(huì)對(duì)傳入的基本數(shù)據(jù)類型進(jìn)行自動(dòng)裝箱、拆箱。
這兩個(gè)操作都很耗時(shí), 還可能占用堆內(nèi)存,是的GC頻繁。
三、常用API
Java類的成員包括以下三類: 屬性字段、構(gòu)造函數(shù)、方法。 反射的API也是和這幾個(gè)相關(guān)。

-
Field類:提供有關(guān)類的屬性信息嗎,以及它的動(dòng)態(tài)訪問(wèn)權(quán)限。它是一個(gè)封裝反射類的屬性的類。 -
Constructor類: 提供類的構(gòu)造方法信息, 以及對(duì)它的動(dòng)態(tài)訪問(wèn)權(quán)限。它是一個(gè)封裝反射類的構(gòu)造器的類。 -
Method類: 提供類的方法信息,包括抽象方法。它是一個(gè)封裝反射類的方法的類。 -
Class類: 表示Java應(yīng)用程序中的類的實(shí)例。
通過(guò)一個(gè)經(jīng)典的例子, 我們來(lái)學(xué)習(xí)反射。 新建一個(gè)Student類:
public class Student {
private static volatile Student INSTANCE;
private String name;
public int age;
private Student(){}
public Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String show(String message){
System.out.println("show: " + name + "," + age+ "," + message);
return "testReturnValue";
}
public static Student getInstance(){
if(INSTANCE==null){
synchronized (Student.class){
if(INSTANCE==null){
INSTANCE = new Student();
}
}
}
return INSTANCE;
}
}
該類包含2個(gè)成員變量、1個(gè)類變量、2個(gè)構(gòu)造器、6個(gè)方法,基本覆蓋平常所用的所有類成員。
1、java.lang.reflect包
Java中java.lang.reflect包提供了反射功能, 它下面的類都沒(méi)有public構(gòu)造方法。 它包含的核心接口和類如下(不包含已經(jīng)介紹過(guò)的):
-
Member接口:反映關(guān)于單個(gè)成員(字段或方法)或構(gòu)造函數(shù)的標(biāo)識(shí)信息。 -
Array類:該類提供動(dòng)態(tài)地生成和訪問(wèn) JAVA 數(shù)組的方法。 -
Modifier類:提供了 static 方法和常量,對(duì)類和成員訪問(wèn)修飾符進(jìn)行解碼。 -
Proxy類:* 提供動(dòng)態(tài)地生成代理類和類實(shí)例的靜態(tài)方法。
2、獲取Class對(duì)象
獲取Class對(duì)象的3種方法:
-
Class.forName方法:
public class UserClient {
public static void main(String[] args) throws ClassNotFoundException {
// 加載一個(gè)類
Class<?> studentClass = Class.forName("org.example.good.reflect.Student");
System.out.println(studentClass.getCanonicalName());
// 加載基礎(chǔ)類型
Class<?> arrayClass = Class.forName("[D");
System.out.println(arrayClass.getCanonicalName());
// 加載一個(gè)類的一維數(shù)組, [代表一維數(shù)組, L和;之間的代表全限定類名
Class<?> arrayClass2 = Class.forName("[Lorg.example.good.reflect.Student;");
System.out.println(arrayClass2.getCanonicalName());
// 加載一個(gè)類的二維數(shù)組
Class<?> arrayClass3 = Class.forName("[[Lorg.example.good.reflect.Student;");
System.out.println(arrayClass3);
}
}
使用類的全限定類名來(lái)反射對(duì)象的類, 常用的場(chǎng)景是JDBC中加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)。
- 類名 +
.class方法:
直接使用類名+'.class'獲取Class對(duì)象。
public class UserClient {
public static void main(String[] args) throws ClassNotFoundException {
// 加載一個(gè)類
Class<?> studentClass = Class.forName("org.example.good.reflect.Student");
System.out.println(studentClass.getCanonicalName());
Class<?> studentClass2 = Student.class;
System.out.println(studentClass2.getCanonicalName());
System.out.println(studentClass2 == studentClass);
}
}
-
Object的getClass方法:
Object類中有getClass方法,因?yàn)樗蓄惗祭^承自Object類。因此可以調(diào)用Object.getClass方法來(lái)獲取Class對(duì)象。
public class UserClient {
public static void main(String[] args) throws ClassNotFoundException {
// 加載一個(gè)類
Class<?> studentClass = Class.forName("org.example.good.reflect.Student");
System.out.println(studentClass.getCanonicalName());
Student student = new Student("王子", 1);
Class<?> studentClass3 = student.getClass();
System.out.println(studentClass3.getCanonicalName());
System.out.println(studentClass == studentClass3);
}
}
3、是否某個(gè)類的實(shí)例
判斷是否是某個(gè)類的實(shí)例,有兩種方式:
- 用
instanceof的關(guān)鍵字 - 用
Class.isInstance方法(它是一個(gè)native方法)
public class UserClient {
public static void main(String[] args) throws ClassNotFoundException {
List<String> dataList = new ArrayList<>();
System.out.println(dataList instanceof List);
System.out.println(List.class.isInstance(dataList));
}
}
4、創(chuàng)建類的實(shí)例
創(chuàng)建類的實(shí)例有2種方法:
- 使用
Class對(duì)象的newInstance方法; - 使用
Constructor對(duì)象的newInsatnce方法:
Class<?> clazz1 = Student.class;
Student student1 = (Student) clazz1.newInstance();
System.out.println(student1);
Constructor<?> constructor = clazz1.getConstructor(String.class, Integer.class);
Student student2 = (Student)constructor.newInstance("王子", 1);
System.out.println(student2);
注意事項(xiàng):
- 使用
Class對(duì)象的newInstance方法時(shí),要求對(duì)應(yīng)的類必須定義一個(gè)public的無(wú)參構(gòu)造方法, 如果無(wú)參構(gòu)造方法設(shè)置成private或protected但沒(méi)有訪問(wèn)權(quán)限, 就會(huì)報(bào)一個(gè)IllegalAccessException錯(cuò)誤。 - 使用
Constructor對(duì)象的newInstance方法時(shí), 構(gòu)造方法的入?yún)⒈仨毑荒苁腔A(chǔ)類型, 否則會(huì)報(bào)參數(shù)不匹配。
5、創(chuàng)建數(shù)組實(shí)例
數(shù)組在Java中是一個(gè)特殊的數(shù)據(jù)類型,它可以復(fù)制給一個(gè)對(duì)象引用。Java中通過(guò)Array.newInstance創(chuàng)建數(shù)組的實(shí)例。利用反射創(chuàng)建數(shù)組:
Class<?> clazz = Integer.class;
Object object = Array.newInstance(clazz, 5);
Array.set(object, 0, 1);
System.out.println(object);
System.out.println(Array.getLength(object));
System.out.println(Array.get(object, 4));
System.out.println(Array.get(object, 0));
java.lang.reflect.Array的源碼:
public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException {
return newArray(componentType, length);
}
6、Field
Class對(duì)象提供以下方法獲取Field。
-
getField(String name): 根據(jù)名稱獲取公共類成員, 包括父類。 -
getFields(): 獲取所有公共類成員, 包括父類。 -
getDeclareField(String name): 獲取所有類成員, 不包括父類。 -
getDeclareFields(): 獲取所有類成員, 不包括父類。
System.out.println("list public field:");
Class<?> clazz = Student.class;
Field[] fields = clazz.getFields();
for(Field field: fields){
System.out.println(String.format("field name: %s, type: %s", field.getName(), field.getType().getName()));
}
System.out.println("list all field:");
Field[] fields2 = clazz.getDeclaredFields();
for(Field field: fields2){
System.out.println(String.format("field name: %s, type: %s", field.getName(), field.getType().getName()));
}
Field field = clazz.getField("age");
Field field2 = clazz.getDeclaredField("name");
System.out.println(String.format("field name: %s, type: %s", field.getName(), field.getType()));
Student student =new Student("", 0);
System.out.println(String.format("before, field value: %s", field.get(student)));
field.set(student, 25);
System.out.println(String.format("after, field value: %s", field.get(student)));
field2.setAccessible(true);
field2.set(student, "小可愛(ài)");
System.out.println(String.format("after, field value: %s", field2.get(student)));
// Output
list public field:
field name: age, type: java.lang.Integer
list all field:
field name: INSTANCE, type: org.example.good.reflect.Student
field name: name, type: java.lang.String
field name: age, type: java.lang.Integer
field name: age, type: class java.lang.Integer
before, field value: 0
after, field value: 25
after, field value: 小可愛(ài)
7、Method
Class對(duì)象提供以下方法獲取Method:
getMethod(String name, Class<?>... parameterTypes): 根據(jù)方法名稱獲取指定公共方法, 包含父類公共方法。其中第一個(gè)參數(shù)為方法名稱, 后面參數(shù)時(shí)方法入?yún)⒌腃lass對(duì)象。getMethods(): 獲取所有公共方法, 包含父類公共方法。getDeclareMethod(String name, Class<?>... parameterTypes): 根據(jù)方法名稱獲取指定方法, 包含所有公共非公共方法, 但不包含父類方法。其中第一個(gè)參數(shù)為方法名稱, 后面參數(shù)時(shí)方法入?yún)⒌腃lass對(duì)象。getDeclareMethod(): 獲取所有該類方法, 不包含父類方法。
Class<?> clazz = Student.class;
Method method = clazz.getMethod("getAge");
System.out.println(method.getName());
Method[] methods = clazz.getDeclaredMethods();
for(Method method1: methods){
System.out.println(String.format("%s", method1.getName()));
}
// Output
// getAge
// getName
// getName
// getInstance
// setName
// setAge
// show
// getAge
獲取一個(gè)Method對(duì)象后, 可以調(diào)用invoke方法類調(diào)用改方法。
Student student = (Student)clazz.newInstance();
Field field = clazz.getDeclaredField("name");
Method method2 = clazz.getMethod("setAge", int.class);
method2.invoke(student, 25);
field.setAccessible(true);
field.set(student, "Lily");
Method method1 = clazz.getDeclaredMethod("show", String.class);
method1.setAccessible(true);
method1.invoke(student, "hello");
// Output
show: Lily,25,hello
Method 調(diào)用invoke() 的時(shí)候,存在許多細(xì)節(jié):
invoke() 方法中第一個(gè)參數(shù) Object 實(shí)質(zhì)上是 Method 所依附的 Class 對(duì)應(yīng)的類的實(shí)例,如果這個(gè)方法是一個(gè)靜態(tài)方法,那么 ojb 為 null,后面的可變參數(shù) Object 對(duì)應(yīng)的自然就是參數(shù)。
invoke() 返回的對(duì)象是 Object,所以實(shí)際上執(zhí)行的時(shí)候要進(jìn)行強(qiáng)制轉(zhuǎn)換。
在對(duì)Method調(diào)用invoke()的時(shí)候,如果方法本身會(huì)拋出異常,那么這個(gè)異常就會(huì)經(jīng)過(guò)包裝,由Method統(tǒng)一拋InvocationTargetException。而通過(guò)InvocationTargetException.getCause() 可以獲取真正的異常。
8、Constructor
Class 對(duì)象提供以下方法獲取對(duì)象的構(gòu)造方法(Constructor):
-
getConstructor: 返回類的特定 public 構(gòu)造方法。參數(shù)為方法參數(shù)對(duì)應(yīng) Class 的對(duì)象。 -
getDeclaredConstructor: 返回類的特定構(gòu)造方法。參數(shù)為方法參數(shù)對(duì)應(yīng) Class 的對(duì)象。 -
getConstructors: 返回類的所有 public 構(gòu)造方法。 -
getDeclaredConstructors: 返回類的所有構(gòu)造方法。
獲取一個(gè)Constructor 對(duì)象后,可以用newInstance 方法來(lái)創(chuàng)建類實(shí)例。
Constructor<?> constructor = clazz.getConstructor();
Student student = (Student) constructor.newInstance();
Constructor<?> constructor1 = clazz.getConstructor(String.class, Integer.class);
Student student1 = (Student) constructor1.newInstance("lily", 25);
System.out.println(student.toString());
System.out.println(student1.toString());
// Output
Student{name='null', age=null}
Student{name='lily', age=25}
9、繞開(kāi)訪問(wèn)限制
有時(shí)候,我們需要通過(guò)反射訪問(wèn)私有成員、方法??梢允褂?Constructor/Field/Method.setAccessible(true) 來(lái)繞開(kāi) Java 語(yǔ)言的訪問(wèn)限制。