建立網(wǎng)站教程視頻百度官方電話號(hào)碼
Java反射是Java編程語(yǔ)言的一種強(qiáng)大功能,它能夠檢查和修改運(yùn)行時(shí)對(duì)象的行為。我們將詳細(xì)探討Java反射中的9個(gè)主要操作。
1. 獲取Class對(duì)象
在Java反射中,所有操作起點(diǎn)都是獲取對(duì)應(yīng)的Class
對(duì)象,由于每個(gè)實(shí)例對(duì)象都屬于某個(gè)類,所以我們最常見(jiàn)的就是從一個(gè)實(shí)例對(duì)象獲取其對(duì)應(yīng)類的Class
對(duì)象。
Java提供了兩種方式來(lái)獲取一個(gè)類的Class
對(duì)象:使用getClass()
方法或使用Class.forName()
靜態(tài)方法。
1.1 通過(guò)實(shí)例對(duì)象獲取Class對(duì)象
getClass()
是一個(gè)實(shí)例方法,它存在于所有Java對(duì)象中,因?yàn)槊總€(gè)對(duì)象都繼承自Object
,而Object
類提供了此方法。當(dāng)在一個(gè)特定對(duì)象上調(diào)用該方法,將返回該對(duì)象所屬的Class對(duì)象。
考慮以下的例子,我們有一個(gè)String對(duì)象,并調(diào)用getClass()
方法獲取該對(duì)象的Class
對(duì)象:
String str = "Hello, World!";
Class strClass = str.getClass();
在這個(gè)例子中,由于字符串"Hello, World!"
是一個(gè)String
對(duì)象,所以getClass()
方法返回的是String
類的Class
對(duì)象。
1.2 通過(guò)類的全限定名獲取Class對(duì)象
如果你只知道類的全限定名,即包含包名的類名,如java.lang.String
,可以通過(guò)Class
類的靜態(tài)方法forName(String className)
來(lái)獲取該類的Class
對(duì)象。這種方式常用于動(dòng)態(tài)加載類。
例如,我們?nèi)缦芦@取String
類的Class
對(duì)象:
Class strClass = Class.forName("java.lang.String"); //動(dòng)態(tài)加載
Class<?> strClass = String.class; //靜態(tài)加載
此方法將加載名為"java.lang.String"
的類,并返回該類的Class
對(duì)象。
總結(jié)起來(lái),獲取Class對(duì)象的主要方式就是通過(guò)實(shí)例對(duì)象的getClass()
方法和Class.forName()
靜態(tài)方法,而后續(xù)的反射操作,都是基于這個(gè)Class對(duì)象進(jìn)行的。
2. 創(chuàng)建對(duì)象
利用反射,我們可以動(dòng)態(tài)地創(chuàng)建一個(gè)對(duì)象。通過(guò)在獲取到的Class對(duì)象上調(diào)用newInstance()
方法,可以創(chuàng)建該類的新實(shí)例,等價(jià)于使用new關(guān)鍵字。
2.1 使用newInstance()
創(chuàng)建對(duì)象
Class類中的newInstance()
方法會(huì)調(diào)用類的無(wú)參構(gòu)造函數(shù)來(lái)創(chuàng)建該類的新實(shí)例。這等價(jià)于使用new
關(guān)鍵字和無(wú)參構(gòu)造函數(shù)創(chuàng)建對(duì)象。
例如,假設(shè)我們有一個(gè)Person
類有一個(gè)無(wú)參構(gòu)造函數(shù),我們可以像下面這樣創(chuàng)建Person
的新實(shí)例:
Class<?> personClass = Person.class;
Person person = (Person) personClass.newInstance();
這里,我們首先獲取到Person
類的Class對(duì)象,然后通過(guò)調(diào)用newInstance()
方法創(chuàng)建了Person
的新實(shí)例。
2.2 注意事項(xiàng)與限制
-
newInstance()
方法只能調(diào)用無(wú)參構(gòu)造函數(shù),如果需要使用帶參數(shù)的構(gòu)造函數(shù)創(chuàng)建對(duì)象,需要使用Constructor類的newInstance(Object... initargs)
方法。 -
調(diào)用
newInstance()
方法時(shí),無(wú)參構(gòu)造函數(shù)的訪問(wèn)權(quán)限需要是可訪問(wèn)的。如果無(wú)參構(gòu)造函數(shù)是private
,那么調(diào)用newInstance()
方法就會(huì)拋出IllegalAccessException
。 -
如果無(wú)參構(gòu)造函數(shù)在執(zhí)行過(guò)程中拋出了異常,那么調(diào)用
newInstance()
方法也會(huì)拋出InstantiationException
。
在使用newInstance()
方法的時(shí)候,對(duì)上述限制和異常需要有一定的注意和處理。
雖然可以使用newInstance()
方法,但因?yàn)槠涫褂玫氖菬o(wú)參構(gòu)造函數(shù),所以在需要參數(shù)構(gòu)造的情況下,推薦使用Constructor.newInstance(Object... initargs)
方法。
要注意,從Java 9開(kāi)始,Class類的newInstance()
方法已經(jīng)被棄用。取而代之的是,應(yīng)該使用Class類的getDeclaredConstructor()
方法獲取Constructor對(duì)象,然后調(diào)用Constructor對(duì)象的newInstance()
方法來(lái)創(chuàng)建類的實(shí)例。
以下是使用getDeclaredConstructor().newInstance()
方法創(chuàng)建實(shí)例的示例:
Class<?> personClass = Person.class;
Person person = (Person) personClass.getDeclaredConstructor().newInstance();
使用這種方式創(chuàng)建對(duì)象,我們可以選擇調(diào)用哪個(gè)構(gòu)造函數(shù)并傳入合適的參數(shù),因此它提供了更大的靈活性。
另外,當(dāng)使用反射創(chuàng)建對(duì)象時(shí),可能會(huì)拋出各種異常(比如ClassNotFoundException
,InstantiationException
,IllegalAccessException
,NoSuchMethodException
,InvocationTargetException
等),因此在使用反射時(shí),需要進(jìn)行恰當(dāng)?shù)漠惓L幚怼?/p>
3. 獲取和操作成員變量
我們可以通過(guò)getDeclaredField(String name)
方法從某個(gè)Class對(duì)象中獲取指定的字段,然后可以使用Field類的set(Object obj, Object value)
方法來(lái)修改此字段的值。
3.1 獲取成員變量
Java反射提供了從Class對(duì)象中獲取指定字段的方法。具體來(lái)說(shuō),通過(guò)如下幾個(gè)方法:
getDeclaredField(String name)
:返回一個(gè)Field
對(duì)象,它反映此Class
對(duì)象所表示的類或接口的指定已聲明字段。這包括所有的字段,無(wú)論其訪問(wèn)權(quán)限如何。getDeclaredFields()
:返回此Class
對(duì)象表示的類或接口所聲明的所有字段的Field
對(duì)象數(shù)組。getField(String name)
:返回一個(gè)Field
對(duì)象,它反映此Class
對(duì)象所表示的類或接口的指定公開(kāi)字段,包括其父類或父接口中的字段。getFields()
:返回包含此Class
對(duì)象表示的類或接口的所有可訪問(wèn)公共字段的Field
對(duì)象的數(shù)組。
例如,假設(shè)我們有一個(gè)Person
類:
public class Person {private String name;// getters and setters
}
我們可以使用以下幾種方法獲取"name"字段以及其他字段:
// 獲取單個(gè)字段
Class<?> personClass = Person.class;
Field nameField = personClass.getDeclaredField("name");// 獲取所有聲明的字段(不包括父類)
Field[] declaredFields = personClass.getDeclaredFields();// 獲取單個(gè)公有字段(包括父類)
Field nameFieldPublic = personClass.getField("name");// 獲取所有公有字段(包括父類)
Field[] publicFields = personClass.getFields();
注意:getDeclaredField
和getDeclaredFields
方法會(huì)返回所有字段,無(wú)論其是否公開(kāi);而getField
和getFields
方法只會(huì)返回公開(kāi)的字段,包括從父類繼承的字段。
3.2 操作成員變量
獲取到Field
對(duì)象后,我們可以對(duì)其進(jìn)行操作。Field
類提供了多個(gè)方法來(lái)設(shè)置或獲取字段的值。
例如,我們可以使用set(Object obj, Object value)
方法來(lái)修改字段的值。此方法接受兩個(gè)參數(shù):第一個(gè)參數(shù)是要修改的對(duì)象,第二個(gè)參數(shù)是新的字段值。
假設(shè)我們有一個(gè)Person
對(duì)象person
,我們可以這樣修改其"name"字段的值:
nameField.setAccessible(true); // necessary for private fields
nameField.set(person, "Alice");
這段代碼首先調(diào)用setAccessible(true)
方法允許訪問(wèn)私有字段,然后調(diào)用set(Object obj, Object value)
方法將person
的"name"字段值設(shè)為"Alice"。
注意,setAccessible
方法是用于削弱Java語(yǔ)言訪問(wèn)控制的,所以需要謹(jǐn)慎使用。此外,對(duì)于final
字段,反射不允許改變其值。
總的來(lái)說(shuō),Java反射提供了靈活的方式來(lái)操作類的字段,實(shí)現(xiàn)了在運(yùn)行時(shí)動(dòng)態(tài)地獲取和設(shè)置字段的值。
4. 獲取構(gòu)造函數(shù)并創(chuàng)建對(duì)象
除了使用newInstance()
方法,我們也可以通過(guò)獲取類的特定構(gòu)造函數(shù)來(lái)創(chuàng)建新對(duì)象。這可以通過(guò)調(diào)用getDeclaredConstructor(Class<?>... parameterTypes)
方法并傳入?yún)?shù)類型來(lái)實(shí)現(xiàn)。
4.1 獲取構(gòu)造函數(shù)
Java反射提供了從Class對(duì)象中獲取構(gòu)造函數(shù)的功能。具體的方式主要有以下幾種:
getDeclaredConstructor(Class... parameterTypes)
:返回Constructor
對(duì)象,該對(duì)象反映此Class
對(duì)象所表示的類或接口的指定聲明構(gòu)造函數(shù)。該方法接受一個(gè)或多個(gè)Class類型的參數(shù),表示你要獲取的構(gòu)造函數(shù)的參數(shù)類型。getDeclaredConstructors()
:返回Constructor
對(duì)象的一個(gè)數(shù)組,這些對(duì)象反映此Class
對(duì)象表示的類或接口的所有已聲明構(gòu)造函數(shù)。getConstructor(Class... parameterTypes)
:返回一個(gè)Constructor
對(duì)象,該對(duì)象反映此Class
對(duì)象所表示的類或接口的指定公開(kāi)(public)構(gòu)造函數(shù)。getConstructors()
:返回包含一個(gè)數(shù)組,該數(shù)組包含Constructor
對(duì)象反映由此Class
對(duì)象表示的類的所有公開(kāi)(public)構(gòu)造函數(shù)。
例如,假設(shè)我們有一個(gè)Person
類,它有一個(gè)接受String
類型參數(shù)的構(gòu)造函數(shù):
public class Person {private String name;public Person(String name) {this.name = name;}// getters and setters
}
我們可以使用以下方法來(lái)獲取該P(yáng)erson類的構(gòu)造函數(shù):
// 獲取單個(gè)指定參數(shù)類型的構(gòu)造函數(shù)
Class<?> personClass = Person.class;
Constructor<?> constructor = personClass.getDeclaredConstructor(String.class);// 獲取所有已聲明的構(gòu)造函數(shù)
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();// 獲取單個(gè)公開(kāi)(public)的構(gòu)造函數(shù)
Constructor<?> publicConstructor = personClass.getConstructor(String.class);// 獲取所有公開(kāi)(public)的構(gòu)造函數(shù)
Constructor<?>[] publicConstructors = personClass.getConstructors();
注意,getDeclaredConstructor
和getDeclaredConstructors
方法返回的都是類自身聲明的構(gòu)造函數(shù),無(wú)論其訪問(wèn)權(quán)限如何;而getConstructor
和getConstructors
方法返回的都是類的公開(kāi)(public)構(gòu)造函數(shù),不包括類自身的私有(private)和受保護(hù)(protected)構(gòu)造函數(shù)。
4.2 使用構(gòu)造函數(shù)創(chuàng)建對(duì)象
獲取到Constructor
對(duì)象后,我們可以使用它來(lái)創(chuàng)建類的新實(shí)例。具體來(lái)說(shuō),Constructor
類的newInstance(Object... initargs)
方法可以創(chuàng)建一個(gè)新的對(duì)象。這個(gè)方法接受一系列參數(shù),用于傳遞給構(gòu)造函數(shù)。
使用上述Person
類的例子,我們可以這樣創(chuàng)建一個(gè)新的Person
對(duì)象:
Person person = (Person) constructor.newInstance("Alice");
這段代碼將創(chuàng)建一個(gè)新的Person
對(duì)象,其"name"字段的值為"Alice"。
相比直接使用Class
類的newInstance()
方法,使用Constructor
對(duì)象創(chuàng)建新實(shí)例的好處是可以選擇調(diào)用哪個(gè)構(gòu)造函數(shù),并傳入合適的參數(shù)。
5. 獲取和調(diào)用成員方法
我們也可以使用反射來(lái)獲取并調(diào)用類的成員方法。通過(guò)getDeclaredMethod(String name, Class<?>... parameterTypes)
方法可以獲取指定的方法,然后通過(guò)調(diào)用Method類的invoke(Object obj, Object... args)
方法來(lái)調(diào)用此方法。
5.1 獲取成員方法
Java反射提供了獲取類的成員方法的功能。具體的方式主要有以下幾種:
getDeclaredMethod(String name, Class... parameterTypes)
:返回一個(gè)Method
對(duì)象,它反映此Class
對(duì)象所表示的類或接口的指定已聲明的方法。此方法接收兩個(gè)參數(shù):一個(gè)是要獲取的方法的名稱,另一個(gè)是該方法參數(shù)的類型。getDeclaredMethods()
:返回包含Method
對(duì)象的一個(gè)數(shù)組,這些對(duì)象反映此Class
對(duì)象表示的類或接口聲明的所有方法, 不包括繼承的方法。getMethod(String name, Class... parameterTypes)
:返回一個(gè)Method
對(duì)象,它反映此Class
對(duì)象所代表的類或接口的指定公共(public)成員方法,包括其繼承的方法。getMethods()
:返回包含Method
對(duì)象的一個(gè)數(shù)組,這些對(duì)象反映此Class
對(duì)象所代表的類或接口的所有公共(public)成員方法,包括其繼承的方法。
例如,假設(shè)我們有一個(gè)Person
類,有一個(gè)名為"sayHello"的方法:
public class Person {public void sayHello(String message) {System.out.println("Hello, " + message);}
}
我們可以使用以下方法來(lái)獲取Person
類的方法:
// 獲取單個(gè)指定參數(shù)類型的方法
Class<?> personClass = Person.class;
Method sayHelloMethod = personClass.getDeclaredMethod("sayHello", String.class);// 獲取所有已聲明的方法
Method[] declaredMethods = personClass.getDeclaredMethods();// 獲取單個(gè)公開(kāi)(public)的方法,包括父類的方法
Method sayHelloMethodPublic = personClass.getMethod("sayHello", String.class);// 獲取所有公開(kāi)(public)的方法,包括父類的方法
Method[] publicMethods = personClass.getMethods();
注意,getDeclaredMethod
和getDeclaredMethods
方法會(huì)返回類自身聲明的所有方法,無(wú)論其訪問(wèn)權(quán)限如何,而getMethod
和getMethods
方法返回的都是類的公開(kāi)(public)方法,包括從父類繼承的方法。
5.2 調(diào)用成員方法
獲取到Method
對(duì)象后,可以使用它來(lái)調(diào)用類的成員方法。具體使用Method
類的invoke(Object obj, Object... args)
方法實(shí)現(xiàn)。invoke
方法接收兩個(gè)參數(shù):第一個(gè)參數(shù)是要調(diào)用方法的對(duì)象,第二個(gè)參數(shù)是調(diào)用方法時(shí)傳入的參數(shù)。
使用前面Person
類的例子,我們可以這樣調(diào)用"sayHello"方法:
Person person = new Person();
sayHelloMethod.invoke(person, "Alice");
執(zhí)行這段代碼后,控制臺(tái)將輸出:Hello, Alice
此外,對(duì)于訪問(wèn)權(quán)限的問(wèn)題,如私有方法,也是可以通過(guò)setAccessible(true)
來(lái)關(guān)閉Java語(yǔ)言的訪問(wèn)權(quán)限檢查,從而調(diào)用私有方法。此處需要格外注意,這么做可能會(huì)導(dǎo)致安全問(wèn)題,必須要謹(jǐn)慎對(duì)待。
6. 獲取類名
我們可以通過(guò)調(diào)用getName()
方法來(lái)獲取類的全限定名(包括包名)。
在Java反射中,Class
類的getName()
方法可用于獲取類的全限定名,也就是類名包含包名。
例如,假設(shè)我們有一個(gè)完全限定名為"com.example.Person"的類,我們可以這樣獲取該類的全限定名:
Class<?> personClass = Person.class;
String className = personClass.getName();
執(zhí)行這段代碼后,className
的值將是:“com.example.Person”。
此外,Class
類還有其他一些方法可以獲取類的信息:
getSimpleName()
:返回類的簡(jiǎn)單名字,不包括包名。對(duì)于上述例子,getSimpleName()
將返回"Person"。getPackage()
:返回包含此類的Package
對(duì)象??梢杂脕?lái)獲取包級(jí)別的注解、聲明的版本等信息。getSuperclass()
:返回表示此Class
所表示的實(shí)體(類、接口、基本類型或 void)的超類的Class
。getInterfaces()
:確定此Class
對(duì)象所表示的類或接口實(shí)現(xiàn)的接口。
這就是關(guān)于Java反射獲取類名和其他相關(guān)類信息的內(nèi)容。
7. 獲取父類
我們可以通過(guò)調(diào)用getSuperclass()
方法獲取類的父類。
在Java反射中,Class
類的getSuperclass()
方法可以獲取類的父類信息。
例如,假設(shè)我們有一個(gè)名為"Person"的類,它繼承自"Human"類,我們可以這樣獲取"Person"類的父類信息:
Class<?> personClass = Person.class;
Class<?> superClass = personClass.getSuperclass();
執(zhí)行這段代碼后,superClass
的值將是代表"Human"類的Class
對(duì)象。
除此之外,Class
類提供了一些其他方法來(lái)獲取類繼承相關(guān)的信息:
getGenericSuperclass()
:返回表示此Class
所表示的實(shí)體(類、接口、基本類型或 void)的直接超類的Type
。如果超類是參數(shù)化的,則返回的類型是參數(shù)化類型。getInterfaces()
:確定此Class
對(duì)象所表示的類或接口實(shí)現(xiàn)的接口。getGenericInterfaces()
:返回表示此Class
所表示的實(shí)體(類、接口、基本類型或 void)實(shí)現(xiàn)的接口的Type
。
例如,獲取"Person"類的所有實(shí)現(xiàn)的接口:
Type[] interfaces = personClass.getInterfaces();
以上就是關(guān)于Java反射中獲取類的父類和其他繼承相關(guān)信息的方法。
8. 獲取實(shí)現(xiàn)的接口
我們也可以通過(guò)使用getInterfaces()
方法獲取一個(gè)類實(shí)現(xiàn)的所有接口。
在Java反射中,Class
類的getInterfaces()
方法可以獲取一個(gè)類實(shí)現(xiàn)的所有接口。它返回一個(gè)包含表示這些接口的Class
對(duì)象的數(shù)組。
例如,如果我們有一個(gè)Person
類實(shí)現(xiàn)了Runnable
和Comparable
接口:
public class Person implements Runnable, Comparable<Person> {// ...
}
我們可以這樣獲取Person
類實(shí)現(xiàn)的所有接口:
Class<?> personClass = Person.class;
Class<?>[] interfaceClasses = personClass.getInterfaces();
執(zhí)行這段代碼后,interfaceClasses
將是一個(gè)包含表示Runnable
類和Comparable
類的Class
對(duì)象的數(shù)組。
除了getInterfaces()
方法,Class
類還提供了getGenericInterfaces()
方法,它返回一個(gè)Type
對(duì)象的數(shù)組,這些對(duì)象代表這個(gè)類所實(shí)現(xiàn)的所有接口。相較于getInterfaces()
,getGenericInterfaces()
考慮了泛型接口。
對(duì)于前面的Person
類,我們可以這樣獲取泛型接口:
Type[] genericInterfaces = personClass.getGenericInterfaces();
執(zhí)行這代碼后,genericInterfaces
將是一個(gè)包含表示Runnable
接口和Comparable<Person>
接口的Type
對(duì)象的數(shù)組。
以上就是在Java反射中獲取類所實(shí)現(xiàn)接口的方法。
9. 獲取類的修飾符
我們可以通過(guò)getModifiers()
方法獲取類的修飾符,比如public、private、static等。然后我們可以使用java.lang.reflect.Modifier
類的靜態(tài)方法來(lái)解析這些修飾符。
在Java反射中,我們可以通過(guò)Class
類的getModifiers()
方法獲取類的修飾符,該方法返回一個(gè)整數(shù),表示類的修飾符。這個(gè)整數(shù)值表示的修飾符包括public、private、protected、static、final、abstract、interface等。例如,如下的獲取方式:
Class<?> personClass = Person.class;
int modifiers = personClass.getModifiers();
然后,我們可以使用java.lang.reflect.Modifier
類的一系列靜態(tài)方法來(lái)解析這個(gè)整數(shù)值,以此來(lái)判斷類的修飾符。Modifier
類提供如下一些方法:
isAbstract(int mod)
:判斷是否為抽象類。isFinal(int mod)
:判斷是否為final類。isInterface(int mod)
:判斷是否為接口。isPrivate(int mod)
:判斷是否為private類。isProtected(int mod)
:判斷是否為protected類。isPublic(int mod)
:判斷是否為public類。isStatic(int mod)
:判斷是否為static類。
如,判斷"Person"類是否為public和abstract:
boolean isPublic = Modifier.isPublic(modifiers);
boolean isAbstract = Modifier.isAbstract(modifiers);
以上主要介紹了如何通過(guò)反射獲取類的修飾符,以及如何解析獲取到的修飾符。