详解Java中Method的Invoke方法

在写代码的时候,发现从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错。虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码。 
Method的invoke方法

1.先检查 AccessibleObject的override属性是否为true。

AccessibleObject是Method,Field,Constructor的父类,override属性默认为false,可调用setAccessible方法改变,如果设置为true,则表示可以忽略访问权限的限制,直接调用。

2.如果不是ture,则要进行访问权限检测。用Reflection的quickCheckMemberAccess方法先检查是不是public的,如果不是再用Reflection.getCallerClass(1)方法获

得到调用这个方法的Class,然后做是否有权限访问的校验,校验之后缓存一次,以便下次如果还是这个类来调用就不用去做校验了,直接用上次的结果,(很奇怪用这种方式缓存,因为这种方式如果下次换个类来调用的话,就不用会缓存了,而再验证一遍,把这次的结果做为缓存,但上一次的缓存结果就被冲掉了。这是一个很简单的缓冲机制,只适用于一个类的重复调用)。

3.调用MethodAccessor的invoke方法。每个Method对象包含一个root对象,root对象里持有一个MethodAccessor对象。我们获得的Method独享相当于一个root对象的镜像,所有这类Method共享root里的MethodAccessor对象,(这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是static final的由native方法实例化)。

ReflectionFactory生成MethodAccessor:如果noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个MethodAccessor。否则返回DelegatingMethodAccessorImpl,并将他与一个NativeMethodAccessorImpl互相引用。但DelegatingMethodAccessorImpl执行invoke方法的时候又委托给NativeMethodAccessorImpl了。

再一步深入

4.NativeMethodAccessorImpl的invkoe方法:

调用natiave方法invoke0执行方法调用.

注意这里有一个计数器numInvocations,每调用一次方法+1,当比 ReflectionFactory.inflationThreshold(15)大的时候,用MethodAccessorGenerator创建一个MethodAccessor,并把之前的DelegatingMethodAccessorImpl引用替换为现在新创建的。下一次DelegatingMethodAccessorImpl就不会再交给NativeMethodAccessorImpl执行了,而是交给新生成的java字节码的MethodAccessor。

MethodAccessorGenerator使用了asm字节码动态加载技术,暂不深入研究。

总结 一个方法可以生成多个Method对象,但只有一个root对象,主要用于持有一个MethodAccessor对象,这个对象也可以认为一个方法只有一个,相当于是static的。因为Method的invoke是交给MethodAccessor执行的,所以我所想要知道的答案在MethodAccessor的invoke中,深入MethodAccessor:

------------------------------------------MethodAccessor---------------------------------
假如有这么一个类A:

public class A {
  public void foo(String name) {
    System.out.println("Hello, " + name);
  }
}

可以编写另外一个类来反射调用A上的方法:

import java.lang.reflect.Method;
public class TestClassLoad {
  public static void main(String[] args) throws Exception {
    Class<?> clz = Class.forName("A");
    Object o = clz.newInstance();
    Method m = clz.getMethod("foo", String.class);
    for (int i = 0; i < 16; i++) {
      m.invoke(o, Integer.toString(i));
    }
  }
}

注意到TestClassLoad类上不会有对类A的符号依赖——也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到main()方法执行到调用Class.forName()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类。……好吧这个细节跟主题没啥关系。

回到主题。这次我的测试环境是Sun的JDK 1.6.0 update 13 build 03。编译上述代码,并在执行TestClassLoad时加入-XX:+TraceClassLoading参数(或者-verbose:class或者直接-verbose都行),如下:

控制台命令

java -XX:+TraTestClassLoad  ceClassLoading

可以看到输出了一大堆log,把其中相关的部分截取出来如下:

