解析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ì)改變的。但是有些特殊情況—例如在定制的反序列化(見(jiàn)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字段的改變是可見(jiàn)的。即便是在這樣的上下文中,在使用這項(xiàng)技術(shù)編碼時(shí)也必須保證安全機(jī)制不會(huì)阻礙代碼的執(zhí)行。改變值為常量變量(見(jiàn)2.2.3節(jié))的final字段將會(huì)導(dǎo)致此改變不可見(jiàn),除非通過(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)文章
Spring?Boot?中的?@HystrixCommand?注解原理及使用方法
通過(guò)使用 @HystrixCommand 注解,我們可以輕松地實(shí)現(xiàn)對(duì)方法的隔離和監(jiān)控,從而提高系統(tǒng)的可靠性和穩(wěn)定性,本文介紹了Spring Boot 中的@HystrixCommand注解是什么,其原理以及如何使用,感興趣的朋友跟隨小編一起看看吧2023-07-07Java實(shí)現(xiàn)ip地址和int數(shù)字的相互轉(zhuǎn)換
這篇文章主要介紹了Java實(shí)現(xiàn)ip地址和int數(shù)字的相互轉(zhuǎn)換,幫助大家更好的利用Java處理數(shù)據(jù),感興趣的朋友可以了解下2020-09-09使用Maven 搭建 Spring MVC 本地部署Tomcat的詳細(xì)教程
這篇文章主要介紹了使用Maven 搭建 Spring MVC 本地部署Tomcat,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08IntelliJ-Idea導(dǎo)出可執(zhí)行Jar流程解析
這篇文章主要介紹了IntelliJ-Idea導(dǎo)出可執(zhí)行Jar流程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12Java?C++題解leetcode消失的兩個(gè)數(shù)字實(shí)例
這篇文章主要介紹了Java?C++題解leetcode消失的兩個(gè)數(shù)字實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09