日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java swing jbutton_Java 反射

發(fā)布時(shí)間:2023/12/15 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java swing jbutton_Java 反射 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

點(diǎn)擊上方“凌天實(shí)驗(yàn)室”,“星標(biāo)或置頂公眾號(hào)”

漏洞、技術(shù)還是其他,我都想第一時(shí)間和你分享

1

前? 言

本章為新手向零基礎(chǔ) Java 反射學(xué)習(xí)筆記。

截取部分本實(shí)驗(yàn)室發(fā)起的項(xiàng)目javaweb-sec。

Gitbook地址:https://javasec.org/

2

反射的基本定義

官方文檔:https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/index.html

Reflection(反射)賦予了 Java 代碼從已加載的類中發(fā)現(xiàn)成員變量(Fields)、成員方法(Methods)和構(gòu)造方法(Constructors)的能力,并可以在安全限制內(nèi)使用這些反射得到的成員變量、成員方法、構(gòu)造方法來操作他們對(duì)應(yīng)的底層對(duì)象。

簡(jiǎn)而言之,你可以在運(yùn)行狀態(tài)中通過反射機(jī)制做到:

  • 對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;

  • 對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性。

這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。

不得不說,反射是一種十分具有 "Hacker" 精神的機(jī)制。

3

反射的基礎(chǔ)

從源代碼到程序運(yùn)行,大致經(jīng)歷的步驟如下:

  • 創(chuàng)建源文件后,程序會(huì)被編譯器編譯為后綴名為?.class?的文件;

  • 通過類加載器系統(tǒng)將字節(jié)碼.class加載入 JVM 的內(nèi)存中,類的加載是通過 ClassLoader 及其子類來完成的;在加載階段,虛擬機(jī)要完成三件事情:通過一個(gè)類的全限定名來獲取其定義的二進(jìn)制字節(jié)流,將這個(gè)字節(jié)流所代表的的靜態(tài)儲(chǔ)存結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),在 Java 堆中生成一個(gè)代表這個(gè)類的?java.lang.Class?對(duì)象,作為對(duì)方法區(qū)中這些數(shù)據(jù)的訪問入口。在加載的過程中將使用雙親委派模型進(jìn)行類的加載;

  • 對(duì)字節(jié)碼進(jìn)行驗(yàn)證;

  • 解析類、接口、字段,是虛擬機(jī)將常量池中的符號(hào)引用轉(zhuǎn)化為直接引用的過程;

  • 類初始化,這里需要注意的是,在使用?Java.lang.reflect?包的方法對(duì)類進(jìn)行反射調(diào)用時(shí),如果類還沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化;

  • 執(zhí)行。

可以看到,在加載的過程中,當(dāng)一個(gè) class 被加載,或當(dāng)加載器(classloader)的?defineClass()被 JVM 調(diào)用時(shí),JVM 將自動(dòng)產(chǎn)生一個(gè) Class 對(duì)象,并且這個(gè)對(duì)象會(huì)保存在同名的?.class?文件里,當(dāng)我們 new 一個(gè)新對(duì)象或者引用一個(gè)靜態(tài)成員變量時(shí),JVM 中的加載器系統(tǒng)會(huì)將對(duì)應(yīng)的 Class 對(duì)象加載到 JVM 中,然后 JVM 再根據(jù)這個(gè)類型信息相關(guān)的 Class 對(duì)象創(chuàng)建我們需要實(shí)例對(duì)象或者提供靜態(tài)變量的引用值。

因此,這些在程序加載過程中產(chǎn)生的類的 Class 對(duì)象就是反射的基礎(chǔ)。

Class 類位于包?java.lang?下:

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement

可以看到,這是個(gè) final 類,實(shí)現(xiàn)了 4 個(gè)接口。

這個(gè)類十分特殊,它同樣的繼承自 Object ,它的實(shí)例能用來表達(dá) Java 程序運(yùn)行時(shí)的 classes 和 interfaces,也能用來表達(dá) enum、array、Java 基礎(chǔ)類型(boolean、byte、char、short、int、long、double、float)以及關(guān)鍵詞 void。

Class 沒有公共構(gòu)造方法。Class 對(duì)象是在加載類時(shí)由 Java 虛擬機(jī)以及通過調(diào)用類加載器中的?defineClass?方法自動(dòng)構(gòu)造的。

4

獲取 Class 對(duì)象的方法

通過上面的了解,我們知道,如果想使用反射,必須得獲得 Class 對(duì)象。

除了?java.lang.reflect.ReflectPermission?以外,java.lang.reflect?中的其他類都沒有 public 的構(gòu)造函數(shù),也就是說要得到這些類,我們必須通過 Class 。

下面列舉了能夠獲取 Class 對(duì)象的方法:

1. Object.getClass()

第一種方法是通過類的實(shí)例來獲取對(duì)應(yīng)的 Class 對(duì)象。

byte[] bytes = new byte[1024];
Class> c = bytes.getClass();

但是對(duì)于基礎(chǔ)數(shù)據(jù)類型不能使用這種方式。

2. 使用.Class 語法

通過類的類型獲取 Class 對(duì)象,基本類型同樣可以使用這種方法。

Class> c = boolean.class;
Class> c = String.class;

3. Class.forName()

通過類的全限定名獲取Class對(duì)象, 基本類型無法使用此方法。

