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