天天頭條:第十九章《類(lèi)的加載與反射》第3節(jié):反射

2023-01-03 15:12:42 來(lái)源:51CTO博客

?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ī)制的原理和作用。


(資料圖片僅供參考)

19.3.1獲得Class類(lèi)對(duì)象

每個(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í)信息了。?

19.3.2從Class類(lèi)對(duì)象中獲取類(lèi)的信息

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 getConstructor(Class... parameterTypes)?

返回此Class對(duì)象對(duì)應(yīng)類(lèi)的、帶指定形參列表的public構(gòu)造方法。?

Constructor[] getConstructors()?

返回此Class對(duì)象對(duì)應(yīng)類(lèi)的所有public構(gòu)造方法?

Constructor getDeclaredConstructor(Class ... parameterTypes)?

返回此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)?

A getAnnotation(Class annotationClass);?

嘗試獲取該Class對(duì)象對(duì)應(yīng)類(lèi)上存在的、指定類(lèi)型的注解,如果該類(lèi)型的注解不存在,則返回null?

A getDeclaredAnnotation(Class annotationClass)?

該方法嘗試獲取直接修飾該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)的所有注解?

A[] getAnnotationsByType(Class annotationClass)?

該方法的功能與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 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 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        Class clazz = 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注解。?

19.3.3方法參數(shù)反射

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,List list){}}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é)果?

19.3.4利用反射生成并操作對(duì)象

一個(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ì)象    Map objectPool = 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ì)象        Class personClazz = 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é)果?

19.3.5利用反射操作數(shù)組

在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)

上一篇:【全球聚看點(diǎn)】編程初學(xué)者:關(guān)于我學(xué)習(xí)編程這件事
下一篇:Lnmp搭建zabbix運(yùn)維監(jiān)控系統(tǒng)