整體Retrofit內(nèi)容如下:
- 1渡冻、Retrofit解析1之前哨站——理解RESTful
- 2、Retrofit解析2之使用簡(jiǎn)介
- 3夸赫、Retrofit解析3之反射
- 4菩帝、Retrofit解析4之注解
- 5、Retrofit解析5之代理設(shè)計(jì)模式
- 6茬腿、Retrofit解析6之面向接口編程
- 7呼奢、Retrofit解析7之相關(guān)類解析
- 8、Retrofit解析8之核心解析——ServiceMethod及注解1
- 9切平、Retrofit解析8之核心解析——ServiceMethod及注解2
- 10握础、Retrofit解析9之流程解析
- 11、Retrofit解析10之感謝
本篇文章的主要內(nèi)容如下:
- 1悴品、什么是反射和反射機(jī)制
- 2禀综、什么是Java反射
- 3、Java反射可以做什么
- 4苔严、反射機(jī)制的優(yōu)缺點(diǎn)
- 5定枷、Java類加載原理
- 6、核心類及API
- 7届氢、Method的invoke原理解析
- 8欠窒、泛型與反射
一、什么是反射與反射機(jī)制
(一)退子、什么是反射
反射主要是指程序可以訪問岖妄、檢測(cè)和修改它本身狀態(tài)或行為的一種能力型将。在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是一類應(yīng)用荐虐,它們能夠自描述和自控制七兜。這類應(yīng)用通過某種機(jī)制來實(shí)現(xiàn)對(duì)自己行為的描述和檢測(cè),并根據(jù)自身行為的狀態(tài)和結(jié)果福扬,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語(yǔ)義腕铸。
(二)、反射機(jī)制
反射機(jī)制是在運(yùn)行狀態(tài)中铛碑,對(duì)于任意一個(gè)類恬惯,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象亚茬,都能夠調(diào)用它的任意一個(gè)方法和屬性酪耳;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法功能稱為反射機(jī)制
二、什么是Java反射
Java 反射是Java語(yǔ)言的一個(gè)很重要的特征刹缝,它使得Java具備了"動(dòng)態(tài)化"碗暗。
在Java中的反射機(jī)制,被稱為Reflection梢夯。它允許運(yùn)行中的Java程序?qū)ψ陨磉M(jìn)行檢測(cè)言疗,并能直接操作程序的內(nèi)部屬性或方法。Reflection機(jī)制允許程序在正在執(zhí)行的過程中颂砸,利用Reflection APIs取得任何已知名稱的類的內(nèi)部信息噪奄。包括如下:package、 type parameters人乓、 superclass勤篮、 implemented interfaces、 inner classes色罚、 outer classes碰缔、 fields、 constructors戳护、 methods金抡、 modifiers等,并可以在執(zhí)行過程中腌且,動(dòng)態(tài)生成Instances梗肝、變更fields內(nèi)容或喚起methods。
通俗的講就是
.class反編譯-->.java铺董,這樣就可以通過反射機(jī)制訪問Java對(duì)象的屬性巫击,方法,構(gòu)造方法等。
三喘鸟、Java反射可以做什么?
Java反射機(jī)制主要提供以下功能:
- 運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類
- 在運(yùn)行時(shí)構(gòu)造任意個(gè)類的對(duì)象
- 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法
- 在運(yùn)行時(shí)調(diào)用人一個(gè)對(duì)象的方法
- 生成動(dòng)態(tài)代理
四、反射機(jī)制的優(yōu)缺點(diǎn):
為什么要用反射機(jī)制驻右,直接創(chuàng)建對(duì)象不就可以了嗎什黑?這就涉及到了動(dòng)態(tài)與靜態(tài)的概念。
- 靜態(tài)編譯:在編譯時(shí)確定類型堪夭,綁定對(duì)象愕把,即通過。
- 動(dòng)態(tài)編譯:運(yùn)行時(shí)確定類型森爽,綁定對(duì)象恨豁。動(dòng)態(tài)編譯最大限度發(fā)揮了Java的靈活性,體現(xiàn)了多態(tài)的應(yīng)用爬迟,有以降低類之間的耦合性橘蜜。
優(yōu)缺點(diǎn)
- 1、優(yōu)點(diǎn):
可以實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象和編譯付呕,體現(xiàn)出很大的靈活性计福,特別是J2EE的開發(fā)中它的靈活性就表現(xiàn)的十分明顯。比如徽职,一個(gè)大型的軟件象颖,不可能一次就把它設(shè)計(jì)的很完美,當(dāng)這個(gè)程序編譯姆钉,發(fā)布了说订,當(dāng)發(fā)現(xiàn)需要更新某些功能時(shí),我們不可能要用戶把之前的卸載潮瓶,再重新安裝新的版本陶冷,假如這樣的話,這個(gè)軟件肯定是沒有多少人用的毯辅。采用靜態(tài)的話埃叭,需要把整個(gè)程序重新編譯一次才可以實(shí)現(xiàn)功能的更新,而采用反射機(jī)制的話悉罕,它就可以不用卸載赤屋,只需要在運(yùn)行時(shí)都才動(dòng)態(tài)的創(chuàng)建和編譯,就可以顯示該功能壁袄。 - 2类早、缺點(diǎn):
對(duì)性能有影響,使用反射基本上是一種解釋操作嗜逻,我可以告訴JVM涩僻,我希望做什么并且它滿足我們的需求,這類操作總是慢于直接執(zhí)行相同的操作。
五逆日、Java類加載原理
為了后里面講解Java反射原理方便嵌巷,在這里先講解Java類加載原理
(一)、概述
Class文件由類裝載器加載后室抽,在JVM中將形成一份描述Class結(jié)構(gòu)的元數(shù)據(jù)對(duì)象搪哪,通過該元數(shù)據(jù)可以獲知Class的結(jié)構(gòu)信息:如構(gòu)造函數(shù),屬性和方法等坪圾,Java允許用戶借這個(gè)Class相關(guān)原信息對(duì)象間接調(diào)用Class對(duì)象的功能晓折。
虛擬機(jī)把描述類的數(shù)據(jù)從class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)兽泄,轉(zhuǎn)換解析和初始化漓概,最終形成可以被虛擬機(jī)直接使用的Java類型,這就是虛擬機(jī)的類加載機(jī)制病梢。
JVM把class文件加載到內(nèi)存胃珍,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)蜓陌,解析和初始化护奈,最形成JVM可以直接使用的JAVA類型的過程。
加載-->鏈接(-->驗(yàn)證-->準(zhǔn)備-->解析)-->初始化-->使用-->卸載
1檐晕、加載
將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)中運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)武通,在堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象眶拉,作為方法區(qū)類數(shù)據(jù)的訪問入口拾氓。
將class文件字節(jié)碼內(nèi)容加載到內(nèi)存中孵奶,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)臀脏,在堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象灌诅,作為方法區(qū)類數(shù)據(jù)的訪問入口。
class字節(jié)碼-->類加載類--->內(nèi)存(Class對(duì)象挎袜,方法區(qū)中運(yùn)行時(shí)數(shù)據(jù))--->外部可以通過Class對(duì)象盯仪,操作類
2、鏈接
將Java類的二進(jìn)制代碼合并到JVM的運(yùn)行狀態(tài)之中的過程
- 驗(yàn)證:確保加載的類信息符合JVM規(guī)范径荔,沒有安全方面的問題。
- 準(zhǔn)備:正式為類變量(static變量)分配內(nèi)存并設(shè)置變量初始值的階段睛蛛,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配
- 解析:虛擬機(jī)常量池內(nèi)的符合引用替換為直接引用的過程鹦马。
- 常用吃:虛擬常用池內(nèi)的符合引用替換為直接引用的過程胧谈。
3、初始化
- 初始化階段是執(zhí)行類構(gòu)造器Class的<clinit>()方法的過程荸频。類構(gòu)造器<clinit>()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句(static)塊中的語(yǔ)句合并產(chǎn)生的菱肖。
- 當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其弗雷還沒有進(jìn)行初始化旭从、則需要先執(zhí)行父類的初始化稳强。
- 虛擬機(jī)會(huì)保證一個(gè)的類<clinit>()方法在多線程中唄正確的加鎖和同步。
- 當(dāng)訪問一個(gè)Java類的靜態(tài)域時(shí)和悦,只有真正聲明這個(gè)域才會(huì)被初始化退疫。
4、使用
5鸽素、卸載
使用和卸載沒有什么好講解的褒繁,就不說了。
最后附上 別人的思維導(dǎo)圖
(二) 其它:
1付鹿、這里說下<clinit>和<init>的區(qū)別
在編譯生成class文件時(shí)澜汤,會(huì)自動(dòng)產(chǎn)生兩個(gè)方法蚜迅,一個(gè)是類的初始化方法<clinit>舵匾,另一個(gè)是實(shí)例化的初始化方法<init>
- <clinit>:在JVM第一次加載class文件時(shí)調(diào)用,包括靜態(tài)變量初始化語(yǔ)句和靜態(tài)塊的執(zhí)行
- <init>: 在實(shí)例創(chuàng)建出來的時(shí)候調(diào)用谁不,包括調(diào)用new操作符坐梯;滴啊用Class或者java.lang.reflect.Constructor對(duì)象的newInstance()方法;調(diào)用任何現(xiàn)有對(duì)象的clone()方法刹帕;通過java.io.ObjectInputStream類的getObject()方法反序列化吵血。
2、主動(dòng)引用和被動(dòng)引用
主動(dòng)引用:一定會(huì)發(fā)生類的初始化
- new一個(gè)類的對(duì)象
- 調(diào)用類的靜態(tài)成員(除了final常量)和靜態(tài)方法
- 使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用
- 當(dāng)虛擬機(jī)啟動(dòng)偷溺,java helloworld权悟,則一定會(huì)初始化helloworld類酪刀,說白了就是先啟動(dòng)main方法所在的類。
- 當(dāng)初始化一個(gè)類,如果其父類沒有被初始化嘹朗,則會(huì)先初始化他的父類。
被動(dòng)引用:不會(huì)發(fā)生類的初始化
- 當(dāng)訪問一個(gè)靜態(tài)域時(shí)晨逝,
當(dāng)訪問一個(gè)靜態(tài)域時(shí)捧存,只有真正聲明這個(gè)域的類才會(huì)被初始化
通過子類引用父類的靜態(tài)變量,不會(huì)導(dǎo)致子類初始化
通過數(shù)組定義類引用袄友,不會(huì)觸發(fā)此類的初始化
引用常量不會(huì)觸發(fā)此類的初始化(常量在編譯階段就存入調(diào)用類的常量池中了)
六殿托、核心類及API
核心類,位于java.lang.reflect包中
- Class類:代表一個(gè)類
- Field類:代表一個(gè)類
- Method類:代表類的方法
- Constructor類:代表類的構(gòu)造方式
- Array類:提供了動(dòng)態(tài)創(chuàng)建數(shù)組剧蚣,以及訪問數(shù)組的元素的靜態(tài)方法
(一)Class
1支竹、Class是什么旋廷?
Java程序在運(yùn)行時(shí),Java運(yùn)行時(shí)系統(tǒng)一直對(duì)所有的對(duì)象進(jìn)行所謂的運(yùn)行時(shí)類型標(biāo)記唾戚,這項(xiàng)信息記錄了每個(gè)對(duì)象所屬的類柳洋。虛擬機(jī)通常使用運(yùn)行時(shí)類型信息選擇正確方法去執(zhí)行,用來保存這些類型的類是Class類叹坦。
Class類封裝一個(gè)對(duì)象和接口運(yùn)行時(shí)的狀態(tài)熊镣,當(dāng)加載類時(shí),Class類型的對(duì)象自動(dòng)創(chuàng)建募书。Class沒有公共構(gòu)造方法绪囱。Class對(duì)象是在加載類時(shí)由Java虛擬機(jī)以及通過調(diào)用類加載器中的defineClass方法自動(dòng)構(gòu)造的,因此不能顯示地聲明一個(gè)Class對(duì)對(duì)象莹捡。
虛擬機(jī)為每種類型管理一個(gè)獨(dú)一無二的Class對(duì)象鬼吵。也就是說,每個(gè)類(型)都有一個(gè)Class對(duì)象篮赢。運(yùn)行程序時(shí)齿椅,Java虛擬機(jī)(JVM)首先檢查是否要加載的類對(duì)應(yīng)的Class對(duì)象是否已經(jīng)加載。如果沒有加載启泣,JVM就會(huì)根據(jù)類名查找.class文件涣脚,并將其Class對(duì)象加載。
基本的Java類型(boolean寥茫、byte遣蚀、char、shor纱耻、int芭梯、long、float弄喘、double)和關(guān)鍵字void也都對(duì)應(yīng)一個(gè)Class對(duì)象玖喘。每個(gè)數(shù)據(jù)屬于被映射為Class對(duì)象的一個(gè)類,所有具有相同元素類型和維數(shù)的數(shù)組都
共享該Class對(duì)象蘑志。一般某個(gè)類Class對(duì)象被載入內(nèi)存累奈,它就用來創(chuàng)建這個(gè)類的所有對(duì)象。
2卖漫、Class類的常用方法
在java.lang.Object類中定義了getClass()方法费尽,因此對(duì)于任意一個(gè)Java對(duì)象,都可以通過此方法獲得對(duì)象的類型羊始。Class類是Reflection API中的核心類旱幼。它有如下方法:
方法名 | 作用 |
---|---|
getName() | 獲得類的完整名稱 |
getFields() | 獲得類的public類型的屬性 |
getDeclaredFields() | 獲得類的所有屬性 |
getMethod() | 獲得類的public類型方法 |
getDeclaredFiedMethods() | 獲得類的所有方法 |
getMethod(String name,Class[] parameterTypes) | 獲得類的特定方法,name參數(shù)制定方法的名字突委,parameterTypes 參數(shù)制定方法參數(shù)類型 |
getConstructors() | 獲得類的public類型的構(gòu)造方法 |
getConstructor(Class[] parameterTypes) | 獲得類的特定構(gòu)造方式柏卤,parameterTypes 參數(shù)指定構(gòu)造方法的參數(shù)類型 |
newInstance() | 通過類的不帶參數(shù)的構(gòu)造方法創(chuàng)建這個(gè)類的一個(gè)對(duì)象 |
PS:
- 大家在使用Class實(shí)例化其他類的對(duì)象的時(shí)候冬三,一定要自己定義午餐的構(gòu)造函數(shù)
- 所有類的對(duì)象其實(shí)都得Class的實(shí)例
3、獲取Class對(duì)象的三種方式
- 通過Object類的getClass()方法
例如 Class c1=new String("hello world").getClass(); - 通過Class類的靜態(tài)方法——forName()來實(shí)現(xiàn):
Class c2 = Class.forName("MyObject"); - 如果T是一個(gè)已定義的類型的話缘缚,在java中勾笆,它的.class文件名:T.class就代表了與其匹配的Class對(duì)象。例如:
Class c3 = Manager.class;
Class c4 = int.class;
Class c5 = Double[].class;
(二)桥滨、Constructor
Constructor 提供關(guān)于類的單個(gè)構(gòu)造方法的信息以及對(duì)它的訪問權(quán)限窝爪,用來封裝反射得到的構(gòu)造器。
1齐媒、獲取構(gòu)造方法
Class 類提供四個(gè)public方法蒲每,用于獲取某個(gè)類的構(gòu)造方法
方法名 | 作用 |
---|---|
Constructor getConstructor(Class[] params) | 根據(jù)構(gòu)造函數(shù)的參數(shù),返回一個(gè)具體的具有public屬性的構(gòu)造函數(shù) |
Constructor[] getConstructor() | 返回 所有具有public屬性的構(gòu)造函數(shù)數(shù)組 |
Constructor getDeclaredConstructor(Class[] params) | 根據(jù)構(gòu)造函數(shù)的參數(shù)喻括,返回一個(gè)具體的構(gòu)造函數(shù)(不分public和非public 屬性) |
Constructor getDeclaredConstrutors() | 返回該類中所有的構(gòu)造函數(shù)數(shù)組(不分public 和非public屬性) |
由于Java語(yǔ)言是一種面向?qū)ο蟮恼Z(yǔ)言邀杏,具有多態(tài)的性質(zhì),那么我們可以通過構(gòu)造方法的參數(shù)列表的不同唬血,來調(diào)用不同的構(gòu)造方法去創(chuàng)建類的實(shí)例望蜡。同樣,獲取不同的構(gòu)造方法的信息拷恨,也需要提供與之對(duì)應(yīng)的參數(shù)類型信息脖律;因此,就產(chǎn)生了以上四種不同的獲取構(gòu)造方法的方式挑随。
(三)状您、Field 成員變量信息
想一想成員變量中都包含什么:
成員變量類型+成員變量名
類的成員變量也是一個(gè)對(duì)象勒叠,它是java.lang.reflect.Field的一個(gè)對(duì)象兜挨,所以我們通過java.lang.reflect.Field里面封裝的方法來獲取這些信息。
如果想單獨(dú)獲取某個(gè)成員變量眯分,通過Class類的以下方法實(shí)現(xiàn):
方法名 | 作用 |
---|---|
Field getDeclaredField(String name) | 獲得該類自身聲明的所有變量拌汇,不包括其父類的變量 |
Field getField(String name) | 獲得該類自所有的public成員變量,包括其父類變量 |
舉例如下:
參數(shù)是成員變量的名字弊决。
例如一個(gè)類A有如下成員變量:
private int n;
如果A有一個(gè)對(duì)象a噪舀,那么就可以這樣得到其成員變量:
Class c = a.getClass();
Field field = c.getDeclaredField("n");
由于Method 在Retrofit比較重要,我們就單獨(dú)講解以下
(四) Method類及invoke
Method 提供關(guān)于類或接口上單獨(dú)某個(gè)方法(以及如何反問該方法)的信息飘诗,一個(gè)完整方法包含的屬性有:方法上使用的注解与倡、方法的修飾符、方法上定義的泛型參數(shù)昆稿、方法的返回值纺座、方法名稱、方法拋出的異常溉潭。
1净响、獲取Method
有4種獲取Method的方式:
方法名 | 作用 |
---|---|
Method getMethod(String name, Class[] params) | 根據(jù)方法名和參數(shù)少欺,返回一個(gè)具體的具有public屬性的方法 |
Method[] getMethods() | 返回所有具有public屬性的方法數(shù)組 |
Method getDeclaredMethod(String name, Class[] params) | 根據(jù)方法名和參數(shù),返回一個(gè)具體的方法(不分public和非public屬性) |
Method[] getDeclaredMethods() | 返回該類中的所有的方法數(shù)組(不分public和非public屬性) |
PS:在獲取類的方法時(shí)馋贤,有一個(gè)地方值得大家注意赞别,就是getMethods()方法和getDeclaredMethods()方法。
在
- getMethods():用于獲取類的所有的public修飾域的成員方法配乓,包括從父類繼承的public方法和實(shí)現(xiàn)接口的public方法
- getDeclaredMethods():用于獲取當(dāng)前類中定義的所有的成員方法和實(shí)現(xiàn)接口的方法仿滔,不包括從父類繼承的方法。
2犹芹、Method的API
那來看下Method的API堤撵,因?yàn)镽etrofit里面大量用到了Method的api
方法名 | 作用 |
---|---|
<T extends Annotation> T getAnnotation(Class<T> annotationClass) | 如果存在該元素的指定類型的注釋,則返回這些注解羽莺,否則返回 null |
Annotation[] etDeclaredAnnotations() | 返回直接存在于此元素上的所有注解 |
Class<?> getDeclaringClass() | 返回表示聲明由此 Method 對(duì)象表示的方法的類或接口的 Class 對(duì)象 |
Object getDefaultValue() | 返回由此 Method 實(shí)例表示的注解成員的默認(rèn)值实昨。 |
Class<?>[] getExceptionTypes() | 返回 Class 對(duì)象的數(shù)組,這些對(duì)象描述了聲明將此 Method 對(duì)象表示的底層方法拋出的異常類型盐固。 |
Type[] getGenericExceptionTypes() | 返回 Type 對(duì)象數(shù)組荒给,這些對(duì)象描述了聲明由此 Method 對(duì)象拋出的異常。 |
Type[] getGenericParameterTypes() | 按照聲明順序返回 Type 對(duì)象的數(shù)組刁卜,這些對(duì)象描述了此 Method 對(duì)象所表示的方法的形參類型的志电。 |
Type getGenericReturnType() | 返回表示由此 Method 對(duì)象所表示方法的正式返回類型的 Type 對(duì)象 |
int getModifiers() | 以整數(shù)形式返回此 Method 對(duì)象所表示方法的 Java 語(yǔ)言修飾符。 |
String getName() | 以 String 形式返回此 Method 對(duì)象表示的方法名稱蛔趴。 |
Annotation[][] getParameterAnnotations() | 返回表示按照聲明順序?qū)Υ?Method 對(duì)象所表示方法的形參進(jìn)行注釋的那個(gè)數(shù)組的數(shù)組挑辆。 |
Class<?>[] getParameterTypes() | 按照聲明順序返回 Class 對(duì)象的數(shù)組,這些對(duì)象描述了此 Method 對(duì)象所表示的方法的形參類型孝情。 |
Class<?> getReturnType() | 返回一個(gè) Class 對(duì)象鱼蝉,該對(duì)象描述了此 Method 對(duì)象所表示的方法的正式返回類型。 |
TypeVariable<Method>[] getTypeParameters() | 返回 TypeVariable 對(duì)象的數(shù)組箫荡,這些對(duì)象描述了由 GenericDeclaration 對(duì)象表示的一般聲明按聲明順序來聲明的類型變量 |
Object invoke(Object obj, Object... args) | 對(duì)帶有指定參數(shù)的指定對(duì)象調(diào)用由此 Method 對(duì)象表示的底層方法魁亦。 |
補(bǔ)充一個(gè)知識(shí)點(diǎn):
在反射機(jī)制中,F(xiàn)ield的getModifiers()方法返回int類型值表示該字段的修飾符羔挡,其中該修飾符是java.lang.reflect.Modifier的靜態(tài)屬性洁奈。對(duì)應(yīng)如下:
Modifier.java
/**
* The {@code int} value representing the {@code public}
* modifier.
*/
public static final int PUBLIC = 0x00000001;
/**
* The {@code int} value representing the {@code private}
* modifier.
*/
public static final int PRIVATE = 0x00000002;
/**
* The {@code int} value representing the {@code protected}
* modifier.
*/
public static final int PROTECTED = 0x00000004;
/**
* The {@code int} value representing the {@code static}
* modifier.
*/
public static final int STATIC = 0x00000008;
/**
* The {@code int} value representing the {@code final}
* modifier.
*/
public static final int FINAL = 0x00000010;
/**
* The {@code int} value representing the {@code synchronized}
* modifier.
*/
public static final int SYNCHRONIZED = 0x00000020;
/**
* The {@code int} value representing the {@code volatile}
* modifier.
*/
public static final int VOLATILE = 0x00000040;
/**
* The {@code int} value representing the {@code transient}
* modifier.
*/
public static final int TRANSIENT = 0x00000080;
/**
* The {@code int} value representing the {@code native}
* modifier.
*/
public static final int NATIVE = 0x00000100;
/**
* The {@code int} value representing the {@code interface}
* modifier.
*/
public static final int INTERFACE = 0x00000200;
/**
* The {@code int} value representing the {@code abstract}
* modifier.
*/
public static final int ABSTRACT = 0x00000400;
/**
* The {@code int} value representing the {@code strictfp}
* modifier.
*/
public static final int STRICT = 0x00000800;
簡(jiǎn)單一點(diǎn)就是:
- PUBLIC: 1
- PRIVATE: 2
- PROTECTED: 4
- STATIC: 8
- FINAL: 16
- SYNCHRONIZED: 32
- VOLATILE: 64
- TRANSIENT: 128
- NATIVE: 256
- INTERFACE: 512
- ABSTRACT: 1024
- STRICT: 2048
3、Method的invoke方法
注意事項(xiàng)1
通過Method對(duì)象調(diào)用invoke()方法
//獲取一個(gè)方法名為doHello绞灼,參數(shù)類型為String的方法
Method method = MyObject.class.getMethod("doHello", String.class);
Object returnValue = method.invoke(null, "value1");
如果是一個(gè)靜態(tài)方法調(diào)用的話利术,可以穿用null代替制定對(duì)象作為invoke()方法的參數(shù),在上面的方法中低矮,如果doHello不是靜態(tài)方法的話印叁,你就要傳入有效的MyObject對(duì)象,而不是null。
注意事項(xiàng)2
當(dāng)通過Method的invoke()方法來調(diào)用對(duì)應(yīng)的方法時(shí)喉钢,Java會(huì)要求程序必須有調(diào)用該方法的權(quán)限姆打,如果程序確實(shí)需要調(diào)用某個(gè)對(duì)象的private方法,則可以先調(diào)用Method對(duì)象的如下方法肠虽。
setAccessible(boolean flag)
將Method對(duì)象的accessible設(shè)置為制定的布爾值幔戏。值為true,指示Method在使用時(shí)應(yīng)該取消Java語(yǔ)言訪問權(quán)限檢查税课;值為false闲延,則指示該Method在使用時(shí)實(shí)施Java語(yǔ)言的訪問權(quán)限檢查。
七韩玩、Method的invoke原理解析
先上代碼
Class actionClass=Class.forName(“MyClass”);
Object action=actionClass.newInstance();
Method method = actionClass.getMethod(“myMethod”,null);
method.invoke(action,null);
上面就是最常見的反射使用的例子垒玲,前兩行實(shí)現(xiàn)了類的裝載、鏈接和初始化(newInstance方法實(shí)際上也是使用反射調(diào)用了<init>方法)找颓,后兩行實(shí)現(xiàn)了從class對(duì)象中獲取到method對(duì)象然后執(zhí)行反射調(diào)用合愈。下面簡(jiǎn)單分析一下后兩行的原理。
Class Method{
public Object invoke(Object obj,Object[] param){
MyClass myClass=(MyClass)obj;
return myClass.myMethod();
}
}
method.invoke(action,null);的原理其實(shí)就是動(dòng)態(tài)的生成類似于上面的字節(jié)碼击狮,加載到JVM中運(yùn)行佛析。
1、獲取Method對(duì)象
首先來看下Method對(duì)象是如何生成的彪蓬。
上面的Class對(duì)象是在加載類時(shí)由JVM構(gòu)造的寸莫,JVM為每個(gè)類管理一個(gè)獨(dú)一無二的Class對(duì)象,這份Class對(duì)象里面維護(hù)著該類的所有Method档冬,F(xiàn)ield膘茎,Constructor的cache,這份cache也可以被稱為根對(duì)象酷誓。每次getMethod獲取到的Method對(duì)象都持有對(duì)跟對(duì)象的引用披坏,因此一些重量級(jí)別的Method的成員變量(主要是MethodAccessor),我們不希望每次創(chuàng)建Method都要重新初始化呛牲,于是所有代表同一個(gè)方法的Method對(duì)象都共享根對(duì)象的MethodAccessor刮萌,每一次創(chuàng)建都會(huì)調(diào)用對(duì)象的copy方法復(fù)制一份:
Method copy() {
if(this.root != null) {
throw new IllegalArgumentException("Can not copy a non-root Method");
} else {
Method var1 = new Method(this.clazz, this.name, this.parameterTypes, this.returnType, this.exceptionTypes, this.modifiers, this.slot, this.signature, this.annotations, this.parameterAnnotations, this.annotationDefault);
var1.root = this;
var1.methodAccessor = this.methodAccessor;
return var1;
}
}
2驮配、調(diào)用invoke()方法
獲取到Method對(duì)象之后娘扩,調(diào)用invoke方法的流程如下:
上代碼
@CallerSensitive
public Object invoke(Object var1, Object... var2) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if(!this.override && !Reflection.quickCheckMemberAccess(this.clazz, this.modifiers)) {
Class var3 = Reflection.getCallerClass();
this.checkAccess(var3, this.clazz, var1, this.modifiers);
}
MethodAccessor var4 = this.methodAccessor;
if(var4 == null) {
var4 = this.acquireMethodAccessor();
}
return var4.invoke(var1, var2);
}
代碼解釋
- 首先,檢查AccessibleObject的overrider屬性是否為true壮锻。因?yàn)锳ccessibleObject是Method琐旁、Field、Constructor的父類猜绣,override屬性默認(rèn)為false灰殴,可調(diào)用setAccessible方法改變,如果設(shè)置為true掰邢,則表示可以忽略訪問權(quán)限的限制牺陶,直接調(diào)用伟阔。
- 其次,如果不是true掰伸,則要進(jìn)行訪問權(quán)限監(jiān)測(cè)皱炉,用Reflection的quickCheckMemberAccess方法檢查是不是public,如果不再用Reflection.getCallerClass()方法獲得到調(diào)用這個(gè)方法的Class狮鸭,然后做是否有權(quán)限訪問的校驗(yàn)合搅,校驗(yàn)之后緩存一次,以便下次如果還是這個(gè)類調(diào)用就不用去做校驗(yàn)歧蕉,直接用上次的結(jié)果灾部。(很奇怪用這種方式緩存,因?yàn)檫@種方式如果下次換個(gè)類來調(diào)用的話惯退,就不會(huì)用緩存了赌髓,而再驗(yàn)證一遍,把這次的結(jié)果作為緩存催跪,但上一次的緩存結(jié)果就沖掉了春弥,這是一個(gè)很簡(jiǎn)單的緩存機(jī)制,只適用一個(gè)類的重復(fù)調(diào)用叠荠。)
- 再次匿沛,調(diào)用MethodAccessor的invoke()方法。每個(gè)Method對(duì)象包含一個(gè)root對(duì)象榛鼎,root對(duì)象里持有
一個(gè)MethodAccessor對(duì)象逃呼。我么獲得的Method獨(dú)享相當(dāng)于一個(gè)root對(duì)象的鏡像,所有這類Method共享root李的MethodAccessor對(duì)象者娱,(這個(gè)對(duì)象有ReflectionFactory方法生成抡笼,ReflectionFactory對(duì)象在Method類中是static final的native方法實(shí)例化。) - 最后黄鳍,我們深入一下NativeMethodAccessorImpl的invoke()方法推姻,NativeMethodAccessorImpl的invoke方法里面調(diào)用了native方法的invoke執(zhí)行】蚬担可以都看到藏古,調(diào)用Method.invoke之后,就會(huì)去調(diào)用MethodAccessor.invoke().MethodAccessor就是上面提到的所有同名method共享的對(duì)象忍燥,有ReflectionFactory創(chuàng)建拧晕。創(chuàng)建機(jī)制采用了一種名為inflation的方式,如果該方法的累計(jì)調(diào)用次數(shù)<=15梅垄,會(huì)創(chuàng)建出NativeMethodAccessorImpl厂捞,它的實(shí)現(xiàn)就是直接調(diào)用native方法實(shí)現(xiàn)反射;如果該方法的累計(jì)調(diào)用次數(shù)>15,會(huì)有Java代碼創(chuàng)建處于字節(jié)碼組裝而成的MethodAccessorImpl靡馁。
PS:是否采用inflation和15這個(gè)數(shù)字都可以在JVM參數(shù)中調(diào)整欲鹏。
總結(jié)一下:
一個(gè)方法可以生成多個(gè)Method對(duì)象,但只有一個(gè)root對(duì)象臭墨,主要用持有一個(gè)MethodAccessor對(duì)象貌虾,這個(gè)對(duì)象也可以認(rèn)為一個(gè)方法只有一個(gè),相當(dāng)于是static的裙犹,因?yàn)镸ethod的invoke是交給MethodAccessor執(zhí)行的尽狠,
八、泛型與反射
(一)叶圃、什么是泛型
泛型(Generic type 或者 generics) 是對(duì)Java語(yǔ)言的類型系統(tǒng)的一種擴(kuò)展袄膏,以支持創(chuàng)建可以按類型進(jìn)行參數(shù)化的類〔艄冢可以把來類型參數(shù)看做是使用參數(shù)化類型時(shí)制定的類型的一個(gè)占位符沉馆,就像方法的形式參數(shù)是運(yùn)行時(shí)傳遞值的占位符一樣。
可以在集合框架( Collection frameword ) 中看到泛型的動(dòng)機(jī)德崭。例如斥黑,Mapl類允許您向一個(gè)Map 添加任意類的對(duì)象,即使最常見的情況是在給定映射(map)中保存某個(gè)特定類型(比如 String) 的對(duì)象眉厨。因?yàn)镸ap.get()被定義為返回Object锌奴,所以一般必須將Map.get()的結(jié)果強(qiáng)制類型轉(zhuǎn)換為期望的類型,如下面的代碼所示:
Map m = new HashMap();
m.put("key", "hello");
String s = (String) m.get("key");
要讓程序通過編譯憾股,必須將get()的結(jié)果強(qiáng)制類型轉(zhuǎn)換為String鹿蜀,并且希望結(jié)果真的是一個(gè)String。但是有可能某人已經(jīng)在映射中保存了不是String的東西,這樣的話,上面的代碼將會(huì)拋出ClassCastException哨鸭。
理想情況下,你可能會(huì)得出這樣一個(gè)觀點(diǎn)往枣,即m是一個(gè)Map,它將String鍵映射到String值粉渠。這可以讓您消除代碼中強(qiáng)制類型轉(zhuǎn)換分冈,同時(shí)獲得一個(gè)附加的類型檢查層,該檢查層可以防止有人將錯(cuò)誤類型的鍵或值保存在集合中渣叛。這樣就是泛型所做的工作丈秩。
(二)、泛型的好處
Java語(yǔ)言中引入泛型是一個(gè)比較大的功能增強(qiáng)淳衙。不僅語(yǔ)言、類型系統(tǒng)和編譯器有了較大的變化,以支持泛型箫攀,而且類庫(kù)也進(jìn)行了大翻修肠牲,所以許多重要的類,比如集合框架靴跛,已經(jīng)成為泛型化缀雳,這帶來很多好處:
- 類型安全:泛型的主要目標(biāo)是提高Java程序的類型安全。通過知道使用泛型定義的變量的類型限制梢睛,編譯器可以在一個(gè)高得多的程序上驗(yàn)證類型假設(shè)肥印。沒有泛型,這些假設(shè)就只存在于程序員的頭腦中(或者如果幸運(yùn)的話绝葡,還存在注釋中)深碱。
Java程序中的一種流行技術(shù)是定義這樣的集合,即它的元素或鍵是公共類型的藏畅,比如"String 列表"或者"String 到 String 的映射"敷硅。通過在變量聲明中捕獲這一附加的類型信息,泛型允許編譯器實(shí)施這些附加的類型約束愉阎。類型錯(cuò)誤現(xiàn)在就可以在編譯時(shí)被捕獲了绞蹦,而不是在運(yùn)行時(shí)當(dāng)做ClassCastException 展示出來。將類型檢查從運(yùn)行時(shí)挪到編譯時(shí)有助于你更容易找到錯(cuò)誤榜旦。并提高程序的可靠性幽七。 - 消除強(qiáng)制類型轉(zhuǎn)換。泛型的一個(gè)附帶好處是溅呢,消除源代碼中的許多強(qiáng)制類型轉(zhuǎn)化锉走。這使得代碼更加可讀,并且減少了出錯(cuò)的機(jī)會(huì)
(三)藕届、命名類型參數(shù)
推薦的命名約定是使用大寫的單個(gè)字幕作為類型參數(shù)挪蹭。這與C++約定有所不同,并反映了大多數(shù)泛型類將具有少量類型參數(shù)的假設(shè)休偶。對(duì)常見的泛型模式梁厉,推薦的名稱是:
- K————鍵,比如映射的鍵
- V————值踏兜,比如List和Set的內(nèi)容词顾,或者M(jìn)ap中的值。
- E————異常類
- T————泛型
(四)泛型擦除
1碱妆、類型擦除
正確理解泛型概念的首要前提是理解類型擦除(type erasure)肉盹。Java中泛型基本上都是編譯器這個(gè)層次來實(shí)現(xiàn)的。在生成的Java字節(jié)碼中是不包含泛型中的類型信息的疹尾。使用泛型的時(shí)候加上的類型參數(shù)上忍,會(huì)被編譯器在編譯的時(shí)候去掉骤肛。這個(gè)過程就叫做類型擦除。
比如在代碼中定義List<Object>和List<String>等類型窍蓝,在編譯之后就會(huì)變成List腋颠。JVM看到的是List,而泛型附加的類型信息對(duì)JVM來說是不可見的吓笙。Java編譯器會(huì)在編譯時(shí)盡可能的發(fā)現(xiàn)可能出錯(cuò)的地方淑玫,但是仍無法避免在運(yùn)行時(shí)出現(xiàn)類型轉(zhuǎn)換異常的情況。類型擦除也是Java泛型實(shí)現(xiàn)方法與C++模板機(jī)制實(shí)現(xiàn)方法是之間的重要區(qū)別
注意:
很多泛型的奇怪特性都與這個(gè)類型擦除的存在有關(guān)面睛,包括泛型類并沒有自己獨(dú)有的Class類對(duì)象絮蒿。比如并不存在List<String>.class或是List<Integer>.class,而只有List.class叁鉴。靜態(tài)變量是被泛型類的所有對(duì)象所共享的土涝。對(duì)于聲明為MyClass<T>的類,訪問其中的靜態(tài)變量的方法仍然是MyClass.myStaticVar亲茅。不管是通過new MyClass<String> 還是new MyClass<Integer> 創(chuàng)建的對(duì)象回铛,都是共享一個(gè)靜態(tài)變量。泛型的類型參數(shù)不能用在Java異常處理的catch語(yǔ)句中克锣,因?yàn)楫惓L幚硎怯蒍VM在運(yùn)行時(shí)進(jìn)行的茵肃,由于類型信息被擦除,JVM是無法區(qū)分兩個(gè)異常類型MyExcetption<String>和MyExcetion<Integer>的袭祟。對(duì)于JVM來說验残,他們都是MyExcetpion類型。也就是無法執(zhí)行與異常對(duì)應(yīng)的catch語(yǔ)句
(五)巾乳、通配符與上下界
在使用泛型類的時(shí)候您没,既可以指定一個(gè)具體的類型,比如List<String> 就聲明了具體的類型是String胆绊,也可以使用通配符氨鹏?來表示位置類型,比如List<?>就聲明了List中包含的元素類型是未知的压状。通配符所代表的其實(shí)是一組類型仆抵,但具體的類型是未知的。List<?> 所聲明的就是所有類型都是可以的种冬。但是List<?>并不等同與List<Object>镣丑。List<Object>實(shí)際上確定了List中包含的是Object及其子類,在使用的時(shí)候都可以通過Object來進(jìn)行引用娱两。而List<?>則表示其中所包含的類型是不確定的莺匠。其中可能包含的是String,也可能是Integer十兢。如果它包含了String的話趣竣,往里面添加Integer類型的元素就是錯(cuò)誤的摇庙。正因?yàn)轭愋臀粗筒荒芡ㄟ^new ArrayList<?>()來創(chuàng)建一個(gè)新的ArrayList對(duì)象期贫,因?yàn)榫幾g器無法知道具體的類型是什么跟匆。但是對(duì)于List<?>中的元素卻總是可以通過Object來引用异袄。因?yàn)殡m然類型未知通砍,但肯定是Object及其子類。
(六) 泛型的使用注意事項(xiàng):
在使用泛型的時(shí)候可以遵循一些基本的原則烤蜕,從而避免一些常見的問題封孙。
- 在代碼中避免泛型類和原始類型的混用。比如List<String> 和List不應(yīng)該共同使用讽营。這樣會(huì)產(chǎn)生一些編譯器警告和潛在的運(yùn)行時(shí)異常虎忌。
- 在使用帶通配符的泛型類的時(shí)候,需要明確通配符所代表的一組類型的概念橱鹏。由于具體的類型是未知的膜蠢,很多操作是不允許的。
- 泛型最好不要和同數(shù)組一塊使用莉兰。
- 不要忽視編譯器給出的警告信息挑围。
(七) 泛型與反射
1、Java泛型與反射結(jié)構(gòu)
java中class,method,field的繼承體系
java中所有對(duì)象的類型定義類Type
2糖荒、相關(guān)類說明
(1)杉辙、Type
它 是所有類型的公共接口,包括:
- 原始類(raw types[對(duì)應(yīng) Class])
- 參數(shù)化類型(parameterized types [對(duì)應(yīng) ParameterizedType])
- 數(shù)組類型(array types [ 對(duì)應(yīng) GenericArrayType])
- 類型變量(type variables [對(duì)應(yīng) TypeVariable ])
- 基本類型((primitivetypes)[對(duì)應(yīng) Class ])
同時(shí)ParameterizedType捶朵,TypeVariable蜘矢,WildcardType,GenericArrayType這四個(gè)接口都是它的子接口综看。
(2)品腹、GenericDeclaration 接口
1、接口含義:所有可以聲明類型變量(TypeVariable)的公共父接口
2红碑、直接實(shí)現(xiàn)子類:java.lang.reflect子包中的:這個(gè)接口Class舞吭、Method、Constructor都有實(shí)現(xiàn)
注意:方法的屬性上面不能定義類型變量句喷,所以GenericDeclaration的直接實(shí)現(xiàn)子類沒有Field類
GenericDeclararion接口的源碼
package java.lang.reflect;
public interface GenericDeclaration {
public TypeVariable<?>[] getTypeParameters();
}
方法的返回值類型是數(shù)組,原因就是一個(gè)可以聲明類型變量的實(shí)體可以同時(shí)聲明多個(gè)類型變量镣典,并且每一個(gè)元素類型是TypeVariable類型。
(3)唾琼、 TypeVariable接口
它表示類型變量兄春。
package java.lang.reflect;
public interface TypeVariable<D extends GenericDeclaration> extends Type {
/**
* Returns the upper bounds of this type variable. {@code Object} is the
* implicit upper bound if no other bounds are declared.
*
* @return the upper bounds of this type variable
*
* @throws TypeNotPresentException
* if any of the bounds points to a missing type
* @throws MalformedParameterizedTypeException
* if any of the bounds points to a type that cannot be
* instantiated for some reason
*/
Type[] getBounds();
/**
* Returns the language construct that declares this type variable.
*
* @return the generic declaration
*/
D getGenericDeclaration();
/**
* Returns the name of this type variable as it is specified in source
* code.
*
* @return the name of this type variable
*/
String getName();
}
泛型接口TypeVariable<D extends GenericDeclaration> 可以知道TypeVariable中通過泛型限定extends指定的父類正好的是接口GenericDeclaration。而GenericDeclaration的直接實(shí)現(xiàn)子類僅有Class锡溯,Method和Constructor赶舆,所以TypeVariable的所有參數(shù)化類型是
- TypeVariable<Class>
- TypeVariable<Method>
- TypeVariable<Constructor>
由于Class和Constructor本身也是泛型類哑姚,但是Method本身不是泛型,所以上面的參數(shù)化類型應(yīng)該是如下:
- TypeVariable<Class<T>> :Class<T>類上聲明的TypeVariable
- TypeVariable<Method> : Method類上聲明的TypeVariable
- TypeVariable<Constructor<T>> : Constructor類上聲明的TypeVariable
所以TypeVariable中的泛型是對(duì)定義TypeVariable位置的描述芜茵,不同的GenericDeclaration的實(shí)現(xiàn)子類代表了這個(gè)類型變量到底是在類上定義的叙量,還是方法上定義的,還是在構(gòu)造上定義的九串。
現(xiàn)在來分析下三個(gè)方法
先定義public class TypeVariableBean<K extends InputStream & Serializable, V> 绞佩,其中K ,V 都是屬于類型變量猪钮。
- Type[] getBounds():得到上邊界的Type數(shù)組品山,如K的上邊接數(shù)組是InputStream和Serializable。V沒有指定則是Object烤低。
- D getGenericDeclaration(): 返回的是聲明這個(gè)Type所在類的Type
- String getName() : 返回的是這個(gè) type variable的名稱
(4)肘交、ParameterizedType
它標(biāo)識(shí)參數(shù)化類型,源碼如下:
/**
* This interface represents a parameterized type such as {@code
* 'Set<String>'}.
*
* @since 1.5
*/
public interface ParameterizedType extends Type {
/**
* Returns an array of the actual type arguments for this type.
* <p>
* If this type models a non parameterized type nested within a
* parameterized type, this method returns a zero length array. The generic
* type of the following {@code field} declaration is an example for a
* parameterized type without type arguments.
*
* <pre>
* A<String>.B field;
*
* class A<T> {
* class B {
* }
* }</pre>
*
*
* @return the actual type arguments
*
* @throws TypeNotPresentException
* if one of the type arguments cannot be found
* @throws MalformedParameterizedTypeException
* if one of the type arguments cannot be instantiated for some
* reason
*/
//返回 這個(gè) Type 類型的參數(shù)的實(shí)際類型數(shù)組扑馁。
// 如 Map<String,Person> map
//這個(gè) ParameterizedType 返回的是 String 類,
//Person 類的全限定類名的 Type Array涯呻。
Type[] getActualTypeArguments();
/**
* Returns the parent / owner type, if this type is an inner type, otherwise
* {@code null} is returned if this is a top-level type.
*
* @return the owner type or {@code null} if this is a top-level type
*
* @throws TypeNotPresentException
* if one of the type arguments cannot be found
* @throws MalformedParameterizedTypeException
* if the owner type cannot be instantiated for some reason
*/
Type getOwnerType();
/**
* Returns the declaring type of this parameterized type.
* <p>
* The raw type of {@code Set<String> field;} is {@code Set}.
*
* @return the raw type of this parameterized type
*/
//返回的是當(dāng)前這個(gè) ParameterizedType 的類型。
//如 Map<String,Person> map
//這個(gè) ParameterizedType 返回的是 Map 類的全限定類名的 Type Array腻要。
Type getRawType();
}
官方給的解釋是
ParameterizedType represents a parameterized type such as Collection<String>
需要注意的是复罐,并不只是 Collection<String> 才是 parameterized,任何類似于 ClassName<V> 這樣的類型都是 ParameterizedType 闯第。比如下面的這些都是
- Map<String, Person> map;
- Set<String> set1;
- Class<?> clz;
- Holder<String> holder;
- List<String> list;
- static class Holder<V>{}
而類似于這樣的 ClassName 不是 ParameterizedType.
Set mSet;
List mList;
(5)GenericArrayType
/**
* {@code GenericArrayType} represents an array type whose component
* type is either a parameterized type or a type variable.
* @since 1.5
*/
public interface GenericArrayType extends Type {
/**
* Returns a {@code Type} object representing the component type
* of this array. This method creates the component type of the
* array. See the declaration of {@link
* java.lang.reflect.ParameterizedType ParameterizedType} for the
* semantics of the creation process for parameterized types and
* see {@link java.lang.reflect.TypeVariable TypeVariable} for the
* creation process for type variables.
*
* @return a {@code Type} object representing the component type
* of this array
* @throws TypeNotPresentException if the underlying array type's
* component type refers to a non-existent type declaration
* @throws MalformedParameterizedTypeException if the
* underlying array type's component type refers to a
* parameterized type that cannot be instantiated for any reason
*/
Type getGenericComponentType();
}
簡(jiǎn)單的來說就是:泛型數(shù)組市栗,組成數(shù)組的元素中有泛型則實(shí)現(xiàn)了該接口;它的組成元素是ParameterizedType或者TypeVariable類型
// 屬于 GenericArrayType
List<String>[] pTypeArray;
// 屬于 GenericArrayType
T[] vTypeArray;
// 不屬于 GenericArrayType
List<String> list;
// 不屬于 GenericArrayType
String[] strings;
// 不屬于 GenericArrayType
Person[] ints;
下面我們一起來看一下例子
public class GenericArrayTypeBean<T> {
public void test(List<String>[] pTypeArray, T[] vTypeArray,
List<String> list, String[] strings, Person[] ints) {
}
}
public static void testGenericArrayType() {
Method method = GenericArrayTypeBean.class.getDeclaredMethods()[0];
System.out.println(method);
// public void test(List<String>[] pTypeArray, T[]
// vTypeArray,List<String> list, String[] strings, Person[] ints)
Type[] types = method.getGenericParameterTypes(); // 這是 Method 中的方法
for (Type type : types) {
System.out.println(type instanceof GenericArrayType);// 依次輸出true咳短,true填帽,false,false咙好,false
}
}
輸出結(jié)果
public void com.demo.beans.GenericArrayTypeBean.test(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],com.demo.beans.Person[])
true
true
false
false
false
(6)篡腌、WildcardType 通配符的類型
/**
* Represents a wildcard type argument.
* Examples include: <pre>
* {@code <?>}
* {@code <? extends E>}
* {@code <? super T>}
* </pre>
* A wildcard type can have explicit <i>extends</i> bounds
* or explicit <i>super</i> bounds or neither, but not both.
*
* @author Scott Seligman
* @since 1.5
*/
public interface WildcardType extends Type {
/**
* Return the upper bounds of this wildcard type argument
* as given by the <i>extends</i> clause.
* Return an empty array if no such bounds are explicitly given.
*
* @return the extends bounds of this wildcard type argument
*/
Type[] extendsBounds();
/**
* Return the lower bounds of this wildcard type argument
* as given by the <i>super</i> clause.
* Return an empty array if no such bounds are explicitly given.
*
* @return the super bounds of this wildcard type argument
*/
看類注釋知道
{@code ?}, {@code ? extends Number}, or {@code ? super Integer} 這些類型 都屬于 WildcardType
PS:extends 用于指定上邊界,沒有指定的話上邊界默認(rèn)是Object勾效,super用來指定下邊界嘹悼,沒有指定的話為null
主要方法解釋:
- Type[] getLowerBounds() 得到上邊界 Type 的數(shù)組
- Type[] getUpperBounds() 得到下邊界 Type 的數(shù)組
(7)、Type總結(jié)
Type及其子接口的來歷
泛型出現(xiàn)之前的類型
沒有泛型的時(shí)候层宫,只有原始類型杨伙。此時(shí),所有原始類型都通過字節(jié)碼文件類Class進(jìn)行抽象萌腿。Class類的一個(gè)具體對(duì)象就代表一個(gè)指定的原始類型限匣。
泛型出現(xiàn)之后的類型
泛型出現(xiàn)之后,擴(kuò)充了數(shù)據(jù)類型毁菱。從只有原始類型擴(kuò)充了參數(shù)畫類型米死、類型變量類型锌历、限定符類型、泛型數(shù)組類型峦筒。
2究西、Generic Method Return Type(方法返回值類型的泛型)
如果你獲得了java.lang.reflect.Method對(duì)象,你也是有可能獲得它的返回值類型的泛型信息的物喷。這不會(huì)是任何參數(shù)化類型的Method對(duì)象卤材,除了在類里面使用了參數(shù)化類型。舉一個(gè)類有參數(shù)化返回值類型的返回值:
public class MyClass {
protected List<String> stringList = new ArrayList<String>();
public List<String> getStringList(){
return this.stringList;
}
}
在這種情況下脯丝,可以取得getStringList()方法的泛型返回值類型商膊。換句話說伏伐,是可以檢測(cè)到getStringList()方法返回的是List<String> 類型而不僅僅是List宠进。代碼如下:
Method method = MyClass.class.getMethod("getStringList", null);
Type returnType = method.getGenericReturnType();
if(returnType instanceof ParameterizedType){
ParameterizedType type = (ParameterizedType) returnType;
Type[] typeArguments = type.getActualTypeArguments();
for(Type typeArgument : typeArguments){
Class typeArgClass = (Class) typeArgument;
System.out.println("typeArgClass = " + typeArgClass);
}
}
這段代碼將會(huì)打印出“typeArgClass = java.lang.String”.Type[ ]類型的數(shù)組typeArguements中包含一個(gè)項(xiàng),一個(gè)代表實(shí)現(xiàn)了Type接口的java.lang.String.Class的Class實(shí)例藐翎。
2材蹬、Generic Method Parameter Types
在運(yùn)行時(shí),你也可以通過Java反射機(jī)制訪問泛型參數(shù)的類型吝镣,下面舉例說明
public class MyClass {
protected List<String> stringList = new ArrayList<String>();
public void setStringList(List<String> list){
this.stringList = list;
}
}
你可以像這樣來訪問其方法參數(shù)的參數(shù)化類型:
method = Myclass.class.getMethod("setStringList", List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for(Type genericParameterType : genericParameterTypes){
if(genericParameterType instanceof ParameterizedType){
ParameterizedType aType = (ParameterizedType) genericParameterType;
Type[] parameterArgTypes = aType.getActualTypeArguments();
for(Type parameterArgType : parameterArgTypes){
Class parameterArgClass = (Class) parameterArgType;
System.out.println("parameterArgClass = " + parameterArgClass);
}
}
}
這段代碼將會(huì)打印出“parameterArgType = java.lang.String”堤器。Type[ ]類型的數(shù)組parameterArgTypes中包含一個(gè)項(xiàng),一個(gè)代表實(shí)現(xiàn)了Type接口的java.lang.String.Class的Class實(shí)例末贾。
至此反射講解完畢闸溃,最后奉上別人做的比較不錯(cuò)的思維導(dǎo)圖