[Loaded TestClassLoad from file:/D:/temp_code/test_java_classload/]
[Loaded A from file:/D:/temp_code/test_java_classload/]
[Loaded sun.reflect.NativeMethodAccessorImpl from shared objects file]
[Loaded sun.reflect.DelegatingMethodAccessorImpl from shared objects file]
Hello, 0
Hello, 1
Hello, 2
Hello, 3
Hello, 4
Hello, 5
Hello, 6
Hello, 7
Hello, 8
Hello, 9
Hello, 10
Hello, 11
Hello, 12
Hello, 13
Hello, 14
[Loaded sun.reflect.ClassFileConstants from shared objects file]
[Loaded sun.reflect.AccessorGenerator from shared objects file]
[Loaded sun.reflect.MethodAccessorGenerator from shared objects file]
[Loaded sun.reflect.ByteVectorFactory from shared objects file]
[Loaded sun.reflect.ByteVector from shared objects file]
[Loaded sun.reflect.ByteVectorImpl from shared objects file]
[Loaded sun.reflect.ClassFileAssembler from shared objects file]
[Loaded sun.reflect.UTF8 from shared objects file]
[Loaded java.lang.Void from shared objects file]
[Loaded sun.reflect.Label from shared objects file]
[Loaded sun.reflect.Label$PatchInfo from shared objects file]
[Loaded java.util.AbstractList$Itr from shared objects file]
[Loaded sun.reflect.MethodAccessorGenerator$1 from shared objects file]
[Loaded sun.reflect.ClassDefiner from shared objects file]
[Loaded sun.reflect.ClassDefiner$1 from shared objects file]
[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]
Hello, 15

可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致JVM新加载了一堆类,其中就包括[Loaded sun.reflect.GeneratedMethodAccessor1 from __JVM_DefineClass__]这么一行。这是哪里来的呢?

先来看看JDK里Method.invoke()是怎么实现的。

java.lang.reflect.Method:
public final
  class Method extends AccessibleObject implements GenericDeclaration,
               Member {
  // ...
  private volatile MethodAccessor methodAccessor;
  // For sharing of MethodAccessors. This branching structure is
  // currently only two levels deep (i.e., one root Method and
  // potentially many Method objects pointing to it.)
  private Method       root;
  // ...
  public Object invoke(Object obj, Object... args)
      throws IllegalAccessException, IllegalArgumentException,
      InvocationTargetException
  {
    if (!override) {
      if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
        Class caller = Reflection.getCallerClass(1);
        Class targetClass = ((obj == null || !Modifier.isProtected(modifiers))
                   ? clazz
                   : obj.getClass());
        boolean cached;
        synchronized (this) {
          cached = (securityCheckCache == caller)
            && (securityCheckTargetClassCache == targetClass);
        }
        if (!cached) {
          Reflection.ensureMemberAccess(caller, clazz, obj, modifiers);
          synchronized (this) {
          securityCheckCache = caller;
          securityCheckTargetClassCache = targetClass;
          }
        }
      }
    }
    if (methodAccessor == null) acquireMethodAccessor();
    return methodAccessor.invoke(obj, args);
  }
  // NOTE that there is no synchronization used here. It is correct
  // (though not efficient) to generate more than one MethodAccessor
  // for a given Method. However, avoiding synchronization will
  // probably make the implementation more scalable.
  private void 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;
      return;
    }
    // Otherwise fabricate one and propagate it up to the root
    tmp = reflectionFactory.newMethodAccessor(this);
    setMethodAccessor(tmp);
  }
  // ...
}

  可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。

每个实际的Java方法只有一个对应的Method对象作为root,。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。

那么MethodAccessor是啥呢?

sun.reflect.MethodAccessor:
public interface MethodAccessor {
  /** Matches specification in {@link java.lang.reflect.Method} */
  public Object invoke(Object obj, Object[] args)
    throws IllegalArgumentException, InvocationTargetException;
}

   可以看到它只是一个单方法接口,其invoke()方法与Method.invoke()的对应。

创建MethodAccessor实例的是ReflectionFactory。