try {
Class> c = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

對(duì)于數(shù)組比較特殊:

Class> doubleArray = Class.forName("[D"); //相當(dāng)于double[].class
Class> cStringArray = Class.forName("[[Ljava.lang.String;"); //相當(dāng)于String[][].class

如果在調(diào)用Class.forName()方法時(shí),沒有在編譯路徑下(classpath)找到對(duì)應(yīng)的類,那么將會(huì)拋出ClassNotFoundException。

4. 基礎(chǔ)類型封裝類型 TYPE

基本類型和 void 類型的包裝類可以使用 TYPE 字段獲取。

Class c = Double.TYPE; //等價(jià)于 double.class
Class c = Void.TYPE;

5. 能夠返回 Class 類型的方法

另外還有一些反射方法可以獲取 Class 對(duì)象,但前提是你已經(jīng)獲取了一個(gè) Class 對(duì)象。比如說你已經(jīng)獲取了一個(gè)類的 Class 對(duì)象,就可以通過Class.getSuperclass()方法獲取這個(gè)類的父類的 Class 對(duì)象。

Class> c = javax.swing.JButton.class.getSuperclass();

類似能夠返回 Class 對(duì)象的方法還有:

  • Class.getClasses()

  • Class.getDeclaredClasses()

  • Class.getDeclaringClass()

  • Class.getEnclosingClass()

  • java.lang.reflect.Field.getDeclaringClass()

  • java.lang.reflect.Method.getDeclaringClass()

  • java.lang.reflect.Constructor.getDeclaringClass()

還有部分類能夠返回 Class 對(duì)象,但基本上都是調(diào)用?Class.forName(),此處不再列舉。

5

從 Class 中獲取信息

在獲取了 Class 對(duì)象后,就可以通過 Class 類提供的方法來獲取其中的信息和進(jìn)行操作了。

1. 獲取類的信息

獲取內(nèi)容方法簽名
類名String getName()
類名簡(jiǎn)稱String getSimpleName()
規(guī)范化類名String getCanonicalName()
類加載器ClassLoader getClassLoader()
泛型的參數(shù)類型TypeVariable>[] getTypeParameters()
直接繼承的父類Class super T> getSuperclass()
直接繼承的父類(包含泛型信息)Type getGenericSuperclass()
包含的方法Method getMethod(String name, Class>... parameterTypes)
內(nèi)部類Class>[] getDeclaredClasses()
構(gòu)造器Constructor getConstructor(Class>... parameterTypes)
包含的屬性Field getField(String name)
修飾符int getModifiers()
類的標(biāo)記Object[] getSigners()
數(shù)組的 Class 對(duì)象Class> getComponentType()
所在包Package getPackage()
所實(shí)現(xiàn)的接口Class>[] getInterfaces()
所實(shí)現(xiàn)的接口(包含泛型信息)Type[] getGenericInterfaces()
包含的Annotation A getAnnotation(Class annotationClass)

在 Class 類中,可以看到類似 getEnclosing*、getDeclared* 與 getDeclaringClass,可以理解為在不同”域“中獲取信息,由于篇幅的原因不再進(jìn)行羅列:

  • get*:返回當(dāng)前類和繼承層次中的所有父類的成員

  • getEnclosing*:返回內(nèi)部或匿名的封閉成員

  • getDeclared*:返回當(dāng)前類中的成員(不包含父類)

  • getDeclaringClass:返回當(dāng)前類聲明所在的類

2. 判斷類本身信息的方法

Class 類提供了一些判斷類本身信息的方法,通常命名為 isXxxxx ,返回類型均為 boolean。

判斷內(nèi)容方法簽名
是否為注解類型boolean isAnnotation()
是否使用了該Annotation修飾boolean isAnnotationPresent(Class extends Annotation> annotationClass)
是否為匿名類boolean isAnonymousClass()
是否為數(shù)組類型boolean isArray()
是否為枚舉類型boolean isEnum()
判斷兩個(gè)類的是否關(guān)聯(lián)boolean isAssignableFrom()
是否為接口boolean isInterface()
obj 是否是該 Class 的實(shí)例boolean isInstance(Object obj)
該類是否為局部類boolean isLocalClass()
該類是否為成員類boolean isMemberClass()
是否為基礎(chǔ)類型boolean isPrimitive()
是否由Java編譯器引入boolean isSynthetic()

3. 獲取構(gòu)造方法

Class 類提供了四個(gè) public 方法,用于獲取某個(gè)類的構(gòu)造方法。

方法描述
Constructor getConstructor(Class[] params)根據(jù)構(gòu)造函數(shù)的參數(shù),返回一個(gè)具體的具有public屬性的構(gòu)造函數(shù)
Constructor getConstructors()返回所有具有public屬性的構(gòu)造函數(shù)數(shù)組
Constructor getDeclaredConstructor(Class[] params)根據(jù)構(gòu)造函數(shù)的參數(shù),返回一個(gè)具體的構(gòu)造函數(shù)(不分public和非public屬性)
Constructor getDeclaredConstructors()返回該類中所有的構(gòu)造函數(shù)數(shù)組(不分public和非public屬性)

如果想要反射出無參數(shù)的構(gòu)造方法,可以直接使用?newInstanse()?方法創(chuàng)建新實(shí)例,因?yàn)樵摲椒ǖ谋举|(zhì)即調(diào)用類的無參數(shù)構(gòu)造方法。

4. 獲取類的成員方法

方法描述
Method getMethod(String name, Class[] params)根據(jù)方法名和參數(shù),返回一個(gè)具體的具有public屬性的方法
Method[] getMethods()返回所有具有public屬性的方法數(shù)組
Method getDeclaredMethod(String name, Class[] params)根據(jù)方法名和參數(shù),返回一個(gè)具體的方法(不分public和非public屬性)
Method[] getDeclaredMethods()返回該類中的所有的方法數(shù)組(不分public和非public屬性)

5. 獲取類的成員屬性

方法描述
Field getField(String name)根據(jù)變量名,返回一個(gè)具體的具有public屬性的成員變量
Field[] getFields()返回具有public屬性的成員變量的數(shù)組

Field

getDeclaredField(String name)

