1 反射
1.1 什么是反射
正射:指的是我們知道類的定義和類中的方法名稱,直接先創建對象,然后通過對象去調用方法。例如:
Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);
反射:指的是一開始不知道我要初始化的類對象是什么,自然也無法使用 new 關鍵字來創建對象,需要用JDK 提供的反射 API 進行反射調用。需要通過類的路徑字符串創建對象,通過方法名稱字符串調用方法。例如:
Class clz = Class.forName("com.chenshuyi.reflect.Apple");//通過字符串獲取類Class
Constructor constructor = clz.getConstructor();//獲取類的構造器
Object object = constructor.newInstance();//創建對象
Method method = clz.getMethod("setPrice", int.class);//通過方法名獲取方法
method.invoke(object, 4);//調用方法;
第一段代碼在未運行時就已經確定了要運行的類(Apple),而第二段代碼則是在運行時通過字符串值才得知要運行的類(com.chenshuyi.reflect.Apple)。反射就是在運行時才知道要操作的類是什么,並且可以在運行時獲取類的完整構造,並調用對應的方法。
1.2 反射獲取類、構造器、方法、屬性
(1)獲取類
使用 Class.forName 靜態方法。當你知道該類的全路徑名時,你可以使用該方法獲取 Class 類對象。
Class clz = Class.forName("java.lang.String");
(2)獲取構造器
通過 Constructor 對象的 newInstance() 方法
Constructor constructor = clz.getConstructor();//獲取類的構造器
Object object = constructor.newInstance();//創建對象
(1) 獲取方法
獲取方法的 Method 對象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
利用 invoke 方法調用方法
setPriceMethod.invoke(appleObj, 14);
(2) 獲取屬性
過 Class 對象的 getFields() 方法可以獲取 Class 類的屬性,但無法獲取私有屬性。
Class clz = Apple.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
輸出結果是:
Price
(5)獲取包含私有屬性
而如果使用 Class 對象的 getDeclaredFields() 方法則可以獲取包括私有屬性在內的所有屬性:
Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
輸出結果是:
name
price
1.3 反射的源碼解析
(1)Method的invoke方法
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
(2)acquireMethodAccessor 返回MethodAccessor對象
private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it
// if so
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);
}
return tmp;
}
(3)反射工廠返回對象reflectionFactory.newMethodAccessor
public MethodAccessor newMethodAccessor(Method var1) {
checkInitted();
if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
} else {
NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
var2.setParent(var3);
return var3;// 最終返回的是DelegatingMethodAccessorImpl對象
}
}
其中DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
是將NativeMethodAccessorImpl var2對象作為入參,傳入DelegatingMethodAccessorImpl的構造函數。那么構造函數內部做了什么?很關鍵!
DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {
this.setDelegate(var1);對象設置在Delegate屬性中,后面會用到
}
delegate 屬性的定義是private MethodAccessorImpl delegate;
(4)步驟(3)最終返回的是DelegatingMethodAccessorImpl對象,所以步驟(1)中return ma.invoke(obj, args);函數invoke是DelegatingMethodAccessorImpl的方法。看看DelegatingMethodAccessorImpl的invoke方法定義如下:
public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
return this.delegate.invoke(var1, var2);//調用了屬性delegate的invoke方法;也就是NativeMethodAccessorImpl對象的invoke方法; }
(5)NativeMethodAccessorImpl對象的invoke實現
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);
}
每次NativeMethodAccessorImpl.invoke()方法被調用時,都會增加一個調用次數計數器numInvocations,看超過閾值 沒有;一旦超過,則調用MethodAccessorGenerator.generateMethod()來生成Java版的 MethodAccessor的實現類,並且通過this.parent.setDelegate(var3);改變DelegatingMethodAccessorImpl所引用的MethodAccessor為 Java版。后續經由DelegatingMethodAccessorImpl.invoke()調用到的就是Java版的實現了。
注意到關鍵的invoke0()方法是個native方法。它在HotSpot VM里是由JVM_InvokeMethod()函數所支持的,不能在深入了:
JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0
(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
{
return JVM_InvokeMethod(env, m, obj, args);
}
為什么要這樣先用native后又切換為java的呢?
就像注釋里說的,實際的 MethodAccessor 實現有兩個版本,一個是 Native 版本NativeMethodAccessorImpl,一個是 Java 版本MethodAccessorImpl。Java版本在第一次啟動時,需要加載字節碼實現Method.invoke() 和 Constructor.newInstance() ,比較耗時。所以Native 版本第一次啟動比java版本快3-4倍,但是后面的調用java版本比Native快20倍。所以為了避免啟動慢,第一次使用native版本快速啟動。為了避免后續運行慢,在切換到java版本MethodAccessorImpl 對象去實現反射。
自己開發了一個股票智能分析軟件,功能很強大,需要的點擊下面的鏈接獲取:
https://www.cnblogs.com/bclshuai/p/11380657.html
百度雲盤下載地址:
鏈接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg
提取碼:mc8l
微信公眾號獲取最新的軟件和視頻介紹
QStockView