sun.reflect.ReflectionFactory:
public class ReflectionFactory {
  private static boolean initted = false;
  // ...
  //
  // "Inflation" mechanism. Loading bytecodes to implement
  // Method.invoke() and Constructor.newInstance() currently costs
  // 3-4x more than an invocation via native code for the first
  // invocation (though subsequent invocations have been benchmarked
  // to be over 20x faster). Unfortunately this cost increases
  // startup time for certain applications that use reflection
  // intensively (but only once per class) to bootstrap themselves.
  // To avoid this penalty we reuse the existing JVM entry points
  // for the first few invocations of Methods and Constructors and
  // then switch to the bytecode-based implementations.
  //
  // Package-private to be accessible to NativeMethodAccessorImpl
  // and NativeConstructorAccessorImpl
  private static boolean noInflation    = false;
  private static int   inflationThreshold = 15;
  // ...
  /** We have to defer full initialization of this class until after
    the static initializer is run since java.lang.reflect.Method's
    static initializer (more properly, that for
    java.lang.reflect.AccessibleObject) causes this class's to be
    run, before the system properties are set up. */
  private static void checkInitted() {
    if (initted) return;
    AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
          // Tests to ensure the system properties table is fully
          // initialized. This is needed because reflection code is
          // called very early in the initialization process (before
          // command-line arguments have been parsed and therefore
          // these user-settable properties installed.) We assume that
          // if System.out is non-null then the System class has been
          // fully initialized and that the bulk of the startup code
          // has been run.
          if (System.out == null) {
            // java.lang.System not yet fully initialized
            return null;
          }
          String val = System.getProperty("sun.reflect.noInflation");
          if (val != null && val.equals("true")) {
            noInflation = true;
          }
          val = System.getProperty("sun.reflect.inflationThreshold");
          if (val != null) {
            try {
              inflationThreshold = Integer.parseInt(val);
            } catch (NumberFormatException e) {
              throw (RuntimeException)
                new RuntimeException("Unable to parse property sun.reflect.inflationThreshold").
                  initCause(e);
            }
          }
          initted = true;
          return null;
        }
      });
  }
  // ...
  public MethodAccessor newMethodAccessor(Method method) {
    checkInitted();
    if (noInflation) {
      return new MethodAccessorGenerator().
        generateMethod(method.getDeclaringClass(),
                method.getName(),
                method.getParameterTypes(),
                method.getReturnType(),
                method.getExceptionTypes(),
                method.getModifiers());
    } else {
      NativeMethodAccessorImpl acc =
        new NativeMethodAccessorImpl(method);
      DelegatingMethodAccessorImpl res =
        new DelegatingMethodAccessorImpl(acc);
      acc.setParent(res);
      return res;
    }
  }
}

  这里就可以看到有趣的地方了。如注释所述,实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。

  为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。

Sun的JDK是从1.4系开始采用这种优化的。

  PS.可以在启动命令里加上-Dsun.reflect.noInflation=true,就会RefactionFactory的noInflation属性就变成true了,这样不用等到15调用后,程序一开始就会用java版的MethodAccessor了。

  上面看到了ReflectionFactory.newMethodAccessor()生产MethodAccessor的逻辑,在“开头若干次”时用到的DelegatingMethodAccessorImpl代码如下:

sun.reflect.DelegatingMethodAccessorImpl:
/** Delegates its invocation to another MethodAccessorImpl and can
  change its delegate at run time. */
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
  private MethodAccessorImpl delegate;
  DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
    setDelegate(delegate);
  }
  public Object invoke(Object obj, Object[] args)
    throws IllegalArgumentException, InvocationTargetException
  {
    return delegate.invoke(obj, args);
  }
  void setDelegate(MethodAccessorImpl delegate) {
    this.delegate = delegate;
  }
}

  这是一个间接层,方便在native与Java版的MethodAccessor之间实现切换。

然后下面就是native版MethodAccessor的Java一侧的声明:

sun.reflect.NativeMethodAccessorImpl:
/** Used only for the first few invocations of a Method; afterward,
  switches to bytecode-based implementation */
