詳解Java反射機(jī)制
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
反射機(jī)制的概念:
反射機(jī)制是指在程序運(yùn)行過(guò)程中,對(duì)任意一個(gè)類都能夠獲取其所有屬性和方法,并且對(duì)任意一個(gè)對(duì)象都能調(diào)用其任意一個(gè)方法。這種動(dòng)態(tài)獲取類和對(duì)象的信息,以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能被稱為Java語(yǔ)言的反射機(jī)制。
Class類的介紹:
說(shuō)到反射,就不得不提起java.lang.Class這個(gè)類,JVM為每個(gè)加載的class創(chuàng)建了對(duì)應(yīng)的Class實(shí)例,并在實(shí)例中保存了該class的所有信息,包括類名、包名、父類、實(shí)現(xiàn)的接口、所有方法、字段等。那么Class到底是個(gè)什么類呢?先看下它的部分源碼:
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
private Class(ClassLoader loader) {
classLoader = loader;
}
}
可以看出這個(gè)類被聲明為final(不可變類),并且它的構(gòu)造方法是private(私有的),即不可能通過(guò)new關(guān)鍵字來(lái)創(chuàng)建該實(shí)例,但是否可以通過(guò)反射機(jī)制(很強(qiáng)大)來(lái)創(chuàng)建那些構(gòu)造方法為私有的類的實(shí)例呢?答案是幾乎都可以,但Class這個(gè)類例外,下面可以來(lái)驗(yàn)證一下:
自定義一個(gè)構(gòu)造方法是私有的類Test
class Test {
private Test(){}
// 只有Test的實(shí)例可調(diào)用
public void sayHello(){
System.out.println("hello");
}
}
主類如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 獲得Test類的class實(shí)例
Class cTest = Test.class;
// 獲得Test類的構(gòu)造方法
Constructor constructor = cTest.getDeclaredConstructor();
// 設(shè)置訪問(wèn)權(quán)限,對(duì)于執(zhí)行非public的之前需先調(diào)用這個(gè)方法
constructor.setAccessible(true);
// 構(gòu)造Test實(shí)例
Test test = (Test) constructor.newInstance();
// 調(diào)用sayHello方法
test.sayHello();
}
}
運(yùn)行結(jié)果:

成功調(diào)用sayHello方法,即證明了我們通過(guò)反射可以實(shí)例化即使構(gòu)造方法是私有的類的實(shí)例。
接下來(lái)再看看通過(guò)反射構(gòu)造Class類的實(shí)例會(huì)怎樣?

可以看到再代碼14行執(zhí)行setAccessible(true)方法時(shí)就拋異常了,前面已經(jīng)說(shuō)過(guò),要想執(zhí)行非public的必須得先調(diào)用setAccessible(true)方法。點(diǎn)進(jìn)源碼看看,