根據(jù)變量名,返回一個(gè)成員變量(不分public和非public屬性)
Field[] getDelcaredFields()返回所有成員變量組成的數(shù)組(不分public和非public屬性)

上面列舉的部分方法中還存在部分方法的重載方法,不再贅述,按需使用。

6

反射操作

能夠反射得到類、屬性、方法之后,應(yīng)該如何操作呢?

首先我們先看反射的包?java.lang.reflect?下的 Member 接口,顧名思義,這是一個(gè)標(biāo)識(shí)為成員的接口,這個(gè)接口有若干個(gè)實(shí)現(xiàn):

其中我們首先關(guān)注的是:

  • java.lang.reflect.Field?:對(duì)應(yīng)類變量。

  • java.lang.reflect.Method?:對(duì)應(yīng)類方法。

  • java.lang.reflect.Constructor?:對(duì)應(yīng)類構(gòu)造函數(shù)。

反射就是通過這三個(gè)類才能在運(yùn)行時(shí)改變對(duì)象狀態(tài)。

而 Class 對(duì)象的?getXXX()?方法返回的成員方法,成員屬性,構(gòu)造方法就是 reflect 包中相對(duì)應(yīng)的類。

其中 Method 類和 Constructor 類繼承自 Executable 類,Executable 類實(shí)現(xiàn)了 Member 接口,而 Field 類則直接實(shí)現(xiàn)了Member 接口。

從 JDK 1.8 開始,java.lang.reflect.Executable.getParameters?為我們提供了獲取普通方法或者構(gòu)造方法的名稱的能力,因此 Method 類和 Constructor 類也具有這個(gè)能力。

1. Field

每個(gè)成員變量有類型和值。java.lang.reflect.Field?為我們提供了獲取當(dāng)前對(duì)象的成員變量的類型,和重新設(shè)值的方法。

具體方法如下:

獲取變量的類型:

方法描述
Class> getType()返回這個(gè)變量的類型
Type getGenericType()如果當(dāng)前屬性有簽名屬性就返回,否則返回getType()

這里再復(fù)習(xí)一下類型,類中的變量分為兩種類型:基本類型和引用類型:

  • 基本類型( 8 種)

    • 整數(shù):byte, short, int, long

    • 浮點(diǎn)數(shù):float, double

    • 字符:char

    • 布爾值:boolean

  • 引用類型

    • 類,枚舉,數(shù)組,接口都是引用類型

    • java.io.Serializable 接口,基本類型的包裝類(比如?java.lang.Double)也是引用類型

獲取和修改成員變量的值:

拿到一個(gè)對(duì)象后,我們可以在運(yùn)行時(shí)修改它的成員變量的值,對(duì)運(yùn)行時(shí)來說,反射修改變量值的操作和類中修改變量的結(jié)果是一樣的。

基本類型的獲取方法:

方法描述
byte getByte(Object obj)獲取一個(gè)靜態(tài)或?qū)嵗?byte 字段的值
int getInt(Object obj)獲取 int 類型或另一個(gè)通過擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 int 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹?/td>
short getShort(Object obj)獲取 short 類型或另一個(gè)通過擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 short 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹?/td>
long getLong(Object obj)獲取 long 類型或另一個(gè)通過擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 long 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹?/td>
float getFloat(Object obj)獲取 float 類型或另一個(gè)通過擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 float 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹?/td>
double getDouble(Object obj)獲取 double 類型或另一個(gè)通過擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 double 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹?/td>
boolean getBoolean(Object obj)獲取一個(gè)靜態(tài)或?qū)嵗?boolean 字段的值
char getChar(Object obj)獲取 char 類型或另一個(gè)通過擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 char 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹?/td>

基本類型的設(shè)置方法:

方法描述
void setByte(Object obj, byte b)將字段的值設(shè)置為指定對(duì)象上的一個(gè) byte 值
void setShort(Object obj, short s)將字段的值設(shè)置為指定對(duì)象上的一個(gè) short 值
void setInt(Object obj, int i)將字段的值設(shè)置為指定對(duì)象上的一個(gè) int 值
void setLong(Object obj, long l)將字段的值設(shè)置為指定對(duì)象上的一個(gè) long 值
void setFloat(Object obj, float f)將字段的值設(shè)置為指定對(duì)象上的一個(gè) float 值
void setDouble(Object obj, double d)將字段的值設(shè)置為指定對(duì)象上的一個(gè) double 值
void setBoolean(Object obj, boolean z)將字段的值設(shè)置為指定對(duì)象上的一個(gè) boolean 值
void setChar(Object obj, char c)將字段的值設(shè)置為指定對(duì)象上的一個(gè) char 值

引用類型的獲取和設(shè)置方法:

方法描述
Object get(Object obj)返回指定對(duì)象上此 Field 表示的字段的值
void set(Object obj, Object value)將指定對(duì)象變量上此 Field 對(duì)象表示的字段設(shè)置為指定的新值

2. Method

繼承的方法(包括重載、重寫和隱藏的)會(huì)被編譯器強(qiáng)制執(zhí)行,這些方法都無法反射。因此,反射一個(gè)類的方法時(shí)不考慮父類的方法,只考慮當(dāng)前類的方法。

每個(gè)方法都由修飾符、返回值、參數(shù)、注解和拋出的異常組成。

java.lang.reflect.Method?方法為我們提供了獲取上述部分的 API。

重寫 Object 類的方法不再描述,重寫 Executable 的方法將在后面部分描述,在此只列舉一些 Method 類自己的方法。

方法描述
Object getDefaultValue()返回由此方法實(shí)例表示的注釋成員的默認(rèn)值
Type getGenericReturnType()獲取目標(biāo)方法返回類型對(duì)應(yīng)的 Type 對(duì)象
Class> getReturnType()獲取目標(biāo)方法返回類型對(duì)應(yīng)的 Class 對(duì)象
boolean isBridge()判斷是否是橋接方法
boolean isDefault()如果此方法是默認(rèn)方法,則返回 true ; 否則返回 false
Object invoke(Object obj, Object... args)使用反射執(zhí)行方法