class NativeMethodAccessorImpl extends MethodAccessorImpl {
  private Method method;
  private DelegatingMethodAccessorImpl parent;
  private int numInvocations;
  NativeMethodAccessorImpl(Method method) {
    this.method = method;
  }
  public Object invoke(Object obj, Object[] args)
    throws IllegalArgumentException, InvocationTargetException
  {
    if (++numInvocations > ReflectionFactory.inflationThreshold()) {
      MethodAccessorImpl acc = (MethodAccessorImpl)
        new MethodAccessorGenerator().
          generateMethod(method.getDeclaringClass(),
                  method.getName(),
                  method.getParameterTypes(),
                  method.getReturnType(),
                  method.getExceptionTypes(),
                  method.getModifiers());
      parent.setDelegate(acc);
    }
    return invoke0(method, obj, args);
  }
  void setParent(DelegatingMethodAccessorImpl parent) {
    this.parent = parent;
  }
  private static native Object invoke0(Method m, Object obj, Object[] args);
}

  每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。

注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的:

由C编写

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);
}
JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))
 JVMWrapper("JVM_InvokeMethod");
 Handle method_handle;
 if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {
  method_handle = Handle(THREAD, JNIHandles::resolve(method));
  Handle receiver(THREAD, JNIHandles::resolve(obj));
  objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));
  oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);
  jobject res = JNIHandles::make_local(env, result);
  if (JvmtiExport::should_post_vm_object_alloc()) {
   oop ret_type = java_lang_reflect_Method::return_type(method_handle());
   assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!");
   if (java_lang_Class::is_primitive(ret_type)) {
    // Only for primitive type vm allocates memory for java object.
    // See box() method.
    JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);
   }
  }
  return res;
 } else {
  THROW_0(vmSymbols::java_lang_StackOverflowError());
 }

JVM_END

其中的关键又是Reflection::invoke_method():

// This would be nicer if, say, java.lang.reflect.Method was a subclass
// of java.lang.reflect.Constructor
oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {
 oop mirror       = java_lang_reflect_Method::clazz(method_mirror);
 int slot        = java_lang_reflect_Method::slot(method_mirror);
 bool override     = java_lang_reflect_Method::override(method_mirror) != 0;
 objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));
 oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);
 BasicType rtype;
 if (java_lang_Class::is_primitive(return_type_mirror)) {
  rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);
 } else {
  rtype = T_OBJECT;
 }
 instanceKlassHandle klass(THREAD, java_lang_Class::as_klassOop(mirror));
 methodOop m = klass->method_with_idnum(slot);
 if (m == NULL) {
  THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");
 }
 methodHandle method(THREAD, m);
 return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);
}

  再下去就深入到HotSpot VM的内部了,本文就在这里打住吧。有同学有兴趣深究的话以后可以再写一篇讨论native版的实现。

  MethodAccessorGenerator长啥样呢?由于代码太长,这里就不完整贴了。它的基本工作就是在内存里生成新的专用Java类,并将其加载。就贴这么一个方法:

private static synchronized String generateName(boolean isConstructor,
                        boolean forSerialization)
{
  if (isConstructor) {
    if (forSerialization) {
      int num = ++serializationConstructorSymnum;
      return "sun/reflect/GeneratedSerializationConstructorAccessor" + num;
    } else {
      int num = ++constructorSymnum;
      return "sun/reflect/GeneratedConstructorAccessor" + num;
    }
  } else {
    int num = ++methodSymnum;
    return "sun/reflect/GeneratedMethodAccessor" + num;
  }
}

  去阅读源码的话,可以看到MethodAccessorGenerator是如何一点点把Java版的MethodAccessor实现类生产出来的。也可以看到GeneratedMethodAccessor+数字这种名字是从哪里来的了,就在上面的generateName()方法里。 
