在不少狀況下,咱們知道如何編寫反射相關類或者方法,可是沒法口述反射是什麼,裏面的機制是什麼,下面我先如淺入深介紹反射。設計模式
反射 (Reflection) 是 Java 的特徵之一,它容許運行中動態加載Java 程序獲取自身的信息,而且能夠操做類或對象的內部屬性,是一種功能強大且複雜的機制,能夠用在下面四個方面:數組
上面是正式的反射定義,比較抽象,用個大白話來講,反射就是動態加載類、調用方法和訪問屬性,它不須要事先知道運行對象是誰。反射的重點在運行時而不是編譯時,也就是動態加載。下面咱們先來看下一個簡單的例子bash
// 普通new
User user = new User();
user.setEmail("xxxx@xxx.com");
// 反射例子
try {
Class clz = Class.forName("com.fomin.demo.model.User");
Method method = clz.getMethod("setEmail", String.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, "aaa@xx.com");
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
| InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
複製代碼
上面須要一個User實例,正常狀況下,咱們只須要new一個實例就行,但咱們也會遇到一些特殊狀況,我不知道初始化的類對象是什麼,沒法使用new進行實例化,這就須要在動態運行時才須要建立一個實例。框架
Java的反射機制的實現要藉助於4個類:Class,Constructor,Field,Method,而且若是須要建立實例,可使用newInstance建立,例如,clz.newInstance或者constructor.newInstance
複製代碼
//使用 Class.forName 靜態方法
Class clz = Class.forName("com.fomin.demo.model.User");
//使用 .class 方法
Class clz = User.class;
//使用類對象的 getClass() 方法
User user = new User();
Class clz = user.getClass();
複製代碼
//獲取指定參數得到public構造函數
Constructor<T> getConstructor(Class<?>... parameterTypes);
//根據指定參數獲取public和非public的構造函數
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
複製代碼
//獲取類的全部聲明字段,但不能獲得其父類的成員變量,包含私有的
Field[] getDeclaredFields()
//獲取指定參數的類的全部聲明字段,但不能獲得其父類的成員變量,包含私有的
Field getDeclaredField(String name)
//獲取類的全部公有聲明字段,但不能獲得其父類的成員變量
Field[] getFields()
//獲取指定參數的類的全部公有聲明字段,但不能獲得其父類的成員變量
Field getField(String name)
複製代碼
//獲取類的全部公用(public)方法,包括其繼承類的公用方法
Method[] getMethods()
//獲取特定的公用(public)方法,第一個參數爲方法名稱,後面的參數爲方法的參數對應Class的對象
Method getMethod(String name, Class<?>... parameterTypes)
//獲取類或接口聲明的全部方法,包括公共、保護、默認訪問和私有方法,不包括繼承方法
Method[] getDeclaredMethods()
//獲取特定的方法,第一個參數爲方法名稱,後面的參數爲方法的參數對應Class的對象
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
複製代碼
屬於Method的一個方法,用來反射執行方法的,包含兩個參數,一個是Object obj表示被調用方法底層所屬對象,Object... args表示調用方法是傳遞的實際參數。 invoke中核心在MethodAccessorImpl,NativeMethodAccessorImpl,DelegatingMethodAccessorImpl這三個類中,其中NativeMethodAccessorImpl和DelegatingMethodAccessorImpl繼承了抽象類MethodAccessorImpl。而且 NativeMethodAccessorImpl 對象交給 DelegatingMethodAccessorImpl 對象代理。 在執行invoke時,首先須要對非重寫方法檢查權限,對非public方法,進行二次檢查checkAccess。ide
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
複製代碼
獲取當前的MethodAccessor,若是是空的,調用acquireMethodAccessor建立,首先先從root獲取,仍是爲空調用ReflectionFactory.newMethodAccessor建立一個,並設置在root中。模塊化
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
複製代碼
最後調用NativeMethodAccessorImpl中的native方法invoke0函數
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
this.parent.setDelegate(var3);
}
return invoke0(this.method, var1, var2);
}
void setParent(DelegatingMethodAccessorImpl var1) {
this.parent = var1;
}
private static native Object invoke0(Method var0, Object var1, Object[] var2);
複製代碼
反射使用的地方不少,例如AOP思想就是須要用到發射、模塊化的開發,經過反射去調用對應的字節碼;動態代理設計模式也採用了反射機制, Spring/Hibernate 等框架。 優勢:性能
缺點:ui