原來(lái)如此,它這里做了校驗(yàn),你要是想訪問(wèn)Class類的構(gòu)造方法,那么不好意思直接給你拋異常。Java虛擬機(jī)規(guī)范規(guī)定,所有的Class類的class實(shí)例均由JVM執(zhí)行過(guò)程中動(dòng)態(tài)加載,即在加載類時(shí),讀取定義該類的class文件(字節(jié)碼文件)創(chuàng)建class實(shí)例并放在JVM的方法區(qū)中,我們是不可能創(chuàng)建的,只能獲得。我們知道類是只會(huì)被JVM加載一次,這也說(shuō)明了類的class實(shí)例只有一個(gè),全局唯一且共享,所以不管使用什么方式獲取某個(gè)類的class實(shí)例,該class實(shí)例是相同的,使用‘’==‘’返回true。
反射的應(yīng)用:
Java中的對(duì)象有兩種類型,即編譯時(shí)類型和運(yùn)行時(shí)類型。編譯時(shí)類型在聲明對(duì)象時(shí)所采用的類型,運(yùn)行時(shí)類型指為對(duì)象賦值時(shí)所采用的類型。在如下代碼中,person對(duì)象的編譯時(shí)類型為Person,運(yùn)行時(shí)類型為Student,
Person person = new Student();
因此,程序在編譯期間無(wú)法預(yù)知該對(duì)象和類的真實(shí)信息,只能通過(guò)運(yùn)行時(shí)信息來(lái)發(fā)現(xiàn)該對(duì)象和類的真實(shí)信息,而其真實(shí)信息(對(duì)象的屬性和方法)通常通過(guò)反射機(jī)制來(lái)獲取,這便是Java語(yǔ)言的反射機(jī)制的核心功能。
反射的API:
Java的反射API主要用于在運(yùn)行過(guò)程中動(dòng)態(tài)生成類、接口或?qū)ο蟮刃畔?,其常用API如下:
Class類:用于獲取類的屬性、方法等信息。
Field類:表示類的成員變量,用于獲取和設(shè)置類中的屬性值
Method類:表示類的方法,用于獲取方法的描述信息或執(zhí)行某個(gè)方法
Constructor類:表示類的構(gòu)造方法
反射的步驟:
1、獲取想要操作的類的Class對(duì)象,該Class對(duì)象是反射的核心,通過(guò)它可以調(diào)用類的任意方法。
2、調(diào)用Class對(duì)象所對(duì)應(yīng)的類中定義的方法,這是反射的使用階段。
3、使用反射API來(lái)獲取并調(diào)用類的屬性和方法等信息。
獲取Class對(duì)象的3種方法如下:
1、調(diào)用某個(gè)對(duì)象的getClass方法以獲取該類對(duì)應(yīng)的Class對(duì)象:
Test test = new Test();
Class c = test.getClass();
2、調(diào)用某個(gè)類的class屬性以獲取該類對(duì)應(yīng)的Class對(duì)象:
Class c = Test.class;
3、調(diào)用Class類中的forName靜態(tài)方法以獲取該類對(duì)應(yīng)的Class對(duì)象,這是最安全、性能也最好的方法:
Class c = Class.forName("test.Test");// 包路徑加類名
訪問(wèn)字段:
Class類提供了以下幾個(gè)方法來(lái)獲取字段:
Field getField(name):根據(jù)字段名獲取某個(gè)public的field(包括父類)
Field getDeclaredField(name):根據(jù)字段名獲取當(dāng)前類的某個(gè)field(不包括父類)
Field[] getFields():獲取所有public的field(包括父類)
Field[] getDeclaredFields():獲取當(dāng)前類的所有field(不包括父類)
一個(gè)Field對(duì)象包含了一個(gè)字段的所有信息:
String getName():返回字段名稱
Class getType():返回字段類型,也是一個(gè)Class實(shí)例
int getModifiers():返回字段的修飾符,它是一個(gè)int,不同的bit表示不同的含義
get(Object):返回指定對(duì)象Object的字段的值
set(Object, Object):設(shè)置字段的值,第一個(gè)Object參數(shù)是指定的實(shí)例,第二個(gè)Object參數(shù)是待修改的值
訪問(wèn)方法:
Class類提供了以下幾個(gè)方法來(lái)獲取Method:
Method getMethod(name, Class…):獲取某個(gè)public的Method(包括父類)
Method getDeclaredMethod(name, Class…):獲取當(dāng)前類的某個(gè)Method(不包括父類)
Method[] getMethods():獲取所有public的Method(包括父類)
Method[] getDeclaredMethods():獲取當(dāng)前類的所有Method(不包括父類)
一個(gè)Method對(duì)象包含一個(gè)方法的所有信息:
String getName():返回方法名稱
Class getReturnType():返回方法返回值類型,也是一個(gè)Class實(shí)例
Class[] getParameterTypes():返回方法的參數(shù)類型,是一個(gè)Class數(shù)組
int getModifiers():返回方法的修飾符,它是一個(gè)int,不同的bit表示不同的含義
Object invoke(Object instance, Object… parameters):調(diào)用某個(gè)對(duì)象的方法
訪問(wèn)構(gòu)造方法:
通過(guò)Class實(shí)例獲取Constructor的方法如下:
getConstructor(Class…):獲取某個(gè)public的Constructor
getDeclaredConstructor(Class…):獲取某個(gè)Constructor
getConstructors():獲取所有public的Constructor
getDeclaredConstructors():獲取所有Constructor
創(chuàng)建對(duì)象的兩種方式:
1、使用Class對(duì)象的newInstance方法創(chuàng)建該Class對(duì)象對(duì)應(yīng)類的實(shí)例,這種方法要求該Class對(duì)象對(duì)應(yīng)的類有默認(rèn)的空構(gòu)造器。
Class c = Test.class;
Test test = (Test) c.newInstance();
2、先使用Class對(duì)象獲取指定的Constructor對(duì)象,再調(diào)用Constructor對(duì)象的newInstance方法創(chuàng)建Class對(duì)象對(duì)應(yīng)類的實(shí)例,通過(guò)這種方法可以選定構(gòu)造方法創(chuàng)建實(shí)例。
// 獲取構(gòu)造方法Integer(int):
Constructor cons = Integer.class.getConstructor(int.class);
// 調(diào)用構(gòu)造方法:
Integer n1 = (Integer) cons.newInstance(123);
注意:
調(diào)用非public的方法時(shí),必須首先通過(guò)setAccessible(true)設(shè)置允許訪問(wèn)。setAccessible(true)可能會(huì)失敗。
至此,Java的反射機(jī)制介紹到這里
————————————————
版權(quán)聲明:本文為CSDN博主「b17a」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/weixin_45685353/article/details/115425373
粉絲福利:Java從入門(mén)到入土學(xué)習(xí)路線圖
??????

??長(zhǎng)按上方微信二維碼 2 秒
感謝點(diǎn)贊支持下哈 
