
?JAVA的反射機(jī)制是指在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類(lèi)都能夠知道這個(gè)類(lèi)的所有屬性和方法,對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性。這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱(chēng)為Java語(yǔ)言的反射機(jī)制。反射是Java語(yǔ)言中非常重要機(jī)制,很多第三方框架都用到了反射,本小節(jié)將詳細(xì)講解反射機(jī)制的原理和作用。
(資料圖片僅供參考)
每個(gè)類(lèi)被加載之后,虛擬機(jī)就會(huì)為該類(lèi)生成一個(gè)對(duì)應(yīng)的Class對(duì)象,通過(guò)該Class對(duì)象就可以訪問(wèn)到這個(gè)類(lèi)。在Java程序中獲得Class對(duì)象通常有如下三種方式:?
使用Class類(lèi)的forName()靜態(tài)方法。該方法需要傳入字符串參數(shù),該字符串參數(shù)的值是某個(gè)類(lèi)的全限定類(lèi)名,全限定類(lèi)名包含完整的包名和類(lèi)名。?調(diào)用某個(gè)類(lèi)的class屬性來(lái)獲取該類(lèi)對(duì)應(yīng)的Class對(duì)象。例如,Person.class 將會(huì)返回Person類(lèi)對(duì)應(yīng)的Class對(duì)象。?調(diào)用某個(gè)對(duì)象的getClass()方法。該方法是Object 類(lèi)中的一個(gè)方法,所以所有的Java對(duì)象都可以調(diào)用該方法,該方法將會(huì)返回該對(duì)象所屬類(lèi)對(duì)應(yīng)的Class對(duì)象。?第一種方式和第二種方式都是直接根據(jù)類(lèi)來(lái)取得該類(lèi)的Class對(duì)象。相比之下,第二種方式有如下兩種優(yōu)勢(shì)。?
代碼更安全。程序在編譯階段就可以檢查需要訪問(wèn)的Class對(duì)象是否存在。?程序性能更好。因?yàn)檫@種方式無(wú)須調(diào)用方法,所以性能更好。?也就是說(shuō),大部分時(shí)候都應(yīng)該使用第二種方式來(lái)獲取指定類(lèi)的Class對(duì)象。但如果程序只能獲得一個(gè)字符串,例如“java.lang.String”,如果需要根據(jù)字符串來(lái)獲取對(duì)應(yīng)的Class對(duì)象,則只能使用第一種方式,即使用Class的forName()靜態(tài)方法獲取Class對(duì)象,該方法可能拋出ClassNotFoundException異常。一旦獲得了某個(gè)類(lèi)所對(duì)應(yīng)的Class對(duì)象之后,程序就可以調(diào)用Class對(duì)象的方法來(lái)獲得該對(duì)象和該類(lèi)的真實(shí)信息了。?
Class類(lèi)代表了一個(gè)類(lèi),而一個(gè)類(lèi)的方法、屬性以及所在的包也都可以用類(lèi)來(lái)表示。表示普通方法的類(lèi)是Method,表示構(gòu)造方法的類(lèi)是Constructor,表示屬性的類(lèi)是Field,而表示包的類(lèi)是Package,只有了解了這些類(lèi)的意義才能很好的學(xué)習(xí)Class類(lèi)。?
Class類(lèi)提供了大量方法來(lái)獲取該Class對(duì)象所對(duì)應(yīng)類(lèi)的詳細(xì)信息,這些方法中很多都包括多個(gè)重載的版本,由于重載版本眾多,下面的表19-2對(duì)每個(gè)方法只列出一個(gè)版本,讀者可以查閱API文檔來(lái)查看每個(gè)方法的詳情。?
表19-2 Class類(lèi)的方法?
方法? | 功能? |
Connstructor | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的、帶指定形參列表的public構(gòu)造方法。? |
Constructor>[] getConstructors()? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的所有public構(gòu)造方法? |
Constructor | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的、帶指定形參列表的構(gòu)造方法,與構(gòu)造方法的訪問(wèn)權(quán)限無(wú)關(guān)。? |
Constructor>[] getDeclaredConstructors()? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的所有構(gòu)造方法,與構(gòu)造方法的訪問(wèn)權(quán)限無(wú)關(guān)? |
Method getMethod(String name, Class> ... parameterTypes)? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的、帶指定形參列表的public方法? |
Method[] getMethods()? | 返回此Class對(duì)象所表示的類(lèi)的所有public方法? |
Method getDeclaredMethod(String name, Class>.. parameterTypes)? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的、帶指定形參列表的方法,與方法的訪問(wèn)權(quán)限無(wú)關(guān)? |
Method[] getDeclaredMethods()? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的全部方法,與方法的訪問(wèn)權(quán)限無(wú)關(guān)? |
Field getField(String name)? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的、指定名稱(chēng)的public屬性? |
Field[]getFields()? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的所有public屬性? |
Field getDeclaredField(String name):? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的、指定名稱(chēng)的屬性,與屬性的訪問(wèn)權(quán)限無(wú)關(guān)? |
Field[] getDeclaredFields():? | 返回此Class對(duì)象對(duì)應(yīng)類(lèi)的全部屬性,與屬性的訪問(wèn)權(quán)限無(wú)關(guān)? |
嘗試獲取該Class對(duì)象對(duì)應(yīng)類(lèi)上存在的、指定類(lèi)型的注解,如果該類(lèi)型的注解不存在,則返回null? | |
該方法嘗試獲取直接修飾該Class對(duì)象對(duì)應(yīng)類(lèi)的、指定類(lèi)型的注解,如果該類(lèi)型的注解不存在,則返回null? | |
Annotation[] getAnnotations()? | 返回修飾該Class對(duì)象對(duì)應(yīng)類(lèi)上存在的所有的注解? |
Annotation[] getDeclaredAnnotations()? | 返回直接修飾該Class對(duì)應(yīng)類(lèi)的所有注解? |
該方法的功能與getAnnotation()方法基本相似。但由于Java8增加了重復(fù)注解功能,因此需要使用該方法獲取修飾該類(lèi)的、指定類(lèi)型的多個(gè)注解? | |
Class>[] getDeclaredClasses()? | 返回該Class對(duì)象對(duì)應(yīng)類(lèi)里包含的全部?jī)?nèi)部類(lèi)? |
Class> getDeclaringClass()? | 返回該Class對(duì)象對(duì)應(yīng)類(lèi)所在的外部類(lèi)? |
Class>[] getlnterfaces()? | 返回該Class對(duì)象對(duì)應(yīng)類(lèi)所實(shí)現(xiàn)的全部接口? |
Class super T> getSuperclass()? | 返回該Class對(duì)象對(duì)應(yīng)類(lèi)的父類(lèi)的Class對(duì)象? |
int getModifiers()? | 返回此類(lèi)或接口的所有修飾符。修飾符由public、 protected、private、final、static、abstract 等對(duì)應(yīng)的常量組成,返回的整數(shù)應(yīng)使用Modifer工具類(lèi)的方法來(lái)解碼,才可以獲取真實(shí)的修飾符? |
Package getPackage()? | 獲取此類(lèi)的包? |
String getName()? | 以字符串 形式返回此Class對(duì)象所表示的類(lèi)的名稱(chēng)? |
String getSimpleName()? | 以字符串形式返回此Class對(duì)象所表示的類(lèi)的簡(jiǎn)稱(chēng),即不包含包名,只有類(lèi)名? |
boolean isAnnotation()。? :? 。? | 返回此Class對(duì)象是否表示一個(gè)注解類(lèi)型? |
boolean isAnnotationPresent(Class extends Annotation> annotationClass)? | 判斷此Class 對(duì)象是否使用了Annotation修飾? |
boolean isAnonymousClass()? | 返回此Class對(duì)象是否是一個(gè)匿 名類(lèi)? |
boolean isArray()? | 返回此Class對(duì)象是否表示一個(gè)數(shù)組類(lèi)? |
boolean isEnum()? | 返回此Class對(duì)象是否表示一個(gè)枚舉? |
boolean isInterface? | 返回此Class對(duì)象是否表示一個(gè)接口? |
boolean islnstance(Object obj)? | 判斷obj是否是此Class 對(duì)象的實(shí)例,該方法與? instanceof操作符作用相同? |
表19-2中,getMethod0方法和getConstructor()方法都需要傳入多個(gè)類(lèi)型為Class>的參數(shù),這些參數(shù)用于獲取指定的方法或指定的構(gòu)造方法。假設(shè)某個(gè)類(lèi)內(nèi)包含如下三個(gè)版本的info()方法:?
public void info()?public void info(String str)?public void info(String str , Integer num)?這三個(gè)同名方法屬于重載,它們的方法名相同,但參數(shù)列表不同。在Java語(yǔ)言中要確定一個(gè)方法?
光有方法名是不行的,如果僅僅只指定info()方法,實(shí)際上可以是上面三個(gè)方法中的任意一一個(gè)。如果需要確定一個(gè)方法,則應(yīng)該由方法名和參數(shù)列表來(lái)確定,但參數(shù)名沒(méi)有任何實(shí)際意義,所以只能由參數(shù)類(lèi)型來(lái)確定。例如想指定第二個(gè)info()方法,則必須指定方法名為info,形參列表為String.class。因此在程序中獲取第二個(gè)info()方法使用如下代碼?
//前一個(gè)參數(shù)指定方法名,后面的個(gè)數(shù)可變的Class參數(shù)指定參數(shù)類(lèi)型列表?clazz. getMethod("info",String.class)?
如果需要獲取第三個(gè)info()方法,則使用如下代碼:?
//前一個(gè)參數(shù)指定方法名,后面的個(gè)數(shù)可變的Class參數(shù)指定形參類(lèi)型列表?clazz . getMethod("info", String.class, Integer.class)?
獲取構(gòu)造方法時(shí)無(wú)須傳入構(gòu)造方法名稱(chēng),因?yàn)橥粋€(gè)類(lèi)的所有構(gòu)造方法的名字都是相同的,所以要確定一個(gè)構(gòu)造方法只要指定參數(shù)列表即可。下面的【例19_06】展示了如何通過(guò)Class類(lèi)對(duì)象獲得類(lèi)的信息。?
【例19_06使用Class類(lèi)對(duì)象獲取類(lèi)的信息】
Exam19_06.java?
import java.lang.annotation.*;import java.lang.reflect.*;import java.util.Arrays;//定義可重復(fù)注解@Repeatable (Annos.class)@interface Anno { }@Retention (value=RetentionPolicy.RUNTIME)@interface Annos { Anno[] value();}//使用4個(gè)注解修飾該類(lèi)@ SuppressWarnings (value="unchecked")@ Deprecated//使用重復(fù)注解修飾該類(lèi)@Anno@Annoclass ClassTest{ //為該類(lèi)定義一個(gè)私有的構(gòu)造方法 private ClassTest (){ } //定義一個(gè)有參數(shù)的構(gòu)造方法 public ClassTest (String name){ System.out.println ("執(zhí)行有參數(shù)的構(gòu)造方法"); } //定義一個(gè)無(wú)參數(shù)的info方法 public void info(){ System.out.println( "執(zhí)行無(wú)參數(shù)的info方法"); } //定義一個(gè)有參數(shù)的info方法 public void info(String str){ System.out.println("執(zhí)行有參數(shù)的info方法" + ",其str參數(shù)值:"+ str); } //定義一個(gè)測(cè)試用的內(nèi)部類(lèi) class Inner{}}public class Exam19_06 { public static void main(String[] args) throws Exception{ //下面代碼可以獲取ClassTest對(duì)應(yīng)的Class Classclazz = ClassTest.class; //獲取該Class對(duì)象所對(duì)應(yīng)類(lèi)的全部構(gòu)造方法 Constructor[] ctors = clazz . getDeclaredConstructors (); System.out.println ("ClassTest的全部構(gòu)造方法如下: "); for (Constructor c : ctors) System.out.println(c); //獲取該Class對(duì)象所對(duì)應(yīng)類(lèi)的全部public構(gòu)造方法 Constructor[] publicCtors = clazz. getConstructors(); System.out.println ("ClassTest的全部public構(gòu)造方法如下: "); for(Constructor c : publicCtors){ System.out.println(c); } //獲取該Class對(duì)象所對(duì)應(yīng)類(lèi)的全部public方法 Method[] mtds = clazz .getMethods(); System.out.println ("ClassTest的全部public方法如下: "); for (Method md : mtds ){ System.out.println (md); } //獲取該Class對(duì)象所對(duì)應(yīng)類(lèi)的指定方法 System.out.println("ClassTest里帶一個(gè)字符串參數(shù)的info方法為:"+clazz.getMethod("info",String.class)); //獲取該Class對(duì)象所對(duì)應(yīng)類(lèi)的全部注解 Annotation[] anns = clazz.getAnnotations(); System.out.println ("ClassTest的全部Annotation如下: "); for(Annotation an : anns){ System.out.println(an); } System.out.println ("該Class元素上的@SuppressWarnings注解為: "+ Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class))); System.out.println("該Class元素上的@Anno注解為: " + Arrays.toString (clazz.getAnnotationsByType(Anno.class))); //獲取該Class對(duì)象所對(duì)應(yīng)類(lèi)的全部?jī)?nèi)部類(lèi) Class>[] inners = clazz.getDeclaredClasses(); System.out.println ("ClassTest的全部?jī)?nèi)部類(lèi)如下: "); for (Class c : inners){ System. out.println(c); } //使用Class. forName ()方法加載ClassTest的Inner內(nèi)部類(lèi) Class inClazz = Class.forName ("ClassTest$Inner"); //通過(guò)getDeclaringClass ()訪問(wèn)該類(lèi)所在的外部類(lèi) System.out.println ("inClazz對(duì)應(yīng)類(lèi)的外部類(lèi)為: " +inClazz.getDeclaringClass()); System.out.println ("ClassTest的包為: " + clazz.getPackage()); System.out.println ("ClassTest的父類(lèi)為: "+ clazz.getSuperclass()); }}
【例19_06】中Annotation表示注解,關(guān)于注解的知識(shí)將在19.4小節(jié)講解。【例19_06】的運(yùn)行結(jié)果如圖19-6所示。?
圖19-6【例19_06】運(yùn)行結(jié)果?
從圖19-6可以看出:getMethods()方法不僅僅能夠獲得這個(gè)類(lèi)中定義的方法,還可以獲得這個(gè)類(lèi)從父類(lèi)中繼承過(guò)來(lái)的方法。需要注意:雖然定義ClassTest類(lèi)時(shí)使用了@SuppressWarnings注解,但程序運(yùn)行時(shí)無(wú)法分析出該類(lèi)里包含的該注解,這是因?yàn)锧SuppressWarmings使用了@Retention(value=SOURCE)修飾,這表明@SuppesWarnings只能保存在源代碼級(jí)別上,而通過(guò)ClassTest.class獲取該類(lèi)的運(yùn)行時(shí)Class對(duì)象,所以程序無(wú)法訪問(wèn)到@SuppressWarnings注解。?
Java 8在java.lang.reflect包下新增了一個(gè)Executable抽象類(lèi),這個(gè)類(lèi)代表所有方法,它有兩個(gè)子類(lèi),分別是代表構(gòu)造方法的Constructor和代表普通方法的Method。Executable類(lèi)提供了大量方法來(lái)獲取修飾該方法的注解信息,還提供了isVarArgs()方法用于判斷該方法是否包含數(shù)量可變的形式參數(shù),以及通過(guò)getModifiers()方法來(lái)獲取該方法的修飾符。除此之外,Executable提供了如表19-3所示的兩個(gè)方法來(lái)獲取該方法的參數(shù)個(gè)數(shù)及參數(shù)名稱(chēng)。?
表19-3 Executable類(lèi)獲取參數(shù)個(gè)數(shù)及參數(shù)名稱(chēng)的方法?
方法? | 功能? |
int getParameterCount()? | 獲取該方法的參數(shù)個(gè)數(shù)? |
Parameter[] getParameters()? | 獲取該方法的所有參數(shù)? |
表19-3中,Parameter類(lèi)就表示方法的形式參數(shù)。Parameter也提供了大量方法來(lái)獲取聲明該參數(shù)的各項(xiàng)信息,如表19-4所示。?
表19-4 Parameter類(lèi)的常用方法?
方法? | 功能? |
getModifiers()? | 獲取修飾該參數(shù)的修飾符? |
String getName()? | 獲取參數(shù)名稱(chēng)? |
Type getParameterizedType()? | 獲取帶泛型的參數(shù)類(lèi)型? |
Class> getType()? | 獲取參數(shù)類(lèi)型? |
boolean isNamePresent()? | 判斷該方法返回該類(lèi)的class文件中是否包含了方法的參數(shù)名稱(chēng)信息。? |
boolean isVarArgs()? | 判斷該參數(shù)是否為個(gè)數(shù)可變的形參? |
需要指出:IDEA編譯Java 源文件時(shí),默認(rèn)生成的class文件并不包含方法的參數(shù)名稱(chēng)信息,因此調(diào)用isNamePresent()方法將會(huì)返回false,調(diào)用getName0方法也不能得到該參數(shù)的形參名。如果希望編譯Java源文件時(shí)可以保留參數(shù)名稱(chēng)信息,需要對(duì)IDEA進(jìn)行編譯參數(shù)的設(shè)置,具體步驟是:以打開(kāi)“File”菜單,選擇“Settings”菜單項(xiàng),在彈出的對(duì)話框中按照“Build,Execution,Deployment”->“Compiler”->“Java Compiler”的順序選擇選項(xiàng),并在“Additional command line parameters”后面填上“-parameters”,如圖19-7所示。?
圖19-7 設(shè)置編譯參數(shù)?
下面的【例19_07】展示了方法參數(shù)反射實(shí)現(xiàn)過(guò)程。?
【例19_07方法參數(shù)反射】
Exam19_07.java?
import java.util.*;import java.lang.reflect.*;class ParameterTest { public void rep(String str,Listlist){}}public class Exam19_07 { public static void main(String[] args)throws Exception { //獲取String的類(lèi) Class clazz = ParameterTest.class; //獲取String類(lèi)的帶兩個(gè)參數(shù)的replace()方法 Method rep = clazz.getMethod("rep", String.class, List.class); //獲取指定方法的參數(shù)個(gè)數(shù) System.out.println("rep()方法參數(shù)個(gè)數(shù): " + rep.getParameterCount ()); //獲取replace的所有參數(shù)信息 Parameter[] parameters = rep.getParameters(); int index = 1; //遍歷所有參數(shù) for (Parameter p : parameters) { if (p.isNamePresent()) { System.out.println("---第" + index++ + "個(gè)參數(shù)信息---"); System.out.println("參數(shù)名: " + p.getName()); System.out.println("形參類(lèi)型: " + p.getType()); System.out.println("泛型類(lèi)型:" + p.getParameterizedType()); } } }}
如果希望能夠正確運(yùn)行【例19_07】,就必須對(duì)IDEA設(shè)置編譯參數(shù),設(shè)置完成后還需重新編譯項(xiàng)目,具體做法是:打開(kāi)“菜單”,單擊“Rebuild Project”菜單項(xiàng)。完成這一步操作后,運(yùn)行【例19_07】的結(jié)果如圖19-8所示。?
圖19-8【例19_07】運(yùn)行結(jié)果?
一個(gè)Class類(lèi)對(duì)象就代表一個(gè)類(lèi),程序員不僅僅能夠通過(guò)這個(gè)Class類(lèi)對(duì)象獲得對(duì)應(yīng)類(lèi)的信息,還能通過(guò)這個(gè)Class類(lèi)對(duì)象創(chuàng)建出對(duì)應(yīng)類(lèi)的對(duì)象。這句話聽(tīng)起來(lái)有點(diǎn)繞口,它的意思是:如果一個(gè)Class類(lèi)對(duì)象clazz代表A類(lèi),那么程序員能夠通過(guò)clazz來(lái)創(chuàng)建出一個(gè)A類(lèi)對(duì)象。19.3.3小節(jié)曾介紹過(guò):Constructor類(lèi)代表類(lèi)的構(gòu)造方法,而通過(guò)反射的方式來(lái)生成對(duì)象需要先使用Class對(duì)象獲取指定的Constructor對(duì)象,再調(diào)用Constructor對(duì)象的newInstance()方法來(lái)創(chuàng)建該Class對(duì)象對(duì)應(yīng)類(lèi)的對(duì)象。下面的【例19_08】展示了使用反射方式創(chuàng)建對(duì)象的過(guò)程。?
【例19_08 用反射技術(shù)創(chuàng)建對(duì)象1】
Exam19_08.java?
import java.util.*;class ObjectPoolFactory{ String[] classNames;//存放類(lèi)名稱(chēng)的字符串?dāng)?shù)組 //定義一個(gè)Map類(lèi)集合,Map的key是對(duì)象名,value是實(shí)際對(duì)象 MapobjectPool = new HashMap () ; //構(gòu)造方法 public ObjectPoolFactory(String[] classNames){ this.classNames = classNames; } //createObject()方法根據(jù)String型參數(shù)clazzName生成Java對(duì)象 private Object createObject (String clazzName) throws Exception { //根據(jù)字符串來(lái)獲取對(duì)應(yīng)的Class對(duì)象 Class> clazz = Class.forName (clazzName) ; //使用clazz對(duì)應(yīng)類(lèi)的默認(rèn)構(gòu)造器創(chuàng)建實(shí)例 return clazz. getConstructor().newInstance() ; } //initPool()方法根據(jù)一組類(lèi)的名稱(chēng)生成相應(yīng)的對(duì)象并存入objectPool public Map initPool() throws Exception { if(classNames!=null){ for (int i=0;i< classNames.length;i++){ Object o = createObject(classNames[i]);//根據(jù)類(lèi)名創(chuàng)建對(duì)象 objectPool.put(classNames[i],o);//把類(lèi)名和創(chuàng)建出的對(duì)象存入objectPool } } return objectPool; }}public class Exam19_08 { public static void main(String[] args) throws Exception { String[] className = {"java.text.SimpleDateFormat","java.util.Date"}; ObjectPoolFactory opf = new ObjectPoolFactory(className); Map objectPool = opf.initPool(); for (int i=0;i< objectPool.size();i++){//循環(huán)取出objectPool中的對(duì)象 System.out.println(objectPool.get(className[i])); } }}
【例19_08】中定義了一個(gè)ObjectPoolFactory類(lèi),這個(gè)類(lèi)的createObject()方法可以根據(jù)參數(shù)指定的類(lèi)名稱(chēng)通過(guò)反射方式創(chuàng)建出該類(lèi)的對(duì)象,而initPool()根據(jù)一組類(lèi)的名稱(chēng)生成一組對(duì)象并存入Map集合中。main()方法創(chuàng)建了ObjectPoolFactory類(lèi)對(duì)象并調(diào)用其initPool()方法生成一組對(duì)象并循環(huán)打印這些對(duì)象。【例19_08】的運(yùn)行結(jié)果如圖19-9所示。?
圖19-9【例19_08】運(yùn)行結(jié)果?
需要注意:利用反射方式生成的第二個(gè)對(duì)象是Date類(lèi)對(duì)象,而Date類(lèi)無(wú)參數(shù)構(gòu)造方法所創(chuàng)建的對(duì)象表示當(dāng)前時(shí)間,因此讀者的運(yùn)行結(jié)果中Date類(lèi)對(duì)象的打印結(jié)果可能與圖19-9不同。?
【例19_08】中調(diào)用的是類(lèi)的無(wú)參數(shù)構(gòu)造方法創(chuàng)建的對(duì)象,如果希望調(diào)用有參數(shù)的構(gòu)造方法創(chuàng)建對(duì)象,則需要調(diào)用有參數(shù)的getConstructor()方法來(lái)獲得Constructor對(duì)象。有參數(shù)的getConstructor()方法的參數(shù)類(lèi)型是Class,這個(gè)參數(shù)表示對(duì)應(yīng)類(lèi)構(gòu)造方法的參數(shù),而獲得的Constructor對(duì)象則是對(duì)應(yīng)類(lèi)的帶參數(shù)構(gòu)造方法。例如A類(lèi)有一個(gè)帶有參數(shù)的構(gòu)造方法,這個(gè)構(gòu)造方法的參數(shù)是String類(lèi)型,那么通過(guò)Class類(lèi)的有參數(shù)的getConstructor()就能獲得這個(gè)有參數(shù)的構(gòu)造方法,在調(diào)用getConstructor()時(shí)需要以String.class作為getConstructor()方法的參數(shù)。當(dāng)獲得了表示有參數(shù)構(gòu)造方法的Constructor對(duì)象后,再調(diào)用其newInstance()方法就能以A類(lèi)有參數(shù)的構(gòu)造方法創(chuàng)建出一個(gè)A類(lèi)對(duì)象,并且在調(diào)用newInstance()方法時(shí)需要向它傳遞相應(yīng)的構(gòu)造方法參數(shù)。下面的【例19_09】展示了如何用反射的方式調(diào)用到j(luò)ava.util.Date類(lèi)的有l(wèi)ong型參數(shù)的構(gòu)造方法。?
【例19_09 用反射技術(shù)創(chuàng)建對(duì)象2】?
Exam19_09.java?
import java.lang.reflect.*;public class Exam19_09 { public static void main(String[] args) throws Exception { //獲取到表示java.util.Date類(lèi)的Class對(duì)象 Class clazz = java.util.Date.class; //獲取到j(luò)ava.util.Date類(lèi)的以long為參數(shù)類(lèi)型的構(gòu)造方法 Constructor constructor = clazz.getConstructor(long.class);//① //以long型參數(shù)的構(gòu)造方法創(chuàng)建對(duì)象 Object o = constructor.newInstance(9876543210L); System.out.println(o); }}
【例19_09】中,語(yǔ)句①調(diào)用了有參數(shù)的getConstructor()方法來(lái)獲得Date類(lèi)有參數(shù)的構(gòu)造方法,由于為getConstructor()方法傳遞的參數(shù)是“l(fā)ong.class”,因此獲得的Constructor對(duì)象表示的是“Date(long date)”這個(gè)構(gòu)造方法。同樣,在調(diào)用Constructor對(duì)象的newInstance()方法時(shí),也需要傳遞一個(gè)long型的參數(shù),這相當(dāng)于為“Date(long date)”這個(gè)構(gòu)造方法傳遞了構(gòu)造參數(shù)。此外還需強(qiáng)調(diào),Java語(yǔ)言中,基礎(chǔ)數(shù)據(jù)類(lèi)型也可以通過(guò)“.class”來(lái)獲得其對(duì)應(yīng)類(lèi)型的Class對(duì)象,并且“l(fā)ong.class”與“Long.class”所得到的Class對(duì)象并不是同一個(gè)對(duì)象。【例19_09】的運(yùn)行結(jié)果如圖19-10所示。?
圖19-10【例19_09】運(yùn)行結(jié)果?
通過(guò)反射的方式不僅能創(chuàng)建出一個(gè)類(lèi)的對(duì)象,還能調(diào)用到一個(gè)類(lèi)中的方法。如果想通過(guò)反射方式調(diào)用一個(gè)類(lèi)的方法,先要調(diào)用Class的getMethods()方法或getMethod()方法來(lái)獲取全部方法或指定方法,這兩個(gè)方法的返回值是Method數(shù)組和Method對(duì)象。前文介紹過(guò):每個(gè)Method對(duì)象對(duì)應(yīng)一個(gè)方法,獲得Method對(duì)象后,程序就可通過(guò)該Method來(lái)調(diào)用它對(duì)應(yīng)的方法。調(diào)用的具體方式是:在程序中執(zhí)行Method類(lèi)的invoke()方法,需要指出:invoke()方法在執(zhí)行時(shí)需要一個(gè)對(duì)象作為參數(shù),執(zhí)行invoke()方法就相當(dāng)于調(diào)用了這個(gè)對(duì)象相應(yīng)的方法。下面的【例19_10】展示了使用反射方式調(diào)用類(lèi)方法的過(guò)程。?
【例19_10用反射技術(shù)調(diào)用類(lèi)中的方法】
Exam19_10.java?
import java.lang.reflect.*;public class Exam19_10 { public static void main(String[] args) throws Exception { Class clazz = System.out.getClass(); //獲取表示println()方法的Method對(duì)象 Method method = clazz.getMethod("println",String.class); //通過(guò)invoke()方法執(zhí)行System.out的println()方法 method.invoke(System.out,"這是通過(guò)反射方式打印的字符串"); }}
【例19_10】的語(yǔ)句①通過(guò)Method對(duì)象的invoke()方法調(diào)用了System.out的println()方法,而“這是通過(guò)反射方式打印的字符串”則是傳遞給println()方法的參數(shù)。【例19_10】的運(yùn)行結(jié)果如圖19-11所示。?
圖19-11【例19_10】運(yùn)行結(jié)果?
通過(guò)反射的方式還可以訪問(wèn)類(lèi)的屬性。程序員只需要通過(guò)Class類(lèi)的getFields()或getField()方法可以獲取該類(lèi)所包括的全部屬性或指定的屬性。Java語(yǔ)言中,屬性由Field類(lèi)表示,F(xiàn)ield類(lèi)提供了一組getXxx()方法來(lái)獲取對(duì)象的某個(gè)屬性的值,而此處的Xxx對(duì)應(yīng)8種基礎(chǔ)數(shù)據(jù)類(lèi)型,如果該屬性的類(lèi)型是引用類(lèi)型,則取消get后面的Xxx。此外,F(xiàn)ield類(lèi)還提供了一組setXxx()方法為對(duì)象的屬性設(shè)置成值,此處的Xxx也對(duì)應(yīng)8種基礎(chǔ)數(shù)據(jù)類(lèi)型,同樣如果該屬性的類(lèi)型是引用類(lèi)型則取消set后面的Xxx。下面的【例19_11】演示了如何使用反射技術(shù)操作屬性。?
【例19_11用反射技術(shù)訪問(wèn)屬性】
Exam19_11.java?
import java.lang.reflect.*;class Person{ private String name; private int age; public String toString(){ return " Person[name:" + name +"age:"+age+"]"; }}public class Exam19_11 { public static void main(String[] args) throws Exception{ //創(chuàng)建-一個(gè)Person對(duì)象 Person p = new Person() ; //獲取Person類(lèi)對(duì)應(yīng)的Class對(duì)象 ClasspersonClazz = Person.class; //獲取Person的名為name的屬性 //使用getDeclaredField ()方法表明可獲取各種訪問(wèn)控制符的屬性 Field nameField = personClazz .getDeclaredField ("name") ; //設(shè)置通過(guò)反射訪問(wèn)該屬性時(shí)取消訪問(wèn)權(quán)限檢查 nameField. setAccessible (true) ; //調(diào)用set()方法為P對(duì)象的name屬性設(shè)置值 nameField.set(p,"張三") ; //獲取Person類(lèi)名為age的屬性 Field ageField = personClazz . getDeclaredField("age") ; //設(shè)置通過(guò)反射訪問(wèn)該屬性時(shí)取消訪問(wèn)權(quán)限檢查 ageField.setAccessible (true) ; //調(diào)用setInt()方法為p對(duì)象的age屬性設(shè)置值 ageField. setInt(p,20) ; System. out.println(p) ; }}
【例19_11】中定義了一個(gè)Person類(lèi),該類(lèi)里包含兩個(gè)private屬性:name和age,在通常情況下,這兩個(gè)屬性只能在Person 類(lèi)里訪問(wèn)。但main()方法中通過(guò)反射修改了Person對(duì)象的name、age兩個(gè)屬性的值。語(yǔ)句①使用getDeclaredField()方法獲取了名為name 的屬性,注意此處不是使用getField()方法,因?yàn)間etField()方法只能獲取public的屬性,而getDeclaredField()方法則可以獲取所有的屬性。語(yǔ)句②是設(shè)置訪問(wèn)該屬性時(shí)不受訪問(wèn)權(quán)限的控制,語(yǔ)句③修改了Person對(duì)象的name屬性的值。修改Person對(duì)象的age屬性值的方式與修改name屬性的方式完全相同。【例19_11】的運(yùn)行結(jié)果如圖19-12所示。?
圖19-12【例19_11】運(yùn)行結(jié)果?
在Java語(yǔ)言中,數(shù)組也可以用反射的方式生成并進(jìn)行操作。在java.lang.reflect包下提供了一個(gè)Array類(lèi),Array對(duì)象可以代表所有的數(shù)組。程序員可以通過(guò)使用Array類(lèi)動(dòng)態(tài)地創(chuàng)建數(shù)組,操作數(shù)組元素等。Array類(lèi)所提供的操作數(shù)組常用方法如表19-5所示。?
表19-5 Array類(lèi)操作數(shù)組的方法?
方法? | 功能? |
static Object newInstance(Class> componentType, int... length)? | 創(chuàng)建一個(gè)具有指定的元素類(lèi)型、指定維度的新數(shù)組? |
static xxx getXxx(Object array, int index)? | 返回array 數(shù)組中第index個(gè)元素。其中xxx是各種基礎(chǔ)數(shù)據(jù)類(lèi)型,如果數(shù)組元素是引用類(lèi)型,則該方法變?yōu)間et(Object array, int index)? |
static void setXxx(Object array, int index, xxx val)? | 將array數(shù)組中第index 個(gè)元素的值設(shè)為val,其中xxx是各種基礎(chǔ)數(shù)據(jù)類(lèi)型,如果數(shù)組元素是引用類(lèi)型,則該方法變成set(Object array, intindex, Object val)? |
下面的【例19_12】展示了使用Array類(lèi)創(chuàng)建并操作數(shù)組的過(guò)程。?
【例19_12 Array類(lèi)操作數(shù)組1】
Exam19_12.java?
import java.lang.reflect.Array;public class Exam19_12 { public static void main(String[] args) { //創(chuàng)建 一個(gè)元素類(lèi)型為String, 長(zhǎng)度為10的數(shù)組 Object arr = Array.newInstance (String. class, 10); //依次為arr數(shù)組中index為5、6的元素賦值 Array.set(arr,5,"《Java語(yǔ)言入門(mén)》"); Array.set(arr,6,"《數(shù)據(jù)庫(kù)基礎(chǔ)》"); //依次取出arr數(shù)組中index為5、6的元素的值 Object book1 = Array .get (arr,5); Object book2 = Array.get (arr,6); //輸出arr數(shù)組中index為5、6的元素 System. out .println (book1) ; System. out .println (book2) ; }}
【例19_12】的運(yùn)行結(jié)果如圖19-13所示。?
圖19-13【例19_12】運(yùn)行結(jié)果?
【例19_12】創(chuàng)建并操作了一個(gè)一維數(shù)組,而Array實(shí)際上也能創(chuàng)建并操作多維數(shù)組,下面的【例19_13】展示了如何使用Array類(lèi)創(chuàng)建并操作三維數(shù)組。在3.2小節(jié)曾經(jīng)介紹過(guò):多維數(shù)組實(shí)際上也是一維數(shù)組,例如三維數(shù)組可以看作是由多個(gè)二維數(shù)組組成的一維數(shù)組,因此可以用操作一維數(shù)組的方式來(lái)操作二維、三維或更高維度的數(shù)組。?
【例19_13 Array類(lèi)操作數(shù)組2】?
Exam19_13.java?
import java.lang.reflect.Array;public class Exam19_13 { public static void main(String[] args) { //創(chuàng)建一個(gè)String型三維數(shù)組 Object arr = Array.newInstance (String.class, 3,4,10) ; //獲取arr數(shù)組中index為2的元素,該元素應(yīng)該是二維數(shù)組 Object arrObj = Array .get(arr, 2); //使用Array為二維數(shù)組的數(shù)組元素賦值,二維數(shù)組的數(shù)組元素是一維數(shù)組 //所以傳入Array的set()方法的第三個(gè)參數(shù)是一 個(gè)一維數(shù)組 Array.set (arrObj,2,new String []{"《Java語(yǔ)言入門(mén)》", "《數(shù)據(jù)庫(kù)基礎(chǔ)》"}) ; //獲取arrObj數(shù)組中index為3的元素,該元素應(yīng)該是一維數(shù)組 Object anArr = Array.get (arrObj, 3) ; Array. set (anArr,8,"《Android開(kāi)發(fā)教程》"); //將arr強(qiáng)制類(lèi)型轉(zhuǎn)換為三維數(shù)組 String[][] [] cast = (String[][][])arr; //獲取cast三維數(shù)組中指定元素的值 System.out.println(cast[2][3][8]) ; System.out.println(cast[2][2][0]) ; System.out.println(cast[2][2][1]) ; }}
【例19_13】創(chuàng)建并操作了一個(gè)三維數(shù)組,操作的每一步都添加了詳細(xì)的注釋?zhuān)x者可以根據(jù)注釋理解每一步的操作意義。【例19_13】的運(yùn)行結(jié)果如圖19-14所示。?
圖19-14【例19_13】運(yùn)行結(jié)果?
本文字版教程還配有更詳細(xì)的視頻講解,小伙伴們可以??點(diǎn)擊這里??觀看。
標(biāo)簽: 構(gòu)造方法 一維數(shù)組 參數(shù)名稱(chēng)