对本文开头的例子的A.foo(),生成的Java版MethodAccessor大致如下:

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);
    }
  }
}

  就反射调用而言,这个invoke()方法非常干净(然而就“正常调用”而言这额外开销还是明显的)。注意到参数数组被拆开了,把每个参数都恢复到原本没有被Object[]包装前的样子,然后对目标方法做正常的invokevirtual调用。由于在生成代码时已经循环遍历过参数类型的数组,生成出来的代码里就不再包含循环了。

  至此找到我的答案了,因为MethodAccessor会做强制类型转换再进行方法调用,但父类强制转化成子类的的时候就会报错类型不匹配错误了,所以如果变量的引用声明是父但实际指向的对象是子,那么这种调用也是可以的。

  当该反射调用成为热点时,它甚至可以被内联到靠近Method.invoke()的一侧,大大降低了反射调用的开销。而native版的反射调用则无法被有效内联,因而调用开销无法随程序的运行而降低。

  虽说Sun的JDK这种实现方式使得反射调用方法成本比以前降低了很多,但Method.invoke()本身要用数组包装参数;而且每次调用都必须检查方法的可见性(在Method.invoke()里),也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0()里或者生成的Java版MethodAccessor.invoke()里);而且Method.invoke()就像是个独木桥一样,各处的反射调用都要挤过去,在调用点上收集到的类型信息就会很乱,影响内联程序的判断,使得Method.invoke()自身难以被内联到调用方。

  相比之下JDK7里新的MethodHandler则更有潜力,在其功能完全实现后能达到比普通反射调用方法更高的性能。在使用MethodHandle来做反射调用时,MethodHandle.invoke()的形式参数与返回值类型都是准确的,所以只需要在链接方法的时候才需要检查类型的匹配性,而不必在每次调用时都检查。而且MethodHandle是不可变值,在创建后其内部状态就不会再改变了;JVM可以利用这个知识而放心的对它做激进优化,例如将实际的调用目标内联到做反射调用的一侧。

  本来Java的安全机制使得不同类之间不是任意信息都可见,但Sun的JDK里开了个口,有一个标记类专门用于开后门:

package sun.reflect;

/** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and
  others, not because it actually implements an interface) is a
  marker class in the hierarchy. All subclasses of this class are
  "magically" granted access by the VM to otherwise inaccessible
  fields and methods of other classes. It is used to hold the code
  for dynamically-generated FieldAccessorImpl and MethodAccessorImpl
  subclasses. (Use of the word "unsafe" was avoided in this class's
  name to avoid confusion with {@link sun.misc.Unsafe}.) </P>
  <P> The bug fix for 4486457 also necessitated disabling
  verification for this class and all subclasses, as opposed to just
  SerializationConstructorAccessorImpl and subclasses, to avoid
  having to indicate to the VM which of these dynamically-generated
  stub classes were known to be able to pass the verifier. </P>
  <P> Do not change the name of this class without also changing the
  VM's code. </P> */
class MagicAccessorImpl {
}

  那个"__JVM_DefineClass__"的来源是这里:

