java反射機制之Method invoke執行調用方法例子php
昨天在羣裏跟你們討論了下java反射調用可變參數的問題,這個問題原由是咱們須要反射調用另外一個部門提供的方法,我同事說java不能反射調用可變參數的方法,因而我寫了個demo證實了他這個觀點的錯誤。可是測試過程當中,有一點我不明白,就是反射調用可變參數的方法時,爲何必定要保證傳入的參數數組長度爲1,在羣裏跟你們討論了不少,沒有獲得確切的答案,參照網上大牛寫的東西和我本身跟源碼的過程,記錄以下:html
1.兩個類,一個父類,一個子類java
[java] view plain copy print?數組
package com.reflect.test;緩存
public class BaseObject {ide
public void getObjectName(){函數
System.out.println("BaseObject");性能
}測試
}優化
[java] view plain copy print?
package com.reflect.test;
public class SubObject extends BaseObject{
@Override
public void getObjectName() {
System.out.println("SubObject");
}
public void getParamsLength(String...params){
System.out.println("param's length is:"+params.length);
}
public void getParamsLength(String param1,String param2){
System.out.println(param1 + "-" + param2);
}
}
2.測試類,主要測試重載方法的調用、可變參數方法的調用、定參方法的調用
[java] view plain copy print?
package com.reflect.test;
import java.lang.reflect.Method;
public class ReflectTest {
private static final String BASE_OBJECT_PATH = "com.reflect.test.BaseObject";
private static final String SUB_OBJECT_PATH = "com.reflect.test.SubObject";
public static void main(String[] args) throws Exception{
Class bClazz = Class.forName(BASE_OBJECT_PATH);
Class sClazz = Class.forName(SUB_OBJECT_PATH);
Object bObj = bClazz.newInstance();//父類實例
Object sObj = sClazz.newInstance();//子類實例
//1.反射調用子類父類的重載方法
//多態+動態綁定
Method bMethod = bClazz.getDeclaredMethod("getObjectName");
bMethod.invoke(bObj);//父類的bMethod調用父類的getObjectName()
bMethod.invoke(sObj);//父類的bMethod調用子類的getObjectName();
Method sMethod = sClazz.getDeclaredMethod("getObjectName");
//不符合多態和動態綁定
//sMethod.invoke(bObj);//sMethod調用父類的getObjectName(),會報錯:java.lang.IllegalArgumentException: object is not an instance of declaring class
sMethod.invoke(sObj);
//2.反射調用可變參數的方法
Method changeMethod = sClazz.getDeclaredMethod("getParamsLength", String[].class);
//可變參數必須這樣封裝,由於java反射內部實現作了參數個數爲1的判斷,若是參數長度不爲1,則會拋出異常
String[] strParams = {"a","b","c"};
Object[] cParams = {strParams};
changeMethod.invoke(sObj, cParams);
//3.反射調用固定長度參數的方法
Method unChangeMethod1 = sClazz.getDeclaredMethod("getParamsLength", String.class,String.class);
unChangeMethod1.invoke(sObj, "Hello","Java");
//也能夠寫成這樣
Class[] clazzs = {String.class,String.class};
Method unChangeMethod2 = sClazz.getDeclaredMethod("getParamsLength", clazzs);
unChangeMethod2.invoke(sObj, "Hello","Java");
//下面的這種調用形式也是能夠的,不過會報警告
//String[] params1 = {"Hello","Java"};
//unChangeMethod1.invoke(sObj, params1);
}
}
下面是JDK裏面Method 的invoke方法的源碼
從代碼中能夠看出,先檢查 AccessibleObject的override屬性是否爲true(override屬性默認爲false)。AccessibleObject是Method,Field,Constructor的父類,可調用setAccessible方法改變,若是設置爲true,則表示能夠忽略訪問權限的限制,直接調用。
若是不是ture,則要進行訪問權限檢測。用Reflection的quickCheckMemberAccess方法先檢查是否是public的,若是不是再用Reflection.getCallerClass()方法得到到調用這個方法的Class,而後作是否有權限訪問的校驗,校驗以後緩存一次,以便下次若是仍是這個類來調用就不用去作校驗了,直接用上次的結果。
[java] view plain copy print?
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
// Until there is hotspot @CallerSensitive support
// can't call Reflection.getCallerClass() here
// Workaround for now: add a frame getCallerClass to
// make the caller at stack depth 2
Class caller = getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
/驗證的代碼,securityCheckCache就是JDK作的緩存
volatile Object securityCheckCache;
void checkAccess(Class caller, Class clazz, Object obj, int modifiers)
throws IllegalAccessException
{
if (caller == clazz) { // quick check
return; // ACCESS IS OK
}
Object cache = securityCheckCache; // read volatile
Class targetClass = clazz;
if (obj != null
&& Modifier.isProtected(modifiers)
&& ((targetClass = obj.getClass()) != clazz)) {
// Must match a 2-list of { caller, targetClass }.
if (cache instanceof Class[]) {
Class[] cache2 = (Class[]) cache;
if (cache2[1] == targetClass &&
cache2[0] == caller) {
return; // ACCESS IS OK
}
// (Test cache[1] first since range check for [1]
// subsumes range check for [0].)
}
} else if (cache == caller) {
// Non-protected case (or obj.class == this.clazz).
return; // ACCESS IS OK
}
// If no return, fall through to the slow path.
slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
}
而後就是調用MethodAccessor的invoke方法了。
調用MethodAccessor的invoke方法。每一個Method對象包含一個root對象,root對象裏持有一個MethodAccessor對象。這個對象由ReflectionFactory方法生成,ReflectionFactory對象在Method類中是static final的由native方法實例化。代碼片斷以下;
[java] view plain copy print?
//Method類中的代碼片斷,生成MethodAccessor
private volatile MethodAccessor methodAccessor;
private Method root;
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;
}
// reflectionFactory在父類AccessibleObject中定義,代碼片斷以下:
static final ReflectionFactory reflectionFactory =
AccessController.doPrivileged(
new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
ReflectionFactory生成MethodAccessor:若是noInflation的屬性爲true則直接返回MethodAccessorGenerator建立的一個MethodAccessor,不然返回DelegatingMethodAccessorImpl,並將他與一個NativeMethodAccessorImpl互相引用。但DelegatingMethodAccessorImpl執行invoke方法的時候又委託給NativeMethodAccessorImpl了。代碼片斷以下:
[java] view plain copy print?
public MethodAccessor newMethodAccessor(Method paramMethod) {
checkInitted();
if (noInflation) {
return new MethodAccessorGenerator().generateMethod(paramMethod.getDeclaringClass(), paramMethod.getName(), paramMethod.getParameterTypes(), paramMethod.getReturnType(), paramMethod.getExceptionTypes(), paramMethod.getModifiers());
}
NativeMethodAccessorImpl localNativeMethodAccessorImpl = new NativeMethodAccessorImpl(paramMethod);
DelegatingMethodAccessorImpl localDelegatingMethodAccessorImpl = new DelegatingMethodAccessorImpl(localNativeMethodAccessorImpl);
localNativeMethodAccessorImpl.setParent(localDelegatingMethodAccessorImpl);
return localDelegatingMethodAccessorImpl;
}
MethodAccessor實現有兩個版本,一個是Java實現的,另外一個是native code實現的。Java實現的版本在初始化時須要較多時間,但長久來講性能較好;native版本正好相反,啓動時相對較快,但運行時間長了以後速度就比不過Java版了。這是HotSpot的優化方式帶來的性能特性,同時也是許多虛擬機的共同點:跨越native邊界會對優化有阻礙做用,它就像個黑箱同樣讓虛擬機難以分析也將其內聯,因而運行時間長了以後反而是託管版本的代碼更快些。 爲了權衡兩個版本的性能,Sun的JDK使用了「inflation」的技巧:讓Java方法在被反射調用時,開頭若干次使用native版,等反射調用次數超過閾值時則生成一個專用的MethodAccessor實現類,生成其中的invoke()方法的字節碼,之後對該Java方法的反射調用就會使用Java版。
看下NativeMethodAccessorImpl 中的invoke方法:
代碼片斷以下:
[java] view plain copy print?
package sun.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class NativeMethodAccessorImpl extends MethodAccessorImpl
{
private Method method;
private DelegatingMethodAccessorImpl parent;
private int numInvocations;
NativeMethodAccessorImpl(Method paramMethod)
{
this.method = paramMethod;
}
public Object invoke(Object paramObject, Object[] paramArrayOfObject)
throws IllegalArgumentException, InvocationTargetException
{
if (++this.numInvocations > ReflectionFactory.inflationThreshold()) {
MethodAccessorImpl localMethodAccessorImpl = (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(localMethodAccessorImpl);
}
return invoke0(this.method, paramObject, paramArrayOfObject);
}
void setParent(DelegatingMethodAccessorImpl paramDelegatingMethodAccessorImpl) {
this.parent = paramDelegatingMethodAccessorImpl;
}
private static native Object invoke0(Method paramMethod, Object paramObject, Object[] paramArrayOfObject);
}
調用natiave方法invoke0執行方法調用.
注意這裏有一個計數器numInvocations,每調用一次方法+1,當比 ReflectionFactory.inflationThreshold(15)大的時候,用MethodAccessorGenerator建立一個MethodAccessor,並把以前的DelegatingMethodAccessorImpl引用替換爲如今新建立的。下一次DelegatingMethodAccessorImpl就不會再交給NativeMethodAccessorImpl執行了,而是交給新生成的java字節碼的MethodAccessor
每次NativeMethodAccessorImpl.invoke()方法被調用時,都會增長一個調用次數計數器,看超過閾值沒有;一旦超過,則調用MethodAccessorGenerator.generateMethod()來生成Java版的MethodAccessor的實現類,而且改變DelegatingMethodAccessorImpl所引用的MethodAccessor爲Java版。後續經由DelegatingMethodAccessorImpl.invoke()調用到的就是Java版的實現了。
注意到關鍵的invoke0()方法是個native方法。它在HotSpot VM裏是由JVM_InvokeMethod()函數所支持的,是用C寫的
爲了驗證這個結論,我故意寫出一個非法參數,循環調用16次並catch下異常,結果以下:從結果中看出,前15次都是調用NativeMethodAccessorImpl,第16次開始就是調用DelegatingMethodAccessorImpl了。
[java] view plain copy print?
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
java.lang.IllegalArgumentException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.reflect.test.ReflectTest.main(ReflectTest.java:44)
下面看看java版的DelegatingMethodAccessorImpl的實現:
[java] view plain copy print?
package sun.reflect;
import java.lang.reflect.InvocationTargetException;
class DelegatingMethodAccessorImpl extends MethodAccessorImpl
{
private MethodAccessorImpl delegate;
DelegatingMethodAccessorImpl(MethodAccessorImpl paramMethodAccessorImpl)
{
setDelegate(paramMethodAccessorImpl);
}
public Object invoke(Object paramObject, Object[] paramArrayOfObject)
throws IllegalArgumentException, InvocationTargetException
{
return this.delegate.invoke(paramObject, paramArrayOfObject);
}
void setDelegate(MethodAccessorImpl paramMethodAccessorImpl) {
this.delegate = paramMethodAccessorImpl;
}
package sun.reflect;
public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
public GeneratedMethodAccessor1() {
super();
}
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException {
// prepare the target and parameters
if (obj == null) throw new NullPointerException();
try {
A target = (A) obj;
if (args.length != 1) throw new IllegalArgumentException();
String arg0 = (String) args[0];
} catch (ClassCastException e) {
throw new IllegalArgumentException(e.toString());
} catch (NullPointerException e) {
throw new IllegalArgumentException(e.toString());
}
// make the invocation
try {
target.foo(arg0);
} catch (Throwable t) {
throw new InvocationTargetException(t);
}
}
}
if (args.length != 1) throw new IllegalArgumentException();這一句就能解釋我以前的疑問了,這塊會判斷參數數組的長度,若是長度不等於1,就會拋出非法參數的異常。
並且MethodAccessor會作強制類型轉換再進行方法調用,但父類強制轉化成子類的的時候就會報錯類型不匹配錯誤了,因此若是變量的引用聲明是父但實際指向的對象是子,那麼這種調用也是能夠的。現貨重油 娛樂及時看
http://bj.bqqm.com/licai/z746373.htm
http://bj.bqqm.com/licai/z746380.htm
http://bj.bqqm.com/licai/z746385.htm
http://blog.chinaunix.net/uid-30065054-id-5753598.html
http://blog.chinaunix.net/uid-30065054-id-5753619.html
http://blog.csdn.net/sinat_36394053/article/details/52857375
http://blog.itpub.net/30065054/viewspace-2126715/
http://blog.itpub.net/30065054/viewspace-2126747/
http://run.gmw.cn/blog-1873077-177613.html
http://site.leshou.com/s/33563826.html
http://site.leshou.com/s/33563870.html
http://site.leshou.com/s/33563896.html
http://site.leshou.com/s/33563950.html
http://site.leshou.com/s/33564004.html
http://site.leshou.com/s/33564069.html
http://site.leshou.com/s/33564112.html
http://site.leshou.com/s/33564134.html
http://site.leshou.com/s/33564165.html
http://site.leshou.com/s/33564197.html
http://www.365128.com/user/myx2/39.html
http://www.365128.com/user/myx2/40.html
http://www.365128.com/user/myx2/41.html
http://www.365128.com/user/myx2/43.html
http://www.365128.com/user/myx2/46.html
http://www.fwol.cn/xinxishow.php?xid=4084018
http://www.fwol.cn/xinxishow.php?xid=4084024
http://www.fwol.cn/xinxishow.php?xid=4084028
http://www.fwol.cn/xinxishow.php?xid=4084032
http://www.fwol.cn/xinxishow.php?xid=4084048
http://www.niuche.com/article/1140677.html
http://www.togv.net/guanlipeixun/j73tzgggg9a7d5neldji.htm
http://www.togv.net/guanlipeixun/m0e3b9999hgzdusvk43t.htm
http://www.wangchao.net.cn/hi/detail_232429.html
http://www.wangchao.net.cn/hi/detail_232433.html
http://www.wangchao.net.cn/hi/detail_232437.html
http://www.wangchao.net.cn/hi/detail_232438.html
http://www.wangchao.net.cn/hi/detail_232441.html
http://www.wangchao.net.cn/hi/detail_232591.html
http://www.wangchao.net.cn/hi/detail_232592.html
http://www.wangchao.net.cn/hi/detail_232599.html
http://www.wangchao.net.cn/hi/detail_232606.html
http://www.wangchao.net.cn/hi/detail_232607.html
http://www.yl1001.com/article/3151476866647599.htm
http://mingjia.cngold.org/expert/1304176/news/c800377.htm
http://mingjia.cngold.org/expert/1304176/news/c800383.htm
http://mingjia.cngold.org/expert/1304176/news/c800390.htm
http://mingjia.cngold.org/expert/1304176/news/c800397.htm
http://mingjia.cngold.org/expert/1304176/news/c800406.htm
http://mingjia.cngold.org/expert/1304176/news/c800410.htm
http://www.cnblogs.com/huqingchen/p/5979936.html