解析Java中的Field類和Method類
Field類
Field類中定義了一些方法,可以用來(lái)查詢字段的類型以及設(shè)置或讀取字段的值。將這些方法與繼承而來(lái)的member方法結(jié)合在一起.就可以使我們能夠找出有關(guān)字段聲明的全部信息,并且能夠操縱某個(gè)特定對(duì)象或類的字段。
getGenericType方法返回表示字段的聲明類型的Type實(shí)例。對(duì)于像String或int這樣的平凡類型,該方法將返回與其相關(guān)聯(lián)的Class對(duì)象,例如String.class和int.classo對(duì)于像List < Stri ng>這樣的參數(shù)化類型,該方法將返回Parameterizedrype的實(shí)例,例如,對(duì)像T這樣的類型,該方法將返回Typevariable實(shí)例。
遺留下來(lái)的getType方法將返回字段的類型的Class對(duì)象。對(duì)于平凡類型,該方法的行為與getGenericType方法的相同。如果字段的聲明類型是參數(shù)化類型,那么getType方法將返回參數(shù)化類型的擦除所對(duì)應(yīng)的Class對(duì)象,即原始類型的Class對(duì)象。例如,對(duì)于聲明為L(zhǎng)ist < Stri ng>的對(duì)象,getType將返回Li St. class的。如果字段的聲明類型是類型變量,那么getType方法將返回類型變量的擦除所對(duì)應(yīng)的class對(duì)象。例如,假設(shè)有一個(gè)類FOO<丁>,對(duì)于其聲明為T類型的字段,get丁ype將返回object.
class對(duì)象。如果FOO被聲明為FOo<下extends Number >,那么get下ype將返回 Number.class.
我們可以使用isEnumConstant方法查詢一個(gè)字段是否是枚舉常量,也可以使用get和set方法來(lái)獲取和設(shè)置字段的值。這些接受object引元并返回Obj ect值的方法都有一種通用形式,以及一些可以直接處理基本類型的更加特化的形式。所有這些方法都要接受一個(gè)引元,用來(lái)指定所要操作的對(duì)象。對(duì)于靜態(tài)字段,將忽略這個(gè)對(duì)象引元,所以此時(shí)也可以將其設(shè)
置為null。下面的方法將打印一個(gè)對(duì)象的short型字段的值:
public static void printShortField(Object o, String name)
throws NoSuchFieldException,IllegalAccessException
{
Field field=o.getClass().getField(name);
short value=(Short) field.get(o);
System.out.println(value);
get方法的返回值可以是這個(gè)字段所引用的任何對(duì)象,如果該字段是基本類型,那么該方法將返回恰當(dāng)類型的包裝器類對(duì)象。對(duì)于我們的”hort型字段,get方法將返回包含該字段值的short類型的對(duì)象,而在將它賦值給本地變量value時(shí),該對(duì)象值會(huì)自動(dòng)進(jìn)行拆箱轉(zhuǎn)換。
set方法的使用也是類似的。將short型字段設(shè)置為所提供的值的方法看起來(lái)可能像下面這樣:
public static voi setShortField(Object o,String name,short nv) throws NoSuchFieldException,IllegalAccessException Field field=0.getClass().getField(name); field .set(o .nv);
雖然set接受的是Object類型的參數(shù),但是我們可以直接傳遞一個(gè)short型的值,并用包裝轉(zhuǎn)換將其包裝為short類型的對(duì)象。
在上面的方法中,如果指定對(duì)象的域是不可訪問(wèn)的,并且這種訪問(wèn)權(quán)限控制是強(qiáng)制執(zhí)行的,那么就會(huì)拋出IllegalACcessException異常;如果傳遞的對(duì)象與該域的類型不同,就會(huì)拋出illegalArgumentException異常;如果該域是非靜態(tài)的且傳遞的對(duì)象引用是null,就會(huì)拋出NullPointerException異常;訪問(wèn)靜態(tài)域可能會(huì)要求對(duì)類進(jìn)行初始化,所以該方法也會(huì)拋出ExceptionInInitializerError異常。
Field類還有特定的用來(lái)獲取和設(shè)置基本類型的方法,例如,我們可以在Field對(duì)象上調(diào)用getPrimitive7ype和set Primitive7ype,其中Primitive7ype是(首字母大寫的)基本類型名。get方法可用于下面的語(yǔ)句:
short value=field.getshort(o);
而set方法可用于下面的語(yǔ)句:
field.setshort(o, nv);
用以上兩種方式聲明的語(yǔ)句中可以避免使用包裝器類對(duì)象。
Field類實(shí)現(xiàn)了AnnotatedElement接口,所以我們也可以像16.2節(jié)那樣查詢應(yīng)用于域
上的注解。
憑借上面介紹的方法,我們可以將Field對(duì)象用作操縱任意值的一種方式,但是我們應(yīng)該盡量避免使用它。因?yàn)镴ava語(yǔ)言會(huì)在程序的編譯期盡可能多地捕獲編程錯(cuò)誤,所以在我們編寫代碼時(shí),使用的諸如「ield對(duì)象這樣的間接方法越少,那么在將它們編譯成代碼之前,就可以防止更多的錯(cuò)誤。而且,我們可以看到,在前面的代碼中,要想知道到底會(huì)發(fā)生什么,與在普通的語(yǔ)法中直接使用域名的情況相比,我們花費(fèi)在閱讀代碼上的精力顯然大了許多。
Final字段
在通常情況下,對(duì)聲明為final的字段進(jìn)行設(shè)置將會(huì)導(dǎo)致拋出IllegalACcessException
異常,這是我們所能預(yù)期的,因?yàn)閒inal字段的值是永遠(yuǎn)不會(huì)改變的。但是有些特殊情況—例如在定制的反序列化(見20.8.4節(jié))中,改變final字段的值就是有意義的,我們只有在實(shí)例字段上才能通過(guò)反射實(shí)現(xiàn)這一點(diǎn),并且前提是在該Field對(duì)象上已經(jīng)調(diào)用過(guò)了setAccessible(true)。注意,可以成功調(diào)用setAccessible(true)是不夠的,必須確實(shí)調(diào)用過(guò)它。
這種能力是為高度特化的上下文提供的,并非用于通用目的,我們介紹它僅僅是為了保持內(nèi)容的完整性。如果脫離了特定的上下文,例如定制的反序列化,那么改變final字段的值可能會(huì)導(dǎo)致意外的甚至是災(zāi)難性的后果。在這些上下文之外,不能保證對(duì)final字段的改變是可見的。即便是在這樣的上下文中,在使用這項(xiàng)技術(shù)編碼時(shí)也必須保證安全機(jī)制不會(huì)阻礙代碼的執(zhí)行。改變值為常量變量(見2.2.3節(jié))的final字段將會(huì)導(dǎo)致此改變不可見,除非通過(guò)使用反射來(lái)實(shí)現(xiàn)這種修改。
Method類
method類和它從member類繼承而來(lái)的方法使得我們可以獲得方法聲明的完整信息:
" public Type getGenericReturnTypeO:該方法返回的是目標(biāo)方法的返回類型的Type對(duì)象。如果目標(biāo)方法被聲明為返回void,則該方法返回void.classo
" public Type[] getGenericParameterTypes():該方法返回目標(biāo)方法所有參數(shù)類型的Type對(duì)象數(shù)組,這些Type對(duì)象將按照參數(shù)的聲明順序存儲(chǔ)于在數(shù)組中。如果目標(biāo)方法沒(méi)有任何參數(shù),則該方法返回一個(gè)空數(shù)組。
.publ i c Type [] getGeneri caccepti onTypes Q:該方法返回在throws子句中列出的所有異常類型的Type對(duì)象數(shù)組,這些Type對(duì)象將按照異常的聲明順序存儲(chǔ)在數(shù)組中。
如果目標(biāo)方法沒(méi)有聲明任何異常,則該方法返回一個(gè)空數(shù)組。
Java還提供了getReturnType,getParameterTypes和getExceptionTypes方法,用來(lái)返回Cl as”對(duì)象而不是Type對(duì)象。就像在使用Field.getType時(shí),參數(shù)化類型和類型變量是由它們的擦除所對(duì)應(yīng)的Class對(duì)象表示的。
method類實(shí)現(xiàn)了AnnotatedElement,并且我們可以像16.2節(jié)所討論的那樣去查詢應(yīng)用于方法上的注解。另外,Method類還提供了getParameterAnnotations,用來(lái)提供對(duì)應(yīng)用于方法參數(shù)上的注解進(jìn)行訪問(wèn)。getParameterAnnotations方法可以返回Annotation數(shù)組,其中最外層數(shù)組的每一個(gè)元素都與方法的參數(shù)相對(duì)應(yīng);如果某個(gè)參數(shù)沒(méi)有任何注解,則該方法為這個(gè)參數(shù)返回一個(gè)長(zhǎng)度為0的Annotation數(shù)組。如果method對(duì)象所表示的方法自身就是一個(gè)注解元素,那么getDefaultvalue方法將返回一個(gè)表示該元素默認(rèn)值的Object對(duì)象;如果method對(duì)象本身不是注解元素或者它沒(méi)有默認(rèn)值,則該方法將返回null.Method類也實(shí)現(xiàn)了GenericDeclaration,因此定義了getTypeParameters方法,該方法將返回一個(gè)Typevariable對(duì)象數(shù)組。如果給定的method對(duì)象表示的不是泛型方法,該方法將返回一個(gè)空數(shù)組。
我們可以使用isvarArgs方法來(lái)檢查某個(gè)method對(duì)象是否是一個(gè)可變引元方法,而i sBridge方法可以用來(lái)檢查它是否是一個(gè)橋接方法
Method對(duì)象最有趣的用法就是反射地調(diào)用它自己:
.public object invoke(object onThis,object…args)throws IllegalACcessException,IllegalArgumentException,工nvocation下argetException:該方法在onThis對(duì)象上調(diào)用method對(duì)象定義的方法,并用args的值來(lái)設(shè)置被調(diào)用方法的參數(shù)。對(duì)于非靜態(tài)方法,onThis的實(shí)際類型就確定了將要調(diào)用方法的哪種實(shí)現(xiàn),而對(duì)于靜態(tài)方法,onThis會(huì)被忽略,并且通常會(huì)設(shè)置為null. args值的數(shù)量必須和被調(diào)用方法的實(shí)際參數(shù)數(shù)量相同,并且這些值的類型必須全部都可賦值給那些被調(diào)用方法的參數(shù);否則,我們將會(huì)得到工llegalArgumentException異常。請(qǐng)注意,可變引元方法的最后一個(gè)參數(shù)是一個(gè)數(shù)組,所以我們必須用實(shí)際想要傳遞的“可變”引元來(lái)填充該數(shù)組。如果我們想調(diào)用我們沒(méi)有訪問(wèn)權(quán)限的方法,該方法就會(huì)拋出IllegalACcessException異常。如果被調(diào)用方法不是on下his對(duì)象的方法,該方法會(huì)拋出工llegalArgumentExcepti on異常。如果onThis為null并且是非靜態(tài)的,該方法就會(huì)拋出NO 1PointerException異常。如果這個(gè) method對(duì)象表示的是靜態(tài)方法,并且聲明這個(gè)靜態(tài)方法的類仍處于待初始化狀態(tài),該方法就會(huì)拋出ExceptionIn工nitializerError異常。如果被調(diào)用法出異冪,談萬(wàn)法就會(huì)拋出InvocationTargetException異常。
當(dāng)我們使用invoke方法時(shí),可以直接傳遞基本類型,也可以使用合適的包裝器類。包裝器類表示的類型必須可賦值給方法所聲明的參數(shù)類型。我們可以使用Long,Float或Double來(lái)包裝double類型的引元,但是不能用Double來(lái)包裝long或float類型的引元,因?yàn)閐ouble不是可賦值給long或們oat的。對(duì)invoke方法返回的object的處理方法和Field.get一樣,都是返回對(duì)應(yīng)于它們的包裝器類的基本類型。如果方法聲明為void, invoke方法將返回null,
簡(jiǎn)單地說(shuō),就是我們?cè)谟胕nvoke來(lái)調(diào)用方法時(shí),只能使用在Java語(yǔ)言中合法的與其參數(shù)
具有相同類型和值的引元。例如,下面的調(diào)用
return str.indexof(".”,8);
可以用反射寫成如下形式:
Throwable fa們ure;
try{
Method indexM=String.class.
getMethod("index0f",String.class,int.class);
return (Integer) indexM.invoke(str,”,”,8);
}catch (NoSuchMethodException e){
failure=e;
}catch (InvocationTargetException e){
fa們ure=e .getCause();
}catch (IllegalAccessException e){
failure=e;
}
throw fa們ure;
雖然編譯器對(duì)于直接調(diào)用所做的安全性檢查,在使用反射的情況下,只能在運(yùn)行時(shí)使用invoke時(shí)進(jìn)行,但是基于反射的代碼確實(shí)擁有與直接調(diào)用的代碼在語(yǔ)義上等效的安全性檢查。訪問(wèn)權(quán)限檢查可能會(huì)以略為不同的方式執(zhí)行—安全管理器可能會(huì)拒絕訪問(wèn)我們的包中的某個(gè)方法,即使我們可以直接調(diào)用該方法。
當(dāng)我們可以使用這種形式的調(diào)用時(shí),我們有充分的理由去避免它。但是如果我們?cè)诰帉懻{(diào)試器或其他需要將用戶輸入解釋為對(duì)對(duì)象操作的泛型應(yīng)用時(shí)使用invoke或get/set方法,就會(huì)顯得很合理。method對(duì)象在某種程度上可以當(dāng)作類似其他語(yǔ)言中的方法指針來(lái)使用,但是我們有更好的工具,尤其是接口、抽象類和嵌套類,可以用來(lái)處理那些通常在其他語(yǔ)言中用方法指針解決的問(wèn)題。
- 解決啟動(dòng)Azkaban報(bào)錯(cuò)問(wèn)題:java.lang.NoSuchMethodError: com.google.common.collect.ImmutableMap.toImmutableMap
- 詳解java代碼中init method和destroy method的三種使用方式
- Java上傳文件錯(cuò)誤java.lang.NoSuchMethodException的解決辦法
- java中Class.getMethods()和Class.getDeclaredMethods()方法的區(qū)別
- 詳解Java中Method的Invoke方法
- java中排序報(bào):Comparison method violates its general contract異常的解決
- 解決 java.lang.NoSuchMethodError的錯(cuò)誤
- java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.setXmlVersion問(wèn)題解決方法
- Java反射機(jī)制及Method.invoke詳解
- Java Method類及invoke方法原理解析
相關(guān)文章
SpringMVC核心DispatcherServlet處理流程分步詳解
這篇文章主要介紹了SpringMVC核心之中央調(diào)度器DispatcherServlet的相關(guān)知識(shí),包括SpringMVC請(qǐng)求處理過(guò)程及SrpingMVC容器和spring IOC容器關(guān)系,需要的朋友可以參考下2023-04-04
Java修改maven的默認(rèn)jdk版本為1.7的方法
這篇文章主要介紹了Java修改maven的默認(rèn)jdk版本為1.7的方法,需要的朋友可以參考下2018-02-02
springCloud gateWay 統(tǒng)一鑒權(quán)的實(shí)現(xiàn)代碼
這篇文章主要介紹了springCloud gateWay 統(tǒng)一鑒權(quán)的實(shí)現(xiàn)代碼,統(tǒng)一鑒權(quán)包括鑒權(quán)邏輯和代碼實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02
SpringBoot如何使用內(nèi)嵌Tomcat問(wèn)題
這篇文章主要介紹了SpringBoot如何使用內(nèi)嵌Tomcat問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
Java Config下的Spring Test幾種方式實(shí)例詳解
這篇文章主要介紹了Java Config下的Spring Test方式實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05
Java @Async注解導(dǎo)致spring啟動(dòng)失敗解決方案詳解
這篇文章主要介紹了Java @Async注解導(dǎo)致spring啟動(dòng)失敗解決方案詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
mybatis升級(jí)mybatis-plus時(shí)踩到的一些坑
這篇文章主要給大家介紹了關(guān)于mybatis升級(jí)mybatis-plus時(shí)踩到的一些坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
IDEA項(xiàng)目的依賴(pom.xml文件)導(dǎo)入問(wèn)題及解決
這篇文章主要介紹了IDEA項(xiàng)目的依賴(pom.xml文件)導(dǎo)入問(wèn)題及解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08