src/share/vm/prims/jvm.cpp
// common code for JVM_DefineClass() and JVM_DefineClassWithSource()
// and JVM_DefineClassWithSourceCond()
static jclass jvm_define_class_common(JNIEnv *env, const char *name,
                   jobject loader, const jbyte *buf,
                   jsize len, jobject pd, const char *source,
                   jboolean verify, TRAPS) {
 if (source == NULL) source = "__JVM_DefineClass__"; 

P.S. log里的"shared objects file",其实就是rt.jar,为什么要这么显示,Stack OverFlow上有这样的回答:

  This is Class Data Sharing. When running the Sun/Oracle Client HotSpot and sharing enable (either -Xshare:auto which is the default, or -Xshare:on), the classes.jsa file is memory mapped. This file contains a number of classes (listed in the classlist file) in internal representation suitable for the exact configuration of the machine running it. The idea is that the classes can be loaded quickly, getting the the JVM up faster. Soon enough a class not covered will be hit, and rt.jar will need to be opened and classes loaded conventionally as required.

  不能很好理解,大概理解就是所有jvm共享,并可以快速加载里面的class.有英文好的朋友可以留言帮助下。

P.S java内联函数

C++是否为内联函数由自己决定,Java由编译器决定。内联函数就是指函数在被调用的地方直接展开,编译器在调用时不用像一般函数那样,参数压栈,返回时参数出栈以及资源释放等,这样提高了程序执行速度。 
Java不支持直接声明为内联函数的,如果想让他内联,则是由编译器说了算,你只能够向编译器提出请求。

final除了不能被override外,还可能实现内联。如果函数为private,则也可能是内联的。

总的来说,一般的函数都不会被当做内联函数,只有声明了final后,编译器才会考虑是不是要把你的函数变成内联函数。

内联不一定好,当被指定为内联的方法体很大时,展开的开销可能就已经超过了普通函数调用调用的时间,引入了内联反而降低了性能,因为在选择这个关键字应该慎重些,不过,在以后高版本的JVM中,在处理内联时做出了优化,它会根据方法的规模来确定是否展开调用。

总结

以上所述是小编给大家介绍的Java中Method的Invoke方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • java.lang.AbstractMethodError: org.apache.xerces.dom.DocumentImpl.setXmlVersion问题解决方法

    读取本地的xml文件,通过DOM进行解析,DOM解析的特点就是把整个xml文件装载入内存中,形成一颗DOM树形结构,树结构是方便遍历和和操纵. DOM解析的特性就是读取xml文件转换为 dom树形结构,通过节点进行遍历. 这是W3c关于节点的概念 如果xml中包含有大量的数据,由于dom一次性把xml装入内存中的特性,所以dom不适合于包含大量数据的xml解析.当包含有大量xml的时候,用SAX进行解析比较节省内存. 下面是一个运用DOM进行解析xml文件的例子: xml文件结构如下: <?xm

  • 解析Java中的Field类和Method类

    Field类 Field类中定义了一些方法,可以用来查询字段的类型以及设置或读取字段的值.将这些方法与继承而来的member方法结合在一起.就可以使我们能够找出有关字段声明的全部信息,并且能够操纵某个特定对象或类的字段. getGenericType方法返回表示字段的声明类型的Type实例.对于像String或int这样的平凡类型,该方法将返回与其相关联的Class对象,例如String.class和int.classo对于像List < Stri ng>这样的参数化类型,该方法将返回Para

  • java.lang.NoSuchMethodException: com.sun.proxy.$Proxy58.list错误解决办法

    java.lang.NoSuchMethodException: com.sun.proxy.$Proxy58.list错误解决办法 玩web的SSH总会有些令你意想不到的exception,这里其中有很多事自己不小心,或者马虎所造成.因此,解决的方案会各有不同,别人出现的异常解决方案对你的可能无效,就像上面的我报的异常一样,百度了很多很多次,给我的答案无非就是在aop上加上一句,但是非常抱歉,我加上去无效!所以还是那句话,对于自己的异常,还是要自己解决. 首先说明一下,我这次的练习的ssh结构

  • Java反射机制及Method.invoke详解

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类:在运行时构造任意一个类的对象:在运行时判断任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 1. 得到某个对象的属性 复制代码 代码如下: public Object get

  • 详解Java中Method的Invoke方法

    在写代码的时候,发现从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错.虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码.  Method的invoke方法 1.先检查 AccessibleObject的override属性是否为true. Accessib

  • 详解Java中native方法的使用

    今天在网上学习时碰到有关于 native修饰符所修饰的方法,上网查了查,觉得很有意思记录一下 1.native简介 简单地讲,一个Native Method就是一个java调用非java代码的接口.一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C.这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数. native是与C++联合开发的时候用的!使用nat

  • 详解Java中方法重写和方法重载的6个区别

    目录 1.方法重写 1.1 基本用法 1.2 使用场景 1.3 注意事项 2.方法重载 2.1 基本使用 2.2 使用场景 2.3 注意事项 3.方法重写 VS 方法重载 总结 方法重写(Override)和方法重载(Overload)都是面向对象编程中,多态特性的不同体现,但二者本身并无关联,它们的区别犹如马德华之于刘德华的区别,除了名字长得像之外,其他的都不像. 接下来咱们就来扒一下二者的具体区别. 1.方法重写 方法重写(Override)是一种语言特性,它是多态的具体表现,它允许子类重新

  • 详解Java中NullPointerException异常的原因详解以及解决方法

    NullPointerException是当您尝试使用指向内存中空位置的引用(null)时发生的异常,就好像它引用了一个对象一样. 当我们声明引用变量(即对象)时,实际上是在创建指向对象的指针.考虑以下代码,您可以在其中声明基本类型的整型变量x: int x; x = 10; 在此示例中,变量x是一个整型变量,Java将为您初始化为0.当您在第二行中将其分配给10时,值10将被写入x指向的内存中. 但是,当您尝试声明引用类型时会发生不同的事情.请使用以下代码: Integer num; num

  • 详解Java中方法next()和nextLine()的区别与易错点

    1.基本语法 1.1基本使用方法 本篇博客重点nextLine()会读取换行('\r'),但是不会进行输出. Java中Scanner类中的方法next()和nextLine()都是吸取输入台输入的字符,区别: next()不会吸取字符前/后的空格/Tab键,只吸取字符,开始吸取字符(字符前后不算)直到遇到空格/Tab键/回车截止吸取: nextLine()吸取字符前后的空格/Tab键,回车键截止. 输入两行字符串: 我爱学JAVA 我真的很爱爱学JAVA 我真的很爱很爱学JAVA 期望输出结果

  • 详解java中String值为空字符串与null的判断方法

    Java空字符串与null的区别 1.类型 null表示的是一个对象的值,而不是一个字符串.例如声明一个对象的引用,String a=null. ""表示的是一个空字符串,也就是说它的长度为0.例如声明一个字符串String s="". 2.内存分配 String a=null:表示声明一个字符串对象的引用,但指向为null,也就是说还没有指向任何的内存空间. String s="":表示声明一个字符串类型的引用,其值为""空

  • 详解Java中List的正确的删除方法

    目录 简介 实例 正确方法 法1:for的下标倒序遍历 法2: list.stream().filter().collect() 法3: iterator迭代器 错误方法 法1:for(xxx : yyy)遍历 法2:for的下标正序遍历 原因分析 简介 本文介绍Java的List的正确的删除方法. 实例 需求:有如下初始数据,将list中的所有数据为"b"的元素删除掉.即:填充removeB()方法 package com.example.a; import java.util.Ar

  • 详解Java中Period类的使用方法

    目录 简介 Duration和Period 创建方法 通过时间单位创建 通过LocalDate创建 解析方法 比较方法 增减方法 转换单位 取值方法 简介 本文用示例介绍java的Period的用法. Duration和Period 说明 Duration类通过秒和纳秒相结合来描述一个时间量,最高精度是纳秒.时间量可以为正也可以为负,比如1天(86400秒0纳秒).-1天(-86400秒0纳秒).1年(31556952秒0纳秒).1毫秒(0秒1000000纳秒)等. Period类通过年.月.日

  • 详解Java中Duration类的使用方法

    目录 简介 Duration和Period 创建方法 通过时间单位创建 通过LocalDateTime或LocalTime 通过已有的Duration 解析方法 用法说明 详解 比较方法 增减方法 转换单位 取值方法 简介 本文用示例介绍java的Duration的用法. Duration和Period 说明 Duration类通过秒和纳秒相结合来描述一个时间量,最高精度是纳秒.时间量可以为正也可以为负,比如1天(86400秒0纳秒).-1天(-86400秒0纳秒).1年(31556952秒0纳

  • 详解Java中异步转同步的六种方法

    目录 一.问题 应用场景 二.分析 三.实现方法 1.轮询与休眠重试机制 2.wait/notify 3.Lock Condition 4.CountDownLatch 5.CyclicBarrier 6.LockSupport 一.问题 应用场景 应用中通过框架发送异步命令时,不能立刻返回命令的执行结果,而是异步返回命令的执行结果. 那么,问题来了,针对应用中这种异步调用,能不能像同步调用一样立刻获取到命令的执行结果,如何实现异步转同步? 二.分析 首先,解释下同步和异步 同步,就是发出一个调

随机推荐