簡介:
反射是Java語言中一個(gè)比較重要的特性帚称,它允許對(duì)正在運(yùn)行的Java進(jìn)行觀測(cè)咧纠,甚至動(dòng)態(tài)修改程序蓬痒,即在運(yùn)行態(tài),對(duì)于任意一個(gè)類漆羔,都能夠知道這個(gè)類的所有屬性和方法梧奢;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性演痒。
反射API介紹
使用反射API的第一步是獲取Class對(duì)象
1.已知具體的類亲轨,通過類的class屬性獲取,對(duì)于基本類型來說鸟顺,
它們的包裝類型(wrapper classes)擁有一個(gè)名為“TYPE”的final靜態(tài)字段惦蚊,
指向該基本類型對(duì)應(yīng)的Class對(duì)象
2.已知某個(gè)類的實(shí)例,調(diào)用對(duì)象的getClass()方法獲取Class對(duì)象
3.已知一個(gè)類的全類名讯嫂,使用靜態(tài)方法Class.forName來獲取
例如
Integer.TYPE 指向 int.class蹦锋。對(duì)于數(shù)組類型來說,
可以使用類名 +“[ ].class”來訪問欧芽,如 int[ ].class莉掂。
除此之外,Class 類和 java.lang.reflect 包中還提供了許多返回 Class 對(duì)象的方法千扔。
例如憎妙,對(duì)于數(shù)組類的 Class 對(duì)象,調(diào)用 Class.getComponentType() 方法可以獲得數(shù)組元素的類型曲楚。
我們還可以利用自定義Classloader
來加載我們的類厘唾,然后可以獲取到該類的Class
對(duì)象。類似于下面的代碼龙誊,注意代碼可能會(huì)拋出異常
MyClassLoader classLoader = new MyClassLoader(workspace + "/src/me/mingshan");
Class<?> proxy0Class = classLoader.findClass("$Proxy0");
拿到Class對(duì)象后阅嘶,我們可以進(jìn)行很多操作,比如生成該類的實(shí)例载迄,訪問字段的值讯柔,調(diào)用方法等。
生成類的實(shí)例
通過類的Class對(duì)象可以生成類的實(shí)例护昧,有兩種方式
- 調(diào)用的是無參數(shù)的構(gòu)造函數(shù)進(jìn)行實(shí)例化
clazz.newInstance();
- 可以選擇調(diào)用哪個(gè)構(gòu)造函數(shù)進(jìn)行實(shí)例化魂迄,獲取構(gòu)造器可以傳入?yún)?shù)來選擇
Constructor c = clazz.getConstructor();
Object obj = c.newInstance();
訪問類的成員
通過調(diào)用getFiles()/getgetConstructors()/getMethods()
來訪問該類的成員。同時(shí)我們會(huì)發(fā)現(xiàn)有些方法會(huì)帶上Declared
惋耙,這表示調(diào)用該方法不會(huì)返回父類的成員捣炬。
當(dāng)然我們也可以直接獲取類的某個(gè)成員熊昌,比如成員和方法。
// 獲取字段
Field field = clazz.getField("name");
// 獲取方法
Method method=clazz.getMethod("studyHard", new Class[]{String.class});
還有一點(diǎn)湿酸,我們可以獲取該類實(shí)現(xiàn)的接口
// 獲取該類所實(shí)現(xiàn)的所有接口
Class<?> interfaces[] = clazz.getInterfaces();
對(duì)類成員操作
當(dāng)獲取到類成員后婿屹,我們可以進(jìn)行下一步操作
- 使用
Constructor/Field/Method.setAccessible(true)
來繞開 Java 語言的訪問限制。 - 使用
Constructor.newInstance(Object[])
來生成該類的實(shí)例推溃。 - 使用
Field.get/set(Object)
來訪問字段的值昂利。 - 使用
Method.invoke(Object, Object[])
來調(diào)用方法。
獲取泛型
在Java中泛型有擦除機(jī)制铁坎,那么我們?cè)谶\(yùn)行時(shí)可不可以獲取泛型的具體類型呢蜂奸?答案是可以的。原因是Class類文件結(jié)構(gòu)中有一個(gè)叫Signature
的屬性硬萍。它的作用是存儲(chǔ)一個(gè)方法在字節(jié)碼層面的特征簽名扩所。這個(gè)屬性中保存的參數(shù)參數(shù)類型并不是原生類型,而是包括了參數(shù)化類型的信息朴乖。
獲取成員變量的泛型信息
比如在一個(gè)類Test中祖屏,有一個(gè)成員變量list
,如下:
private List<String> list;
現(xiàn)在我想直接想獲取List的泛型买羞,怎么獲取呢袁勺?實(shí)現(xiàn)代碼如下:
Type t = Test.class.getDeclaredField("list").getGenericType();
if (ParameterizedType.class.isAssignableFrom(t.getClass())) {
for (Type t1 : ((ParameterizedType) t).getActualTypeArguments()) {
System.out.print(t1 + ",");
}
}
由于可能會(huì)有多個(gè)泛型參數(shù),例如Map
哩都,所以返回是一個(gè)數(shù)組。
獲取類的泛型信息
一個(gè)類是泛型類婉徘,在該類中我們可能需要獲取這個(gè)泛型到底是啥漠嵌,然后繼續(xù)進(jìn)行下面的邏輯,一個(gè)應(yīng)用是Hibernate
動(dòng)態(tài)拼接HQL
盖呼,需要知道泛型信息儒鹿。
那么如何操作?代碼如下:
/**
* 通過反射獲取泛型實(shí)例
*/
public class Genericity<T> {
@SuppressWarnings("rawtypes")
protected Class clazz;
@SuppressWarnings("unchecked")
/**
* 把泛型的參數(shù)提取出來的過程放入到構(gòu)造函數(shù)中寫几晤,因?yàn)? * 當(dāng)子類創(chuàng)建對(duì)象的時(shí)候约炎,直接調(diào)用父類的構(gòu)造函數(shù)
*/
public Genericity() {
// 通過反射機(jī)制獲取子類傳遞過來的實(shí)體類的類型信息
ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
//得到t的實(shí)際類型
clazz = (Class<T>) type.getActualTypeArguments()[0];
}
/**
* 獲取指定實(shí)例的所有屬性名及對(duì)應(yīng)值的Map實(shí)例
* @param entity 實(shí)例
* @return 字段名及對(duì)應(yīng)值的Map實(shí)例
*/
protected Map<String, Object> getFieldValueMap(T entity) {
// key是屬性名,value是對(duì)應(yīng)值
Map<String, Object> fieldValueMap = new HashMap<String, Object>();
// 獲取當(dāng)前加載的實(shí)體類中所有屬性
Field[] fields = this.clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field f = fields[i];
// 屬性名
String key = f.getName();
//屬性值
Object value = null;
// 忽略序列化版本ID號(hào)
if (! "serialVersionUID".equals(key)) {
// 取消Java語言訪問檢查
f.setAccessible(true);
try {
value =f.get(entity);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
fieldValueMap.put(key, value);
}
}
return fieldValueMap;
}
}
在上面的代碼中蟹瘾,this.getClass().getGenericSuperclass()
返回表示此 Class
所表示的實(shí)體(類圾浅、接口、基本類型或 void)的直接超類的Type
憾朴,然后將其轉(zhuǎn)換ParameterizedType
狸捕。
getActualTypeArguments()
返回表示此類型實(shí)際類型參數(shù)的 Type 對(duì)象的數(shù)組。
反射Array相關(guān)API
reflect.Array
類位于java.lang.reflect
包下,它是個(gè)反射工具包众雷,全是靜態(tài)方法灸拍。我們可以利用這個(gè)類來對(duì)數(shù)組進(jìn)行操作做祝。
調(diào)用Class.getComponentType()
獲取數(shù)組元素的類型,代碼如下:
int[] arr = {1,2,3,4,5};
Class<?> c = arr.getClass().getComponentType();
假設(shè)我們需要獲取數(shù)組長度鸡岗,用Array
的靜態(tài)方法獲然旎薄:
int len = Array.getLength(arr);
當(dāng)然我們可以用Array
類來創(chuàng)建數(shù)組,向數(shù)組添加元素轩性,修改元素等声登,具體可以參考官方API。
最后推薦一個(gè)反射工具類炮姨,可以參考參考捌刮。