平常的學習工做中,可能用到反射的地方不太多,但看看一些優秀框架的源碼,會發現基本上都離不開反射的使用;所以本篇博文將專一下如何使用java
本片博文佈局以下:框架
反射是什麼,有什麼用,能夠作什麼ide
如何使用反射工具
實例:佈局
指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力性能
直接說定義的話,可能並不能很是清晰的解釋說明,結合做用進行描述學習
反射能夠幹什麼?測試
在運行時構造任意一個類的對象。 在運行時判斷任意一個對象所屬的類。 在運行時判斷任意一個類所具備的成員變量和方法。 在運行時調用任意一個對象的方法
有了上面四點,基本上你想幹嗎就能夠幹嗎,好比我如今就有下面這個類this
public class RefectTest extends MyRefect implements IRefect { private static String s1 = "hello"; private static int s2 = 100; private int s3 = 200; private boolean ans; protected RefectTest next; public RefectTest() { } public RefectTest(int s3, boolean ans, RefectTest next) { this.s3 = s3; this.ans = ans; this.next = next; } public RefectTest next() { return next; } private int count(int a, int b) { return a + b; } }
如今我有了clz,其賦值語句爲 Class clz = RefectTest.class
, 那麼我能夠幹啥?hibernate
建立一個 RefectTest 對象
// 如有默認構造方法 RefectTest instance = clz.newIntance(); // 若須要傳參數 Constructor con = clz.getConstructor(int.class, boolean.class, RefectTest.class); RefectTest instance2 = con.newInstance(10, true, new RefectTest());
判斷父類是不是 MyRefect
// 判斷MyRefect是否爲clz的父類 boolean ans = MyRefect.class.isAssignableFrom(clz);
獲取全部的成員變量
// 獲取全部的成員變量(包括私有的) Field[] fields = clz.getDeclaredFields();
獲取全部的方法
// 獲取全部的成員方法(包括私有方法) Method[] methods = clz.getDeclaredMethods();
上面給出了能夠幹些什麼,並給了對應的簡單示例,引入了幾個新的類Constructor
, Field
, Method
, 下面將詳細解釋這三個類是什麼,怎麼用
努力結合實際的應用場景,給出每種利用反射的實現對應需求的使用姿式,有些場景可能並非特別貼切,歡迎提出給合適的場景以此進行替換
這是個比較常見的場景,我在使用了自定義註解時,一般會這麼晚
應用場景:
我定義了一個校驗器的註解ValDot
,註解中有個校驗規則class對象,以下
public interface ICheckRule { boolean check(Object ... obj); } public class DefaultCheckRule implements ICheckRule { @Override public boolean check(Object... obj) { return false; } } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CheckDot { // 校驗規則 Class<? extends ICheckRule> check() default DefaultCheckRule.class; }
上面定義了註解和校驗條件,接着進入總體,在切面中,須要獲取
@Aspect @Component public class CheckAspect { @Before("@annotation(checkDot)") public void process(JoinPoint joinPoint, CheckDot checkDot) throws IllegalAccessException, InstantiationException { // 注意,這裏獲取註解上的校驗規則類,並獲取實例 ICheckRule rule = checkDot.check().newInstance(); if(rule.check(joinPoint.getArgs())) { throw new IllegalStateException("check argument error!"); } } }
上面是一個較好的利用反射獲取實例的應用場景,想想,若是不用反射,這個校驗規則怎麼傳進來呢,這個時候就沒那麼方便了(固然也不是不能夠,最簡單的就是拿一個Holder持有類名到類對象的映射關係,而後在註解中傳類名,也能夠達到上面的效果)
還有一種場景可能就比較蛋疼了,若是一個類沒有默認構造方法,經過反射就無法直接用class.newInstanace()
了
Constructor
構造器類
根據Class優先獲取到 Constructor
對象,而後傳入須要的構造參數, 測試以下
public class ConTest { private int a,b; public ConTest(int a, int b) { this.a = a; this.b = b; } @Override public String toString() { return "ConTest{" + "a=" + a + ", b=" + b + '}'; } public static void main(String[] args) throws Exception { Class clz = ConTest.class; // 獲取對應的構造器(注意參數類型) Constructor constructor = clz.getConstructor(int.class, int.class); // 建立實例(注意參數要匹配) ConTest test = (ConTest) constructor.newInstance(10, 20); System.out.println(test.toString()); } }
輸出
ConTest{a=10, b=20}
通常經常使用下面四種方式獲取
// 根據參數類型獲取匹配的構造器 Constructor getConstructor(Class[] params) // 獲取全部的 Constructor[] getConstructors() // 相比較前面的,這裏能夠獲取私有方法 Constructor getDeclaredConstructor(Class[] params) // 能夠獲取私有方法 Constructor[] getDeclaredConstructors()
基本類型較爲特殊,因此JDK很人性化的給封裝了一個方法,Class#isPrimitive
所以返回true的類型有:
封裝後的類型,返回的依然是false
<font color="red">附帶一句,是沒有null.class
這種用法的</font>
一般咱們利用 instanceof
關鍵字來判斷繼承關係,可是這個是針對對象來的,如今給一個class,要怎麼玩?
看下面,主要就是 Class#isAssignableFrom()
的功勞了
public class ExtendTest { interface ITest {} abstract class ATest { abstract public void print(); } class TestClz extends ATest implements ITest { @Override public void print() { System.out.println("TestClz"); } } public static void main(String[] args) { Class clz = TestClz.class; System.out.println(ATest.class.isAssignableFrom(clz)); System.out.println(ITest.class.isAssignableFrom(clz)); } }
須要注意一點,父類做爲調用方,子類做爲參數
泛型,又是一個有意思的功能,這裏很少說,繼承一個泛型基類,而後問題是如何經過反射得到泛型簽名中的類型,通常會在繼承或實現泛型接口時會用到它。
class A<T, ID> { } class B extends A<String, Integer> { } public static void main(String[] args) { System.out.println(B.class.getGenericSuperclass()); }
換成泛型接口呢 ?
interface A<T, ID> { } class B implements A<String, Integer> { } public static void main(String[] args) { ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0]; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } }
獲取成員變量,主要是根據 B.class.getDeclaredFields()
來獲取全部聲明的變量,這個應用場景會和下面的獲取方法並執行聯合一塊兒說明
// 獲取指定的公共成員變量 Field getField(String name) // 得到全部公共字段 Field[] getFields() // 獲取指定聲明的成員變量(包括prive) Field getDeclaredField(String name) // 獲取全部聲明的成員變量 Field[] getDeclaredFields()
這個主要返回 Field
對象,如今有了Field,能夠作些啥?
判斷成員的修飾 Field#getModifiers()
int modify = field.getModifiers(); // 是不是靜態變量 boolean ans = Modifier.isStatic(modifier); // 是不是公共變量 boolean ans = Modifier.isPublic(modifier); // 是否不可變 boolean ans = Modifier.isFinal(modifier); // ...
獲取成員的變量名 : field#getName()
獲取成員對應的value: field#get(instance)
獲取註解: field#getAnnotations()
Max
,就能夠設置參數的最大值,其實就是經過反射獲取到註解,而後進行相應的邏輯獲取方法,同上面的差很少,也有四種方式
// 根據方法名,參數類型獲取公共方法 Method getMethod(String name, Class[] params) // 獲取全部的公共方法 Method[] getMethods() // 根據方法名,參數類型,獲取聲明的方法(包括私有) Method getDeclaredMethod(String name, Class[] params) // 獲取全部聲明的方法 Method[] getDeclaredMethods()
返回了一個Method
類,那麼這個東西又有一些什麼功能?
獲取方法名 Method#getName()
獲取方法所在的類 : Method#getDeclaringClass()
獲取方法返回類型 : Method#getReturnType()
獲取方法上的註解 : Method#getAnnotations()
執行方法 有了這個就能夠作不少事情了,實例中給出說明
// 設置方法可訪問(即私有方法也能夠被調用) method.setAccessible(true); // instance爲實例對象, args爲傳入參數 method.invoke(instance, args)
經過反射的方式,實現一個 BeanUtils
,實現Bean的拷貝
當一個Bean有較多的成員變量時,若是咱們採用最原始的setXXX()
來一次賦值的時候,一是實現比較繁瑣,其次就是當Bean的字段發生變更以後,也須要同步的修改,那麼咱們藉助反射的方式,實現一個優雅的 BeanUtils
工具類
public class BeanUtils { public static void copy(Object source, Object dest) throws Exception { Class destClz = dest.getClass(); // 獲取目標的全部成員 Field[] destFields = destClz.getDeclaredFields(); Object value; for (Field field : destFields) { // 遍歷全部的成員,並賦值 // 獲取value值 value = getVal(field.getName(), source); field.setAccessible(true); field.set(dest, value); } } private static Object getVal(String name, Object obj) throws Exception { try { // 優先獲取obj中同名的成員變量 Field field = obj.getClass().getField(name); field.setAccessible(true); return field.get(obj); } catch (NoSuchFieldException e) { // 表示沒有同名的變量 } // 獲取對應的 getXxx() 或者 isXxx() 方法 name = name.substring(0, 1).toUpperCase() + name.substring(1); String methodName = "get" + name; String methodName2 = "is" + name; Method[] methods = obj.getClass().getMethods(); for (Method method : methods) { // 只獲取無參的方法 if (method.getParameterCount() > 0) { continue; } if (method.getName().equals(methodName) || method.getName().equals(methodName2)) { return method.invoke(obj); } } // 沒有匹配到,這裏返回null其實是不合適的 // 由於若是原屬性爲基本數據類型,賦值null爲報錯 throw new Exception(); } }
反射的四種用途
建立一個 RefectTest 對象
// 如有默認構造方法 RefectTest instance = clz.newIntance(); // 若須要傳參數 Constructor con = clz.getConstructor(int.class, boolean.class, RefectTest.class); RefectTest instance2 = con.newInstance(10, true, new RefectTest());
判斷父類是不是 MyRefect
// 判斷MyRefect是否爲clz的父類 boolean ans = MyRefect.class.isAssignableFrom(clz);
獲取全部的成員變量
// 獲取全部的成員變量(包括私有的) Field[] fields = clz.getDeclaredFields();
獲取全部的方法
// 獲取全部的成員方法(包括私有方法) Method[] methods = clz.getDeclaredMethods();
使用注意事項
field.setAccessible(true);
確保可訪問Class#isAssignableFrom()
來判斷類繼承關係Class#isPrimitive()
判斷是否爲基本數據類型Class#getGenericSuperclass()
獲取泛型類型盡信書則不如,已上內容,純屬一家之言,因本人能力通常,看法不全,若有問題,歡迎批評指正