首先來看一下?getReturnType()?和?getGenericReturnType()?的異同。

  • getReturnType()返回類型為 Class,getGenericReturnType()返回類型為 Type ; Class 實(shí)現(xiàn) Type 。

  • 返回值為普通簡(jiǎn)單類型如 Object , int , String 等,getGenericReturnType()返回值和getReturnType()一樣。
    例如?public String function1()那么各自返回值為:getReturnType() : class java.lang.StringgetGenericReturnType() : class java.lang.String

  • 返回值為泛型
    例如public T function2()那么各自返回值為:getReturnType() : class java.lang.ObjectgetGenericReturnType() : T

  • 返回值為參數(shù)化類型
    例如public Class function3()那么各自返回值為:getReturnType() : class java.lang.ClassgetGenericReturnType() : java.lang.Class

  • 其實(shí)反射中所有形如getGenericXXX()的方法規(guī)則都與上面所述類似。

    然后就是非常重要的invoke()方法,

    public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,
    InvocationTargetException

    第一個(gè) Object 參數(shù)代表的是對(duì)應(yīng)的 Class 對(duì)象實(shí)例,第二個(gè)參數(shù)是可變形參,能夠接受多個(gè)參數(shù)。

    這里需要注意的是,invoke 方法的兩個(gè)參數(shù)均為?Object?類型。

    簡(jiǎn)單來說,通過?invoke()?方法可以讓我們調(diào)用反射得到的類的方法。那么具體是怎么實(shí)現(xiàn)的呢?

    invoke過程圖解:

    此處不進(jìn)行過多描述,有興趣可以跟一下源碼。

    3. Constructor

    同樣地,我們也只列舉 Constructor 自己的 public 方法:

    方法描述
    T newInstance(Object ... initargs)調(diào)用構(gòu)造方法創(chuàng)建新實(shí)例

    方法使用此 Constructor 對(duì)象表示的構(gòu)造函數(shù),使用指定的初始化參數(shù)來創(chuàng)建和初始化構(gòu)造函數(shù)的聲明類的新實(shí)例。個(gè)別參數(shù)自動(dòng)展開以匹配原始形式參數(shù),原始參考參數(shù)和參考參數(shù)都需要進(jìn)行方法調(diào)用轉(zhuǎn)換。

    通過 Class 對(duì)象也可以創(chuàng)建新實(shí)例,但是兩者的異同在于:

    • Class.newInstance()只能反射無參數(shù)的構(gòu)造器,也就是使用無參數(shù)構(gòu)造方法創(chuàng)建新實(shí)例,而?Constructor.newInstance()可以反射任何構(gòu)造器;

    • Class.newInstance()?需要構(gòu)造器可見(visible),Constructor.newInstance()可以反射私有構(gòu)造器;

    • Class.newInstance()對(duì)于捕獲或者未捕獲的異常均由構(gòu)造器拋出,Constructor.newInstance()通常會(huì)把拋出的異常封裝成InvocationTargetException拋出;

    因此,還是建議直接使用?Constructor.newInstance()?反射構(gòu)造方法,幾乎全部的框架都是使用此種模式進(jìn)行反射的。

    4. Executable

    Executable 抽象類繼承 AccessibleObject 類,實(shí)現(xiàn)了 Member 接口,并有兩個(gè)子類分別為 Constructor 和 Method,如下:

    該類中定義了多個(gè)方法,能夠在通過反射獲得的成員方法或構(gòu)造方法中使用:

    該抽象類聲明的方法有:

    方法描述
    Class>[] getParameterTypes()按照聲明順序返回 Class 對(duì)象的數(shù)組,這些對(duì)象描述了此 Method/Constructor 對(duì)象所表示的方法的形參類型。
    Class>[] getExceptionTypes()返回 Class 對(duì)象的數(shù)組,這些對(duì)象描述了聲明將此 Method/Constructor 對(duì)象表示的底層方法拋出的異常類型。
    Type[] getGenericParameterTypes()按照聲明順序返回 Type 對(duì)象的數(shù)組,這些對(duì)象描述了此 Method/Constructor 對(duì)象所表示的形參類型的。
    Type[] getGenericExceptionTypes()返回 Type 對(duì)象數(shù)組,這些對(duì)象描述了聲明由此 Method/Constructor 對(duì)象拋出的異常的類型。
    String toGenericString()返回描述此 Method/Constructor 的字符串,包括類型參數(shù)。
    Annotation[][] getParameterAnnotations()返回表示按照聲明順序?qū)Υ?Method/Constructor 對(duì)象所表示方法的形參進(jìn)行注釋的那個(gè)數(shù)組的數(shù)組。
    AnnotatedType getAnnotatedReturnType()返回一個(gè)AnnotatedType對(duì)象,該對(duì)象表示使用一個(gè)類型來指定由該可執(zhí)行文件表示的方法/構(gòu)造函數(shù)的返回類型
    Type[] getGenericExceptionTypes()返回一個(gè)AnnotatedType對(duì)象數(shù)組,這些對(duì)象表示使用類型來指定由該可執(zhí)行文件表示的方法/構(gòu)造函數(shù)聲明的異常
    AnnotatedType getAnnotatedReceiverType()返回一個(gè)AnnotatedType對(duì)象,該對(duì)象表示使用一個(gè)類型來指定該可執(zhí)行對(duì)象表示的方法/構(gòu)造函數(shù)的接收者類型
    AnnotatedType[] getAnnotatedParameterTypes()返回一個(gè)AnnotatedType對(duì)象數(shù)組,這些對(duì)象表示使用類型來指定由該可執(zhí)行文件表示的方法/構(gòu)造函數(shù)的形式參數(shù)類型
    int getParameterCount()獲取參數(shù)的個(gè)數(shù)(無論是顯式聲明的還是隱式聲明的或不聲明的
    Parameter[] getParameters()返回一個(gè)參數(shù)對(duì)象數(shù)組,該數(shù)組表示該方法對(duì)象的所有參數(shù)
    boolean isVarArgs()是否是可變參數(shù)

    5. AccessibleObject

    在?java.lang.reflect?包中,存在一個(gè) AccessibleObject 類,細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)了,該類是Method、Field、Constructor 類的基類,它提供了標(biāo)記反射對(duì)象的能力,以抑制在使用時(shí)使用默認(rèn) Java 語言訪問控制檢查,從而能夠任意調(diào)用被私有化保護(hù)的方法、域和構(gòu)造函數(shù)。

    此類實(shí)現(xiàn)了 AnnotatedElement 接口,主要是注解相關(guān)的操作。

    聲明的方法:

    方法說明
    boolean isAccessible()獲取此對(duì)象的accessible標(biāo)志的值(布爾類型)
    void setAccessible(AccessibleObject[] array, boolean flag)使用單一安全檢查來設(shè)置對(duì)象數(shù)組的可訪問標(biāo)志的一個(gè)方便的方法(為了效率),靜態(tài)方法
    void setAccessible(boolean flag)將對(duì)象的可訪問標(biāo)志設(shè)置為指示的布爾值

    通過調(diào)用?setAccessible()?方法會(huì)關(guān)閉反射訪問檢查,這行代碼執(zhí)行之后不論是私有的、受保護(hù)的以及包訪問的作用域,你都可以在任何地方訪問,即使你不在他的訪問權(quán)限作用域之內(nèi)。

    • 當(dāng)isAccessible()的結(jié)果是 false 時(shí)不允許通過反射訪問該字段;

    • 當(dāng)該字段時(shí) private 修飾時(shí)isAccessible()得到的值是 false ,必須要改成 true 才可以訪問;

    • 所以?f.setAccessible(true)得作用就是讓我們?cè)谟梅瓷鋾r(shí)訪問私有變量。

    所以你其實(shí)并不是把 private 方法改成了 public ,也沒有更改任何的權(quán)限,你只是關(guān)閉了反射訪問的檢查,這種情況下,其實(shí)可以導(dǎo)致一些效率上的提升,與此同時(shí)帶來的就是安全性的下降。

    6. AnnotatedElement

    此接口位于包?java.lang.reflect?下。

    這個(gè)接口的對(duì)象代表了在當(dāng)前 JVM 中的一個(gè)“被注解元素”。在 Java 語言中,所有實(shí)現(xiàn)了這個(gè)接口的“元素”都是可以“被注解的元素”。

    使用這個(gè)接口中聲明的方法可以讀取(通過 Java 的反射機(jī)制)“被注解元素”的注解。

    這個(gè)接口中的所有方法返回的注解都是不可變的、并且都是可序列化的。這個(gè)接口中所有方法返回的數(shù)組可以被調(diào)用者修改,而不會(huì)影響其返回給其他調(diào)用者的數(shù)組。

    此類具有以下的實(shí)現(xiàn)類:

    • AccessibleObject(可訪問對(duì)象,如:方法、構(gòu)造器、屬性等)

    • Class

    • Constructor

    • Executable(可執(zhí)行的,如構(gòu)造器和方法)

    • Field(屬性,類中屬性的類型)

    • Method(方法,類中方法的類型)

    • Package(包)

    • Parameter(參數(shù),主要指方法或函數(shù)的參數(shù),其實(shí)是這些參數(shù)的類型)

    接口聲明的方法有:

    方法描述
    boolean isAnnotationPresent(Class extends Annotation> annotationClass)如果指定類型的注解出現(xiàn)在當(dāng)前元素上,則返回 true ,否則將返回false。這種方法主要是為了方便地訪問一些已知的注解。
    T getAnnotation(Class annotationClass)如果在當(dāng)前元素上存在參數(shù)所指定類型(annotationClass)的注解,則返回對(duì)應(yīng)的注解,否則將返回 null 。
    Annotation[] getAnnotations()返回在這個(gè)元素上的所有注解。如果該元素沒有注釋,則返回值是長(zhǎng)度為0的數(shù)組。該方法的調(diào)用者可以自由地修改返回的數(shù)組;它不會(huì)對(duì)返回給其他調(diào)用者的數(shù)組產(chǎn)生影響。
    T[] getAnnotationsByType(Class annotationClass)返回與該元素相關(guān)聯(lián)的注解。如果沒有與此元素相關(guān)聯(lián)的注解,則返回值是長(zhǎng)度為0的數(shù)組。這個(gè)方法與getAnnotation(Class)的區(qū)別在于,該方法檢測(cè)其參數(shù)是否為可重復(fù)的注解類型( JLS 9.6 ),如果是,則嘗試通過“ looking through ”容器注解來查找該類型的一個(gè)或多個(gè)注解。該方法的調(diào)用者可以自由地修改返回的數(shù)組;它不會(huì)對(duì)返回給其他調(diào)用者的數(shù)組產(chǎn)生影響。
    T getDeclaredAnnotation(Class annotationClass)如果參數(shù)中所指定類型的注解是直接存在于當(dāng)前元素上的,則返回對(duì)應(yīng)的注解,否則將返回null。這個(gè)方法忽略了繼承的注解。(如果沒有直接在此元素上顯示注釋,則返回null。)
    T[] getDeclaredAnnotationsByType(Class annotationClass)如果參數(shù)中所指定類型的注解是直接存在或間接存在于當(dāng)前元素上的,則返回對(duì)應(yīng)的注解。這種方法忽略了繼承的注解。如果沒有直接或間接地存在于此元素上的指定注解,則返回值是長(zhǎng)度為0的數(shù)組。這個(gè)方法和getDeclaredAnnotation(Class)的區(qū)別在于,這個(gè)方法檢測(cè)它的參數(shù)是否為可重復(fù)的注釋類型( JLS 9.6 ),如果是,則嘗試通過“ looking through ”容器注解來查找該類型的一個(gè)或多個(gè)注解。該方法的調(diào)用者可以自由地修改返回的數(shù)組;它不會(huì)對(duì)返回給其他調(diào)用者的數(shù)組產(chǎn)生影響。
    Annotation[] getDeclaredAnnotations()返回直接出現(xiàn)在這個(gè)元素上的注解。這種方法忽略了繼承的注解。如果在此元素上沒有直接存在的注解,則返回值是長(zhǎng)度為0的數(shù)組。該方法的調(diào)用者可以自由地修改返回的數(shù)組;它不會(huì)對(duì)返回給其他調(diào)用者的數(shù)組產(chǎn)生影響。

    7. Member

    Member 接口標(biāo)識(shí)類或接口的所有公共成員的集合,包括繼承的成員。Constructor / Filed / Method 類都直接或間接的繼承此類。

    Member 接口中定義的方法為:

    方法描述
    Class> getDeclaringClass()返回表示該類或接口的 Class 對(duì)象
    String getName()返回此成員表示的基礎(chǔ)成員或構(gòu)造函數(shù)的簡(jiǎn)單名稱
    int getModifiers()以整數(shù)形式返回此成員表示的成員或構(gòu)造函數(shù)的 Java 語言修飾符
    boolean isSynthetic()是否由 Java 編譯器引入

    8. 反射操作總結(jié)

    通過以上知識(shí)的學(xué)習(xí),可以知道:

    • AnnotatedElement?接口提供獲取注解相關(guān)能力。

    • Member?接口提供通用成員屬性獲取能力。

    • GenericDeclaration?接口提供給 Class 獲取泛型類型的能力。

    • Executable?抽象類提供獲取可執(zhí)行對(duì)象相關(guān)信息的能力。

    • AccessibleObject?抽象類提供判斷可更改可訪問標(biāo)識(shí)的能力。

    • Constructor、Method、Field各自基礎(chǔ)或?qū)崿F(xiàn)上述接口和抽象類,賦予了他們獲取相關(guān)信息的能力。

    以上類或接口之間的關(guān)系為:

    9. 數(shù)組和枚舉

    數(shù)組和枚舉也是對(duì)象,但是在反射中,對(duì)數(shù)組和枚舉的創(chuàng)建、訪問和普通對(duì)象有些不同,所以 Java 反射為數(shù)組和枚舉提供了一些特定的API接口。

    數(shù)組

    數(shù)組類型
    數(shù)組類型:數(shù)組本質(zhì)是一個(gè)對(duì)象,所以它也有自己的類型。例如對(duì)于int[] intArray,數(shù)組類型為class [I。數(shù)組類型中的[個(gè)數(shù)代表數(shù)組的維度,例如[代表一維數(shù)組,[[代表二維數(shù)組;[后面的字母代表數(shù)組元素類型,I代表int,一般為類型的首字母大寫( long 類型例外,為 J )。

    class [B //byte類型一維數(shù)組class [S //short類型一維數(shù)組class [I //int類型一維數(shù)組class [C //char類型一維數(shù)組class [J //long類型一維數(shù)組,J代表long類型,因?yàn)長(zhǎng)被引用對(duì)象類型占用了class [F //float類型一維數(shù)組class [D //double類型一維數(shù)組class [Lcom.dada.Season //引用類型一維數(shù)組class [[Ljava.lang.String //引用類型二維數(shù)組//獲取一個(gè)變量的類型
    Class> c = field.getType();
    //判斷該變量是否為數(shù)組
    if (c.isArray()) {
    //獲取數(shù)組的元素類型
    c.getComponentType()
    }

    創(chuàng)建和初始化數(shù)組

    Java反射為我們提供了java.lang.reflect.Array類用來創(chuàng)建和初始化數(shù)組。

    //創(chuàng)建數(shù)組, 參數(shù)componentType為數(shù)組元素的類型,后面不定項(xiàng)參數(shù)的個(gè)數(shù)代表數(shù)組的維度,參數(shù)值為數(shù)組長(zhǎng)度
    Array.newInstance(Class> componentType, int... dimensions)

    //設(shè)置數(shù)組值,array為數(shù)組對(duì)象,index為數(shù)組的下標(biāo),value為需要設(shè)置的值
    Array.set(Object array, int index, int value)

    //獲取數(shù)組的值,array為數(shù)組對(duì)象,index為數(shù)組的下標(biāo)
    Array.get(Object array, int index)

    例子,用反射創(chuàng)建?int[] array = new int[]{1, 2}

    Object array = Array.newInstance(int.class, 2);
    Array.setInt(array , 0, 1);
    Array.setInt(array , 1, 2);

    注意:反射支持對(duì)數(shù)據(jù)自動(dòng)加寬,但不允許數(shù)據(jù)narrowing(變窄?真難翻譯)。意思是對(duì)于上述set方法,你可以在int類型數(shù)組中 set short類型數(shù)據(jù),但不可以set long類型數(shù)據(jù),否則會(huì)報(bào)IllegalArgumentException。

    多維數(shù)組

    Java反射沒有提供能夠直接訪問多維數(shù)組元素的API,但你可以把多維數(shù)組當(dāng)成數(shù)組的數(shù)組處理。

    Object matrix = Array.newInstance(int.class, 2, 2);
    Object row0 = Array.get(matrix, 0);
    Object row1 = Array.get(matrix, 1);

    Array.setInt(row0, 0, 1);
    Array.setInt(row0, 1, 2);
    Array.setInt(row1, 0, 3);
    Array.setInt(row1, 1, 4);

    或者

    Object matrix = Array.newInstance(int.class, 2);
    Object row0 = Array.newInstance(int.class, 2);
    Object row1 = Array.newInstance(int.class, 2);

    Array.setInt(row0, 0, 1);
    Array.setInt(row0, 1, 2);
    Array.setInt(row1, 0, 3);
    Array.setInt(row1, 1, 4);

    Array.set(matrix, 0, row0);
    Array.set(matrix, 1, row1);

    枚舉

    枚舉隱式繼承自java.lang.Enum,Enum繼承自O(shè)bject,所以枚舉本質(zhì)也是一個(gè)類,也可以有成員變量,構(gòu)造方法,方法等;對(duì)于普通類所能使用的反射方法,枚舉都能使用;另外java反射額外提供了幾個(gè)方法為枚舉服務(wù)。

    方法描述
    Class.isEnum()此類是否為枚舉類型
    Class.getEnumConstants()返回按照聲明順序索引由枚舉類型定義的枚舉常量的列表
    java.lang.reflect.Field.isEnumConstant()此字段是否表示枚舉類型的元素

    10. 常見異常

    NoSuchMethodException

    造成這種異常的可能有:

    • 方法本身不存在

    • 傳入的參數(shù)類型不匹配(也可能是泛型擦除導(dǎo)致)

    • 傳入的參數(shù)個(gè)數(shù)不匹配

    反射調(diào)用泛型方法時(shí),由于運(yùn)行前編譯器已經(jīng)把泛型擦除,參數(shù)類型會(huì)被擦除為上邊界(默認(rèn) Object)。

    此時(shí)再試圖傳入特定類型的參數(shù)會(huì)導(dǎo)致NoSuchMethodException異常。

    解決的方式是使用 Object 作為參數(shù)類型即可。

    IllegalAccessException

    當(dāng)你訪問 private 的方法或者 private 的類中的方法,會(huì)拋出IllegalAccessException異常。

    也有可能是試圖操作 final 修飾的 Field 導(dǎo)致。

    解決方法就是給該 method 設(shè)置?setAccessible(true)。

    llegalArgumentException

    如果一個(gè)方法沒有參數(shù),但是我們反射時(shí)傳入?yún)?shù),就會(huì)導(dǎo)致?llegalArgumentException。

    InvocationTargetException

    被調(diào)用的方法本身所拋出的異常在反射中都會(huì)以?InvocationTargetException拋出。

    換句話說,反射調(diào)用過程中如果異常?InvocationTargetException拋出,說明反射調(diào)用本身是成功的,因?yàn)檫@個(gè)異常是目標(biāo)方法本身所拋出的異常。

    通過調(diào)用?InvocationTargetException?對(duì)象的?getCause()?方法,會(huì)得到 Throwable 對(duì)象,原始的異常就包含在里面。

    ClassNotFoundException

    Class.forName()傳入的包名有誤,或 Class 本身不存在。將會(huì)引發(fā)此異常。

    NoSuchFieldException

    Field 名稱不正確,或?qū)?getField()?和?getDeclaredField()?的使用不正確將會(huì)引發(fā)此異常。

    InstantiationException

    實(shí)例化出錯(cuò),可能是?Class.newInstance()?或者?Constructor.newInstance()?出現(xiàn)了異常。

    7

    簡(jiǎn)單使用反射

    我們了解的反射的基本情況,能夠獲取 Class 對(duì)象,又能從 Class 中獲取相應(yīng)的信息,記下來記錄如何使用反射。

    1. 創(chuàng)建類對(duì)象

    通過反射創(chuàng)建類對(duì)象主要有兩種方式:通過 Class 對(duì)象的?newInstance()?方法,或通過 Constructor 對(duì)象的?newInstance()?方法。前面提到過,?newInstanse()?方法就是調(diào)用無參數(shù)的構(gòu)造方法。

    第一種:Class 對(duì)象的?newInstance()?方法。

    Class clz = Apple.class;
    Apple apple = (Apple)clz.newInstance();

    第二種:Constructor 對(duì)象的?newInstance()?方法。

    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor();
    Apple apple = (Apple)constructor.newInstance();

    通過 Constructor 對(duì)象創(chuàng)建類對(duì)象可以選擇特定構(gòu)造方法,而通過 Class 對(duì)象則只能使用默認(rèn)的無參數(shù)構(gòu)造方法。下面的代碼就調(diào)用了一個(gè)有參數(shù)的構(gòu)造方法進(jìn)行了類對(duì)象的初始化。

    Class clz = Apple.class;
    Constructor constructor = clz.getConstructor(String.class, int.class);
    Apple apple = (Apple)constructor.newInstance("紅富士", 15);

    2. 獲取屬性

    下面例子展示了使用?getDeclaredFields()?獲取全部屬性:

    Class clz = Apple.class;
    Field[] fields = clz.getDeclaredFields();
    for (Field field : fields) {
    System.out.println(field.getName());
    }

    3. 調(diào)用方法

    package org.su18;

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    public class Reflection {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    // Class> testClass = TestReflection.class;
    Class> testClass = Class.forName("org.su18.TestReflection");
    Method funTwoReflect = testClass.getDeclaredMethod("funTwo", String.class, String.class, String.class);
    funTwoReflect.setAccessible(true);
    funTwoReflect.invoke(null, "Kiki", "Pants", "Dance");

    System.out.println("\n");

    Method funOneReflect = testClass.getDeclaredMethod("funOne");
    funOneReflect.invoke(null);

    System.out.println("\n");

    Method funThreeReflect = testClass.getDeclaredMethod("funThree", Integer[].class);
    funThreeReflect.setAccessible(true);
    Integer[] nums = {1,2,3,4,5,6,123,141};
    int result = (int) funThreeReflect.invoke(new TestReflection(), (Object) nums);
    System.out.println(result);
    }
    }

    class TestReflection {

    static void funOne() {
    System.out.println("Let's dance");
    }


    private static void funTwo(String name, String clothes, String action) {
    System.out.printf("The First Man Name Is %s .\n", name);
    System.out.printf("He Wears Such Little %s .\n", clothes);
    System.out.print("His Brother Was A Champion .\n");
    System.out.printf("But %s Loves To %s .\n", name, action);
    }

    private int funThree(Integer... numbers) {
    int sum = 0;
    for (Integer number : numbers) {
    sum += number;
    }
    return sum;
    }
    }

    上例演示了在各種不同情況下調(diào)用的方式的不同結(jié)果如下:

    4. 調(diào)用內(nèi)部類

    假設(shè)com.reflect.Outer類,有一個(gè)內(nèi)部類 inner 和靜態(tài)內(nèi)部類 StaticInner 。那么靜態(tài)內(nèi)部類的構(gòu)造函數(shù)為Outer$StaticInner();?而普通內(nèi)部類的構(gòu)造函數(shù)為Outer$Inner(Outer outer),多了一個(gè) final 的 Outer 類型屬性,即Outer$Inner.this$0,用于存儲(chǔ)外部類的屬性值,也就是說非static內(nèi)部類保持了外部類的引用。

    直接實(shí)例化內(nèi)部類方法如下:

    // 靜態(tài)內(nèi)部類
    Outer.StaticInner sInner = new Outer.StaticInner();
    // 非靜態(tài)內(nèi)部類
    Outer.Inner inner = new Outer().new Inner();

    內(nèi)部類的類名使用采用 $ 符號(hào),來連接外部類與內(nèi)部類,格式為outer$Inner。

    String className = "com.reflect.Outer$Inner";
    Class.forName(className);

    除了格式了差異,關(guān)于內(nèi)部類的屬性和方法操作基本相似,下面以調(diào)用該靜態(tài)類的靜態(tài)方法為例:

    public static Object invokeMethod(String methodName, Class[] argsType, Object... args) {
    Class clazz = Class.forName("com.reflect.Outer$StaticInner");
    Method method = clazz.getDeclaredMethod(methodName, argsType);
    method.setAccessible(true);
    return method.invoke(null, args);
    8

    反射的作用

    有了如此強(qiáng)大的反射特性,我們可以進(jìn)行:

    • 為所欲為:理論上可以訪問任意類任意方法任意成員變量,這一步能做的就很多,直接調(diào)用某些類的方法、在運(yùn)行中獲取某些類的某些成員變量、通過獲取注解對(duì)web框架獲取到類和地址的映射關(guān)系等等。

    • 框架實(shí)現(xiàn):大部分的框架基本都是基于反射實(shí)現(xiàn)的。

    • 防護(hù)繞過:針對(duì) RASP 技術(shù)防護(hù)的站,可以反射調(diào)用底層方法進(jìn)行封裝,來繞過一些攔截位置。

    還有諸多作用等著大家發(fā)掘吧~

    9

    反射的缺點(diǎn)

    • 性能開銷
      反射涉及類型動(dòng)態(tài)解析,所以 JVM 無法對(duì)這些代碼進(jìn)行優(yōu)化。因此,反射操作的效率要比那些非反射操作低得多。我們應(yīng)該避免在經(jīng)常被執(zhí)行的代碼或?qū)π阅芤蠛芨叩某绦蛑惺褂梅瓷洹?/p>

    • 安全限制
      使用反射技術(shù)要求程序必須在一個(gè)沒有安全限制的環(huán)境中運(yùn)行。如果一個(gè)程序必須在有安全限制的環(huán)境中運(yùn)行,如 Applet,那么這就是個(gè)問題了。

    • 內(nèi)部曝光
      由于反射允許代碼執(zhí)行一些在正常情況下不被允許的操作(比如訪問私有的屬性和方法),所以使用反射可能會(huì)導(dǎo)致意料之外的副作用--代碼有功能上的錯(cuò)誤,降低可移植性。反射代碼破壞了抽象性,因此當(dāng)平臺(tái)發(fā)生改變的時(shí)候,代碼的行為就有可能也隨著變化。

    雖然大家都在說反射性能低,是因?yàn)闊o法通過 JIT 進(jìn)行優(yōu)化,但現(xiàn)在的 JDK 已經(jīng)非常強(qiáng)悍了,并不至于因?yàn)樾阅荛_銷大而無法使用強(qiáng)大的反射功能。

    凌天實(shí)驗(yàn)室

    凌天實(shí)驗(yàn)室,是安百科技旗下針對(duì)應(yīng)用安全領(lǐng)域進(jìn)行攻防研究的專業(yè)技術(shù)團(tuán)隊(duì),其核心成員來自原烏云創(chuàng)始團(tuán)隊(duì)及社區(qū)知名白帽子,團(tuán)隊(duì)專業(yè)性強(qiáng)、技術(shù)層次高且富有實(shí)戰(zhàn)經(jīng)驗(yàn)。實(shí)驗(yàn)室成立于2016年,發(fā)展至今團(tuán)隊(duì)成員已達(dá)35人,在應(yīng)用安全領(lǐng)域深耕不輟,向網(wǎng)絡(luò)安全行業(yè)頂尖水平攻防技術(shù)團(tuán)隊(duì)的方向夯實(shí)邁進(jìn)。

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的java swing jbutton_Java 反射的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。