在写代码的时候,发现从⽗类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\ 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, 0Hello, 1Hello, 2Hello, 3Hello, 4Hello, 5Hello, 6Hello, 7Hello, 8Hello, 9Hello, 10Hello, 11Hello, 12Hello, 13Hello, 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版的实现。 回到Java的⼀侧。MethodAccessorGenerator长啥样呢?由于代码太长,这⾥就不完整贴了,有兴趣的可以到OpenJDK 6的Mercurial仓库看:。它的基本⼯作就是在内存⾥⽣成新的专⽤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;
/**
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}.)
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.
Do not change the name of this class without also changing the VM's code.
*/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 representationsuitable for the exact configuration of the machine running it. The idea is that the classes can be loaded quickly, getting the the JVM upfaster. 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中,在处理内联时做出了优化,它会根据⽅法的规模来确定是否展开调⽤。
因篇幅问题不能全部显示,请点此查看更多更全内容