java反射的用法
對於衆多java應用程序員來說,java反射也許是個即熟悉又陌生的東西,說熟悉是經常會聽到“反射”這個字眼,說陌生是因爲寫應用程序並不怎麼直接跟反射打交道,更多地用到反射技術的往往是一些稍底層一點的東西,像一些框架之類的。那麼什麼是反射呢?
一、定義
先來看看官方的說法
Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine.
反射是由應用程序使用,用於檢查或修改在Java虛擬機中運行的應用程序的運行時行爲。
這個說法感覺還是很抽象的,我查了一些資料,結合對API的理解,用自己的話來解釋一下什麼是反射(Reflection)。反射是一種編程語言的能力,或者通俗地講它是一種技術,什麼樣的技術呢?它可以在程序運行的時候獲取類的構造器、方法、屬性等元素,並可以賦值或調用類的方法。(貌似只有java纔有這種能力,其它語言都沒有,這是我猜的 - 參考),那麼這種能力有什麼用呢?
二、作用
我認爲是爲了我們的程序更具靈活性,比如在使用運行時註解時就要用到反射來檢查類或類的元素有沒添加註解,如果添加了,添加的是什麼註解。然後再根據檢查結果作出不同的處理。
除了運行時註解,反射還被用在動態代理上,以及無法通過new來創建對象的場合。
三、用法
下面來看看在代碼中怎麼使用。通常情況下跟反射相關的類有以下幾個:
java.lang.Class
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method
如果涉及到泛型和數組,還會用到Type接口和Array類,這兩個東西會複雜點。
java.lang.reflect.Type
java.lang.reflect.Array
先來看看Class類,Class類是一切反射操作的入口,無論是想要獲取一個類的構造器,還是類的屬性,或是一個類的方法都要先有這個類的Class實例,獲取Class實例的方法通常有以下幾種:
// 一 :通過getClass()方法
String str="hello world";
Class<?> clazz1 = str.getClass();
// 二:通過類的class屬性
Class<?> clazz2 = String.class;
// 三:通過Class類的靜態方法forName(String className)
Class<?> clazz3 = Class.forName("java.lang.String");
有了Class實例,就像演登上了舞臺,可以盡情地表演了,看幾個常用的api:
跟構造器相關的:
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共構造方法。
Constructor<?>[] getConstructors()
返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公共構造方法。
Constructor<?>[] getDeclaredConstructors()
返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法。
跟成員變量相關的:
Field getDeclaredField(String name)
返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。
Field[] getDeclaredFields()
返回 Field 對象的一個數組,這些對象反映此 Class 對象所表示的類或接口所聲明的所有字段。
Field getField(String name)
返回一個 Field 對象,它反映此 Class 對象所表示的類或接口的指定公共成員字段。
Field[] getFields()
返回一個包含某些 Field 對象的數組,這些對象反映此 Class 對象所表示的類或接口的所有可訪問公共字段。
跟方法相關的:
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法。
Method[] getDeclaredMethods()
返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的所有方法,包括公共、保護、默認(包)訪問和私有方法,但不包括繼承的方法。
Method getMethod(String name, Class<?>... parameterTypes)
返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法。
Method[] getMethods()
返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法。
API就不多列了,下面寫個例子來感受一下,先寫個類,私有公有的字段、構造方法、方法,以及帶參數不帶參數的方法,這裏沒有涉及到泛型和數組,今後有空的話再補上:
/**
* 用於理解反射的類
*
* @author Huangming 2019/6/22
*/
public class TestReflection {
private String privateField;
public String publicField;
private TestReflection() {
System.out.println("I am from private construct");
}
public TestReflection(String a, String b) {
System.out.println("I am from public construct");
System.out.println("參數是:{" + a + ", " + b + "}");
this.privateField = a;
this.publicField = b;
}
private void privateMethod() {
System.out.println("I am from private method");
}
public void publicMethod(String s, int i) {
System.out.println("I am from public method");
System.out.println("參數是:{" + s + "," + i + "}");
}
}
接下來編寫工具類,反射的相關操作就寫在util類裏面,
/**
* 反射相關操作的類
*
* @author Huangming 2019/6/22
*/
public class ReflectionUtil {
public Object parseConstructor(Class clazz) {
Constructor[] constructors = clazz.getDeclaredConstructors();
Object instance = null;
try {
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
int modifier = constructor.getModifiers();
System.out.println("構造方法 " + constructor.getName() + " 的修飾符是" + Modifier.toString(modifier));
if (!constructor.isAccessible()) {
// 注意,isAccessible()的返回值並不代表是否可訪問,具體看setAccessible(boolean flag)源碼上的註釋
constructor.setAccessible(true);
}
// 取構造函數的參數
Class[] parameterTypes = constructor.getParameterTypes();
for (int m = 0; m < parameterTypes.length; m++) {
Class p = parameterTypes[m];
System.out.println("它的第" + (m + 1) + "個參數類型爲" + p.getName());
}
if (parameterTypes.length == 0) {
// 無參構造函數
System.out.println("它沒有參數");
System.out.println("使用反射調用無參構造函數。。");
constructor.newInstance();
} else {
System.out.println("使用反射調用有參構造函數。。");
// 這裏省略了創建參數的步驟(包括參數類型、參數個數的判斷)
instance = constructor.newInstance("a", "b");
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return instance;
}
public void parseFields(Object object, Class clazz) {
Field[] fields = clazz.getDeclaredFields();
try {
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
int modifier = field.getModifiers();
System.out.println("成員變量 " + field.getName() + " 的修飾符是" + Modifier.toString(modifier));
if (!field.isAccessible()) {
// 注意,isAccessible()的返回值並不代表是否可訪問,具體看setAccessible(boolean flag)源碼上的註釋
field.setAccessible(true);
}
System.out.println("用反射讀取成員變量的值:" + field.get(object).toString());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public void parseMethods(Object object, Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
int modifier = method.getModifiers();
System.out.println("方法 " + method.getName() + " 的修飾符是" + Modifier.toString(modifier));
if (!method.isAccessible()) {
// 注意,isAccessible()的返回值並不代表是否可訪問,具體看setAccessible(boolean flag)源碼上的註釋
method.setAccessible(true);
}
// 取方法的參數
Class[] parameterTypes = method.getParameterTypes();
for (int m = 0; m < parameterTypes.length; m++) {
Class p = parameterTypes[m];
System.out.println("它的第" + (m + 1) + "個參數類型爲" + p.getName());
}
System.out.println("用反射調用該方法");
if (parameterTypes.length == 0) {
try {
method.invoke(object, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} else {
try {
// 這裏省略了創建參數的步驟(包括參數類型、參數個數的判斷)
method.invoke(object, "abc", 999);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
最後寫測試類:
/**
* @author Huangming 2019/6/19
*/
public class Main {
public static void main(String[] args) {
try {
Class c = Class.forName("com.example.proxyinjava.TestReflection");
ReflectionUtil util = new ReflectionUtil();
Object instance = util.parseConstructor(c);
util.parseFields(instance, c);
util.parseMethods(instance, c);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
輸出結果:
構造方法 com.example.proxyinjava.TestReflection 的修飾符是private
它沒有參數
使用反射調用無參構造函數。。
I am from private construct
構造方法 com.example.proxyinjava.TestReflection 的修飾符是public
它的第1個參數類型爲java.lang.String
它的第2個參數類型爲java.lang.String
使用反射調用有參構造函數。。
I am from public construct
參數是:{a, b}
成員變量 privateField 的修飾符是private
用反射讀取成員變量的值:a
成員變量 publicField 的修飾符是public
用反射讀取成員變量的值:b
方法 privateMethod 的修飾符是private
用反射調用該方法
I am from private method
方法 publicMethod 的修飾符是public
它的第1個參數類型爲java.lang.String
它的第2個參數類型爲int
用反射調用該方法
I am from public method
參數是:{abc,999}
總結一下:
反射相關的api並不是太難,卻是很重要的一個工具,瞭解了它的使用,對今後的註解等其它內容會更容易理解。知識都是串起來的,如果哪天讀某個框架的源碼時發現用到了反射,你要是不理解的話,那麼對那個框架的理解也會大打折扣。我的感悟,不要小看了基礎的東西。
由於水平有限,如果文中存在錯誤之處,請大家批評指正,歡迎大家一起來分享、探討!
博客:http://blog.csdn.net/MingHuang2017
GitHub:https://github.com/MingHuang1024
Email: [email protected]
微信:724360018