Retrofit解析3之反射

整體Retrofit內(nèi)容如下:

本篇文章的主要內(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ì)被初始化退疫。
初始化.png
4、使用
5鸽素、卸載

使用和卸載沒有什么好講解的褒繁,就不說了。

最后附上 別人的思維導(dǎo)圖

類加載.png

(二) 其它:

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ì)象是如何生成的彪蓬。


Method生成.png

上面的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方法的流程如下:


調(diào)用invoke.png

上代碼

    @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的繼承體系


Paste_Image.png

java中所有對(duì)象的類型定義類Type


Paste_Image.png
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蜘矢,WildcardTypeGenericArrayType這四個(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)圖

Java反射.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拱撵,隨后出現(xiàn)的幾起案子辉川,更是在濱河造成了極大的恐慌,老刑警劉巖拴测,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乓旗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡集索,警方通過查閱死者的電腦和手機(jī)屿愚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來务荆,“玉大人妆距,你說我怎么就攤上這事『埃” “怎么了娱据?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)浦箱。 經(jīng)常有香客問我吸耿,道長(zhǎng)祠锣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任咽安,我火速辦了婚禮伴网,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妆棒。我一直安慰自己澡腾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布糕珊。 她就那樣靜靜地躺著动分,像睡著了一般。 火紅的嫁衣襯著肌膚如雪红选。 梳的紋絲不亂的頭發(fā)上澜公,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音喇肋,去河邊找鬼坟乾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蝶防,可吹牛的內(nèi)容都是我干的甚侣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼间学,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼殷费!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起低葫,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤详羡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后氮采,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殷绍,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年鹊漠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了主到。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躯概,死狀恐怖登钥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情娶靡,我是刑警寧澤牧牢,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響塔鳍,放射性物質(zhì)發(fā)生泄漏伯铣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一轮纫、第九天 我趴在偏房一處隱蔽的房頂上張望腔寡。 院中可真熱鬧,春花似錦掌唾、人聲如沸放前。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凭语。三九已至,卻和暖如春撩扒,著一層夾襖步出監(jiān)牢的瞬間似扔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工却舀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虫几,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓挽拔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親但校。 傳聞我的和親對(duì)象是個(gè)殘疾皇子螃诅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法状囱,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法惨驶,線程的語(yǔ)...
    子非魚_t_閱讀 31,622評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理掏熬,服務(wù)發(fā)現(xiàn),斷路器叨粘,智...
    卡卡羅2017閱讀 134,651評(píng)論 18 139
  • 在經(jīng)過一次沒有準(zhǔn)備的面試后猾编,發(fā)現(xiàn)自己雖然寫了兩年的android代碼,基礎(chǔ)知識(shí)卻忘的差不多了升敲。這是程序員的大忌答倡,沒...
    猿來如癡閱讀 2,838評(píng)論 3 10
  • 有人讀書一目十行瘪撇,一天一本,成為讀書達(dá)人。 有人一輩子就讀幾本書倔既,也取得了相當(dāng)大的成就恕曲。 我,曾今好讀書不求甚解渤涌,...
    三才日記閱讀 1,259評(píng)論 0 5
  • 一码俩、蘋果系統(tǒng)底層藍(lán)牙掃描設(shè)備名稱原理 1、第一次連接設(shè)備歼捏,系統(tǒng)無GAP層設(shè)備名稱緩存稿存,此時(shí)掃描到的設(shè)備名稱為設(shè)備廣...
    猿二胖閱讀 826評(píng)論 0 0