JAVA反射機制是在運行狀態(tài)中叹坦,對于任意一個類熊镣,都能夠知道這個類的所有屬性和方法;對于任意一個對象募书,都能夠調用它的任意一個方法轧钓;這種動態(tài)獲取的信息以及動態(tài)調用對象的方法的功能稱為java語言的反射機制。
Class類和java.lang.reflect類庫一起對反射機制進行了支持锐膜,該類庫包含了Field、Method以及Constructor類弛房。下面就從這兩大方面對反射進行介紹:
1. Class類
類作為程序的一部分道盏,每個類都擁有一個Class對象。每當編寫并編譯一個新類,就會產生一個Class對象(保存在一個同名的.class文件中)荷逞。為了生成這個類的對象媒咳,jvm使用了類加載器系統(tǒng)。
所有的類都是在第一次使用時种远,動態(tài)加載到jvm中的涩澡。類加載器首先檢查這個類的Class對象是否已經加載,未加載就會查找.class文件(本地或網絡獲茸狗蟆)妙同。
1.1 Class類獲取
以下三種方式均可以獲取Class類:
return reportInfo;
Integer num = new Integer(123);
Class c1 = num.getClass();
Class c2 = Integer.class;
Class c3 = Class.forName("java.lang.Integer");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class cl4 = loader.loadClass("java.lang.Integer");
注意點
- .class方式不會引起類的初始化,而Class.forName會引起對應類進行初始化膝迎。
- 基本的 Java 類型(boolean、byte芒涡、char、short卖漫、int费尽、long、float 和 double)和關鍵字 void 也都對應一個 Class 對象羊始。
- 每個數組屬于被映射為 Class 對象的一個類旱幼,所有具有相同元素類型和維數的數組都共享該 Class 對象。
1.2 Class提供常用方法
- getClassLoader() :返回該類的類加載器
- isArray() :判斷是否是數組
- getName():以 String 的形式返回此 Class 對象所表示的實體(類店枣、接口速警、數組類、基本類型或 void)名稱
- newInstance():可以創(chuàng)建該類的示例
- 同時提供了獲取Constructor鸯两、Method和Field的方法闷旧,后續(xù)會詳細說明
1.3 Class使用技巧
- forName和newInstance結合起來使用,可以根據存儲在字符串中的類名創(chuàng)建對象钧唐,例如:Object obj = Class.forName("xxxx").newInstance();
- 虛擬機為每種類型管理一個獨一無二的Class對象忙灼。因此可以使用==操作符來比較類對象,例如:if(e.getClass() == Employee.class)
2. reflect包
reflect包中有三個類钝侠,Field该园,Method,Constructor帅韧,分別去描述類的域里初,方法忽舟,構造器泣特。
通過Class類可以分別獲取上述三個類的具體實例,下面進行分別講述:
2.1 Field類
表示類的成員變量膏孟,其中一個成員變量對應一個Field對象担猛。Class對象獲取Field方法如下:
- getFields():獲得類的public類型的屬性
- getDeclaredFields():獲得類的所有屬性
- getField(String name):獲取指定名稱public類型屬性
- getDeclaredFields(String name):獲取指定名稱屬性
通過上述4種方法,我們可以獲取指定的Field對象仇奶。通過Field對象我們可以實現以下常見功能:
- String getName():獲取字段名
- Class<?> getType() 和 Type getGenericType() :獲取類型和泛型
- int getModifiers():獲取修飾
- Object get(Object obj):獲取指定對象該字段對應值
- void set(Object obj, Object value):給指定對象的該段賦值
這里演示一下如何修改private的屬性值:
這里需要注意的是在賦值給private屬性之前需要調用field.setAccessible(true)方法關閉對private屬性訪問檢查狈茉。
2.2 Method類
表示類的成員方法,其中一個成員方法對應一個Method對象。Class對象獲取Method方法與Filed類似如下:
- getMethods():獲得類的public類型的方法
- getDeclaredMethods():獲得類的所有方法
- getMethod(String name, Class[] parameterTypes):獲得類的特定方法实昨,name參數指定方法的名字族跛,parameterTypes 參數指定方法的參數類型溪北,同時該方法為public的
- getDeclaredMethod(String name, Class<?>... parameterTypes):同上只是范圍擴大到所有方法
同樣獲取Method對象以后,我們可以實現以下常見功能:
- Class<?>[] getParameterTypes():獲取該方法的所有參數類型
- Class<?> getReturnType():獲取該方法返回值
- <T extends Annotation> T getAnnotation(Class<T> annotationClass):獲取方法注解
- Object invoke(Object obj, Object... args)執(zhí)行該方法
同樣這里演示如何執(zhí)行一個private方法:
同樣在執(zhí)行private方法之前需要執(zhí)行method.setAccessible(true),關閉對private方法訪問檢查吉挣。
2.3 Constructor類
表示類的構造方法镀赌,其中一個構造方法對應一個Constructor對象喉钢。Class獲取Constructor的方法如下:
- getConstructors():獲得類的public類型的構造方法
- getDeclaredConstructors():獲取所有構造方法
- getConstructor(Class<?>... parameterTypes):獲得類的特定public構造方法,parameterTypes 參數指定構造方法的參數類型。
- getDeclaredConstructor(Class<?>... parameterTypes):同上范圍擴大到所有構造方法
同樣獲取Constructor對象以后伯复,我們可以實現以下常見功能:
- Class<?>[] getParameterTypes():構造方法參數列表
- T newInstance(Object ... initargs):實例化對應類
接下來示例展示如何利用Constructor對象實例化對應類
2.4 反射與泛型
常見兩種泛型使用場景:
- 聲明一個需要被參數化(parameterizable)的類/接口叮雳,例如:class MyClass<T>
- 使用一個參數化類说莫,例如:List<String> arrays;
2.4.1Type接口
在此之前我們需要介紹一下Type接口,Java編程語言中所有類型的公共父接口,這里所說的所有類型包括:
原始類型 (raw types)對應Class刮萌,參數化類型 (parameterizedtypes)對應ParameterizedType, 數組類型 (array types)對應GenericArrayType,類型變量 (type variables)對應TypeVariable澎语,基本數據類型(primitivetypes)仍然對應Class。
Class類實現了該接口减俏,同時該接口的直接子接口包括以下4個:
- ParameterizedType: 表示一種參數化的類型,比如Collection<String>
- GenericArrayType: 表示一種元素類型是參數化類型或者類型變量的數組類型
- TypeVariable: 是各種類型變量的公共父接口
- WildcardType: 代表一種通配符類型表達式
配合后面的反射內容重點介紹一下java.lang.reflect.ParameterizedType接口
- 含義:表示參數化類型比如:Map<String, Date>這種參數化類型
- Type[] getActualTypeArguments():獲取參數化類型<>中的實際類型(注意對于多次嵌套廊谓,該方法只返回脫去最外層的<>以后剩下內容返回)
- 關于返回值是數組因為存在Map<String, Date>返回不止一種類型
- 為什么返回父接口春弥,因為返回值多態(tài)性,對于ArrayList<ArrayList<Integer>>返回的是ArrayList<Integer>是ParameterizedType類型的公给,而對于ArrayList<E>返回的是E是TypeVariable類型蔫缸,而對于 對于ArrayList<E[]>返回的是E[]則對應的是GenericArrayType類型街望。
- Type getRawType():獲取承載該泛型信息的對象,該類型表示<>前面的類型比如Map<String,String>蔫敲,則返回Map
2.4.2參數化類型反射獲取
講述完Type相關內容以后吞加,我們來看下Field、Method和Constructor相關類如何獲取泛型參數或泛型返回值衔憨。這里我們以Method為例說明:
在這個示例中我們重點關注一下method.getGenericReturnType()方法叶圃,返回返回值Type,查看jdk源碼:
觀察代碼可以發(fā)現在獲取泛型返回是首先判斷getGenericSignature()是否為空該函數是native的沒找到相關資料巫财,從名稱推斷是標識是否是泛型盗似,在Field相關源碼中也有出現。如果返回不問空則返回ParameterizedType類型,否則調用getReturnType返回Class<?>類型缨叫。
查看Field婉商、Constructor相關代碼可以看出相似使用方法:
- Field類中屬性類型 Class<?> getType()和Type getGenericType()
- Method類中方法返回值類型Class<?> getReturnType() 和Type getGenericReturnType()
- Method類中方法參數類型Class<?>[] getParameterTypes()和Type[] getGenericParameterTypes()
- Constructor類中方法參數和Method中方法參數完全一致
最后,如果需要解析Field俏险、Method、Constructor中泛型相關類型锉走,使用步驟和給出示例類似:
- 調用相關Genric對應的方法
- 判斷返回值instanceof ParameterizedType
- 步驟為true強制轉換為ParameterizedType類型
- 后續(xù)調用getActualTypeArguments等方法獲取真實參數
參考文檔
http://lavasoft.blog.51cto.com/62575/15433/
http://developer.51cto.com/art/201103/250028.htm
http://blog.csdn.net/benjaminzhang666/article/details/9838937