本文主要介绍Java中方法的Invoke方法,有需要的朋友可以参考一下。
写代码的时候发现通过getDeclaredMethod从父类获取的方法可以调用子类的对象,子类重写这个方法,也可以通过getDeclaredMethod从子类获取方法。此时,调用父类的对象也会报错。虽然这符合多态和java的动态绑定规范,但是我还是想了解java是怎么实现的,所以我学习了Method的源代码。
调用方法的方法
1.先检查AccessibleObject的override属性是否为true。
AccessibleObject是方法、方法、字段、构造函数的父类。默认情况下,override属性为false,可通过调用setAccessible方法进行更改。如果设置为true,则表示可以忽略访问权限的限制,直接调用。
2.如果不正确,则需要检查访问权限。首先使用反射的quickCheckMemberAccess方法检查它是否是公共的,如果不是,则使用Reflection.getCallerClass(1)方法获取它。
调用此方法的类,然后检查您是否有权访问它。检查完之后,缓存一次,这样下次被这个类调用的时候,就不用检查了,直接用上次的结果就可以了。(这样缓存很奇怪,因为如果下次被别的类调用,就不需要缓存了,而是再次验证,把这个结果作为缓存,但是最后缓存的结果会被洗掉。这是一个非常简单的缓冲机制,只适用于一个类的重复调用)。
3.调用MethodAccessor的invoke方法。每个方法对象都包含一个根对象,该根对象包含一个MethodAccessor对象。获得的方法相当于根对象的镜像,所有这些方法共享根中的MethodAccessor对象(此对象由ReflectionFactory方法生成,该方法在方法类中是静态final的,由本机方法实例化)。
ReflectionFactory生成MethodAccessor:如果noInflation的属性为true,则直接返回MethodAccessorGenerator创建的MethodAccessor。否则,返回DelegatingMethodAccessorImpl并将其与NativeMethodAccessorImpl交叉引用。但是,在执行invoke方法时,DelegatingMethodAccessorImpl被委托给NativeMethodAccessorImpl。
更进一步。
4.4的invkoe方法。NativeMethodAccessorImpl:
调用natiave方法invoke0来执行方法调用。
请注意,这里有一个计数器。每次调用方法1时,当它大于reflection factory . inflation threshold(15)时,将使用MethodAccessorGenerator创建一个MethodAccessor,并且以前的DelegatingMethodAccessorImpl引用将替换为新创建的引用。下一个DelegatingMethodAccessorImpl不会交给NativeMethodAccessorImpl执行,而是交给新生成的java字节码的MethodAccessor。
MethodAccessorGenerator使用asm字节码动态加载技术,所以暂时不做深入研究。
总结一个方法可以生成多个Method对象,但是根对象只有一个,主要用来存放MethodAccessor对象。这个对象也可以认为只是一个方法,相当于static。因为方法的调用是给MethodAccessor执行的,所以我想知道的答案在MethodAccessor的调用中,深入到MethodAccessor:
-method访问器-
如果有这样一个A级:
公共A类{
public void foo(字符串名){
System.out.println(Hello, name);
}
}
您可以编写另一个类来反映对的方法调用:
导入Java . lang . reflect . method;
公共类TestClassLoad {
公共静态void main(String[] args)引发异常{
班级?clz=class . forname( A );
object o=clz . new instance();
方法m=clz.getMethod(foo ,string . class);
for(int I=0;i 16i ) {
m.invoke(o,integer . tostring(I));
}
}
}
注意到TestClassLoad类上不会有对类A的符号依赖——也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到主()方法执行到调用Class.forName()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的类加载器来加载,并且初始化新加载的类。……好吧这个细节跟主题没啥关系。
回到主题。这次我的测试环境是太阳的JDK 1.6.0更新13内部版本03 .编译上述代码,并在执行TestClassLoad时加入-XX: TraceClassLoading参数(或者-verbose:class或者直接-冗长都行),如下:
控制台命令
Java-XX:TraTestClassLoad ceClassLoading
可以看到输出了一大堆日志,把其中相关的部分截取出来如下:
[已从文件加载测试类加载:/D:/temp _ code/test _ Java _ class load/]
[从文件中加载了:/D:/temp _ code/test _ Java _ class load/]
[已从共享对象文件加载星期日反思。nativemethodaccessorimpl]
[已从共享对象文件加载星期日反思。delegatingmethodaccessorimpl]
你好,0
你好,1
你好,2号
你好,3号
你好,4号
你好,5号
你好,6号
你好,7号
你好,8号
你好,9号
你好,10号
你好,11
你好,12号
你好,13
你好,14
[已从共享对象文件加载星期日反思。类文件常量]
[已从共享对象文件加载星期日反思。存取生成器]
[已从共享对象文件加载星期日反思。methodaccessorgenerator]
[已从共享对象文件加载星期日反思。bytevectorfactory]
[已从共享对象文件加载星期日反思。字节向量]
[已从共享对象文件加载星期日反思。bytevectorimpl]
[已从共享对象文件加载星期日反思。类文件汇编程序]
[从共享对象文件加载了星期日反思。utf8]
[从共享对象文件加载Java。郎。无效]
[已从共享对象文件加载星期日反思。标签]
[已从共享对象文件加载星期日反思。标签$PatchInfo]
[从共享对象文件加载Java。util。摘要列表$ Itr]
[已从共享对象文件加载星期日反思。方法辅助生成器$ 1]
[已从共享对象文件加载星期日反思。类别定义者]
[从共享对象文件加载了星期日反思。类别定义者$ 1]
[已从__JVM_DefineClass__]加载星期日反思。生成的方法访问器1]
你好,15
可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致虚拟机(Java虚拟机的缩写)新加载了一堆类,其中就包括[已从__JVM_DefineClass__]加载星期日反思。生成的方法访问器1]这么一行。这是哪里来的呢?
先来看看JDK里方法。调用()是怎么实现的。
java.lang.reflect.Method:
公开决赛
班级方法扩展可访问对象实现泛型声明,
成员{
//.
私有易变方法访问器;
//用于共享方法访问器.这个分支结构是
//目前只有两层深度(即一个根方法和
//可能有许多方法对象指向它。)
私有方法根;
//.
公共对象调用(对象对象,对象.参数)
抛出IllegalAccessException,IllegalArgumentException,
InvocationTargetException
{
如果(!覆盖){
如果(!反思。快速检查成员访问(clazz,modifiers)) {
类调用方=反射。getcallerclass(1);
Class targetClass=((obj==null ||!Modifier.isProtected(modifiers))
?克拉兹
目标。getclass());
布尔缓存;
同步(这){
cached=(securityCheckCache==caller)
(securityCheckTargetClassCache==目标类);
}
如果(!缓存){
反思。ensurememberaccess(caller,clazz,obj,modifiers);
同步(这){
securityCheckCache=呼叫者
securityCheckTargetClassCache=目标类;
}
}
}
}
if(方法访问器==null)获取方法访问器();
返回methodAccessor.invoke(obj,args);
}
//注意,这里没有使用同步。这是正确的
//(尽管效率不高)来生成多个方法访问器
//对于给定的方法。但是,避免同步会
//大概让实现更有伸缩性。
私有void acquireMethodAccessor() {
//首先检查是否已经创建了一个,并获取它
//如果是
方法访问器tmp=null
如果(根!=null)tmp=root。getmethod访问器();
如果(tmp!=null) {
methodAccessor=tmp
返回;
}
//否则,创建一个并向上传播到根目录
tmp=反射工厂。新方法访问器(this);
setmethod访问器(tmp);
}
//.
}
可以看到方法。调用()实际上并不是自己实现的反射调用逻辑,而是委托给星期日反思。方法访问器来处理。
每个实际的Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)方法只有一个对应的方法对象作为根,100 .这个根是不会暴露给用户的,而是每次在通过反射获取方法对象时新创建方法对象把根包装起来再给用户。在第一次调用一个实际Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)方法对应得方法对象的调用()方法之前,实现调用逻辑的方法访问器对象还没创建;等第一次调用时才新创建方法访问器并更新给根,然后调用MethodAccessor.invoke()真正完成反射调用。
那么方法访问器是啥呢?
星期日反思。方法访问者:
公共接口方法访问器{
/**匹配{ @链接Java。郎。反思。方法} */中的规范
公共对象调用(对象obj,对象[]参数)
抛出IllegalArgumentException,InvocationTargetException
}
可以看到它只是一个单方法接口,其调用()方法与方法。调用()的对应。
创建方法访问器实例的是反射工厂。
星期日反思。反射工厂:
公共类ReflectionFactory {
私有静态布尔initted=false
//.
//
//"膨胀"机制。加载字节码以实现
//Method.invoke()和Constructor.newInstance()当前开销
//比第一次通过本机代码调用多3-4倍
//调用(尽管后续调用已经过基准测试
//快20倍以上)。不幸的是,这个成本增加了
//使用反射的某些应用程序的启动时间
//集中(但每个类只能一次)引导它们自己。
//为了避免这种损失,我们重用了现有的虚拟机(Java Virtual Machine的缩写)入口点
//对于方法和构造函数的最初几次调用,以及
//然后切换到基于字节码的实现。
//
//包-私有可供NativeMethodAccessorImpl访问
//和NativeConstructorAccessorImpl
私有静态布尔noInflation=false
私人静态int通胀阈值=15;
//.
/**我们必须将该类的完全初始化推迟到
静态初始化器是从java.lang.reflect .方法的
静态初始值设定项(更确切地说,用于
Java。郎。反思。可访问对象)导致该类的
在设置系统属性之前运行。*/
私有静态void checkinited(){
如果(初始化)返回;
门禁控制器。doprivileged(新的特权操作(){
公共对象运行(){
//测试以确保系统属性表完全
//已初始化。这是必要的,因为反射代码是
//在初始化过程的早期调用(之前
//命令行参数已被解析,因此
//安装了这些用户可设置的属性。)我们假设
//如果System.out为非空,则系统类已被
//完全初始化,并且大部分启动代码
//已经运行。
if (System.out==null) {
//java.lang.System尚未完全初始化
返回空
}
string val=system。getproperty(孙。反思。无通货膨胀’);
如果(瓦尔!=空值。等于(真){
noInflation=true
}
val=系统。getproperty(孙。反思。通胀阈值’);
如果(瓦尔!=null) {
尝试{
通胀阈值=整数。parse int(val);
} catch(NumberFormatException e){
throw (RuntimeException)
新的RuntimeException(无法分析属性sun.reflect.inflationThreshold)).
初始化原因(e);
}
}
initted=true
返回空
}
});
}
//.
公共方法访问器新方法访问器(方法方法){
checkinited();
如果(无膨胀){
返回新的MethodAccessorGenerator().
生成方法(方法。getdeclaringclass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
方法。get修饰符());
}否则{
NativeMethodAccessorImpl acc=
新NativeMethodAccessorImpl(方法);
委托方法accessorimpl RES=
新委派方法accessorimpl(ACC);
ACC。设置父级(RES);
返回表示留数
}
}
}
这里就可以看到有趣的地方了。如注释所述,实际的方法访问器实现有两个版本,一个是Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)实现的,另一个是本机代码实现的Java .实现的版本在初始化时需要较多时间,但长久来说性能较好;当地的版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)版了。这是热点的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越当地的边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了权衡两个版本的性能,孙的JDK使用了"通货膨胀"的技巧:让Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)方法在被反射调用时,开头若干次使用当地的版,等反射调用次数超过阈值时则生成一个专用的方法访问器实现类,生成其中的调用()方法的字节码,以后对该Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)方法的反射调用就会使用Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)版。
太阳的JDK是从1.4系开始采用这种优化的。
PS .可以在启动命令里加上-Dsun.reflect.noInflation=true,就会RefactionFactory的无膨胀属性就变成真实的了,这样不用等到15调用后,程序一开始就会用Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)版的方法访问器了。
上面看到了反射工厂。新方法访问器()生产方法访问器的逻辑,在"开头若干次"时用到的委派方法代码如下:
星期日反思。委派方法accessorimpl:
/**将其调用委托给另一个方法AccessorImpl,并且可以
在运行时更改其委托。*/
类委托方法辅助扩展MethodAccessorImpl {
私有方法辅助委托;
DelegatingMethodAccessorImpl(MethodAccessorImpl委托){
setDelegate(委托);
}
公共对象调用(对象obj,对象[]参数)
抛出IllegalArgumentException,InvocationTargetException
{
返回delegate.invoke(obj,args);
}
void set委托(MethodAccessorImpl委托){
this.delegate=代表
}
}
这是一个间接层,方便在当地的与Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)版的方法访问器之间实现切换。
然后下面就是当地的版方法访问器的Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)一侧的声明:
星期日反思。nativemethodaccessorimpl:
/**仅用于方法的前几次调用;之后,
切换到基于字节码的实现*/
类NativeMethodAccessorImpl扩展了MethodAccessorImpl {
私有方法方法;
私有委托方法辅助父级;
私人电话号码;
NativeMethodAccessorImpl(方法方法){
这个方法=方法;
}
公共对象调用(对象obj,对象[]参数)
抛出IllegalArgumentException,InvocationTargetException
{
if(numInvocations反射工厂。通胀阈值()){
MethodAccessorImpl ACC=(MethodAccessorImpl)
新方法AccessorGenerator().
生成方法(方法。getdeclaringclass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
方法。get修饰符());
父母。设置代表(ACC);
}
返回invoke0(方法,对象,参数);
}
void set parent(DelegatingMethodAccessorImpl parent){
this.parent=parent
}
私有静态原生对象调用0(方法m,对象obj,对象[]args);
}
每次nativemethodaccessorimpl。调用()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用methodaccessorgenerator。生成方法()来生成Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)版的方法访问器的实现类,并且改变委派方法所引用的方法访问器为Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)版。后续经由delegatingmethodaccessorimpl。调用()调用到的就是Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)版的实现了。
注意到关键的调用0()方法是个当地的方法。它在热点虚拟机里是由JVM_InvokeMethod()函数所支持的:
由C编写
JNI导出作业对象JNI调用Java _ sun _ reflect _ NativeMethodAccessorImpl _ invoke 0
(JNIEnv *env,jclass unused,jobject m,jobject obj,jobjectArray参数)
{
返回JVM_InvokeMethod(env,m,obj,args);
}
JVM _ ENTRY(作业对象,JVM_InvokeMethod(JNIEnv *env,作业对象方法,作业对象对象,作业对象数组参数0))
JVM包装器(“JVM _ invoke方法”);
处理方法_句柄
if(thread-stack _ available((address)method _ handle)=JVMInvokeMethodSlack){
method_handle=Handle(THREAD,JNIHandles:resolve(方法));
句柄接收器(线程,JNI句柄:resolve(obj));
objArrayHandle args(THREAD,objArrayOop(JNI句柄:resolve(args 0)));
OOP result=Reflection:invoke _ method(method _ handle(),receiver,args,CHECK _ NULL);
jobobject RES=JNI句柄: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,"健全性检查:ret_type oop不能为NULL!”);
if(Java _ lang _ Class:is _ primitive(ret _ type)){
//仅用于基本类型伏特计为Java语言(一种计算机语言,尤用于创建网站)对象分配内存。
//参见方框()方法。
JvmtiExport:post _ VM _ object _ alloc(JavaThread:current(),result);
}
}
返回表示留数
}否则{
THROW _ 0(VM symbols:Java _ lang _ stack溢出错误());
}
JVM _结束
其中的关键又是反射* invoke _ method():
//如果java.lang.reflect .方法是一个子类,那就更好了
//Java。郎。反思。构造器的
面向对象的程序设计(Object Oriented Programming的缩写)反射* 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(方法_镜像);
bool override=Java _ lang _ reflect _ Method:override(Method _ mirror)!=0;
objArrayHandle ptypes(THREAD,objarray OOP(Java _ lang _ reflect _ Method:parameter _ types(Method _ mirror)));
OOP return _ type _ mirror=Java _ lang _ reflect _ Method:return _ type(Method _ mirror);
基本类型
if(Java _ lang _ Class:is _ primitive(return _ type _ mirror)){
rtype=basic _ type _ mirror _ to _ basic _ type(return _ type _ mirror,CHECK _ NULL);
}否则{
rtype=T _ OBJECT
}
instanceKlassHandle klass(THREAD,Java _ lang _ Class:as _ klassOop(mirror));
方法OOP m=klass-method _ with _ idnum(slot);
if (m==NULL) {
THROW _ MSG _ 0(VM symbols:Java _ lang _ internal error(), invoke );
}
方法句柄方法(螺纹,m);
返回invoke(klass,method,receiver,override,ptypes,rtype,args,true,THREAD);
}
再下去就深入到热点虚拟机的内部了,本文就在这里打住吧。有同学有兴趣深究的话以后可以再写一篇讨论当地的版的实现。
方法辅助生成器长啥样呢?由于代码太长,这里就不完整贴了。它的基本工作就是在内存里生成新的专用Java 语言(一种计算机语言,尤用于创建网站)语言(一种计算机语言,尤用于创建网站)类,并将其加载。就贴这么一个方法:
私有静态同步字符串generateName(布尔是构造器,
用于序列化的布尔型)
{
if (isConstructor) {
if(用于序列化){
int num=serializationConstructorSymnum;
return sun/reflect/GeneratedSerializationConstructorAccessor num;
}否则{
int num=constructorSymnum
return sun/reflect/generated constructor accessor num;
}
}否则{
int num=methodSymnum
返回“sun/reflect/GeneratedMethodAccessor”编号;
}
}
如果你读一下源代码,你就能看到MethodAccessorGenerator是如何一点一点地产生Java版的MethodAccessor实现类的。还可以看到GeneratedMethodAccessor这个名字的出处,在上面的generateName()方法中。
对于本文开头的示例A.foo(),生成的Java版本的MethodAccessor大致如下:
包sun.reflect
公共类GeneratedMethodAccessor1扩展了MethodAccessorImpl {
公共生成的MethodAccessor1() {
super();
}
公共对象调用(对象obj,对象[]参数)
抛出IllegalArgumentException,InvocationTargetException {
//准备目标和参数
if (obj==null)抛出新的NullPointerException();
尝试{
目标=(A)obj;
if (args.length!=1)抛出新的IllegalArgumentException();
String arg 0=(String)args[0];
} catch (ClassCastException e) {
抛出新的IllegalArgumentException(e . tostring());
} catch (NullPointerException e) {
抛出新的IllegalArgumentException(e . tostring());
}
//进行调用
尝试{
target . foo(arg 0);
} catch (Throwable t) {
抛出新的InvocationTargetException(t);
}
}
}
就反射调用而言,这个invoke()方法非常干净(然而,就“普通调用”而言,开销是显而易见的)。请注意,参数数组已经被反汇编,将每个参数恢复到被Object[]包装之前的原始状态,然后对目标方法进行普通的invokevirtual调用。因为在生成代码时,参数类型数组已经循环过,所以生成的代码不再包含循环。
到目前为止,我找到了我的答案,因为MethodAccessor会做强制类型转换,然后调用方法,但是当父类被强制成子类时,会报错类型不匹配,所以如果变量的引用语句是父类但实际指向的对象是子类,那么这个调用也是可以的。
当反射调用成为热点时,甚至可以内联到靠近Method.invoke()的一侧,大大降低了反射调用的开销。而原生版本的反射调用无法有效内联,所以调用开销无法随着程序的运行而降低。
虽然Sun的JDK实现使得反射调用方法的成本比以前低了很多,但是Method.invoke()本身要在数组中包装参数;而且每次调用都要检查方法的可见性(在Method.invoke()中),还要检查每个实参与形参的类型匹配(在nativemendaccessorimpl.invoke0()或生成的Java版MethodAccessor.invoke())中;而且Method.invoke()就像一座独木桥,反射调用都要推过去,调用点收集的类型信息会很乱,影响内联程序的判断,使得Method.invoke()本身很难内联到调用者。
相比之下,JDK7中新的MethodHandler更有潜力。在它的功能完全实现后,可以达到比普通反射调用方法更高的性能。当使用MethodHandle进行反射调用时,MethodHandle.invoke()的形参和返回值类型都是准确的,所以只需要在链接方法时检查类型的匹配,而不是每次调用都要检查。而且MethodHandle是一个不可变的值,创建后其内部状态不会改变;JVM可以利用这些知识对其进行彻底的优化,比如将实际的调用目标内联到进行反射调用的一侧。
本来Java的安全机制并不是让任意的信息在不同的类之间可见,但是在Sun的JDK中有一个开口,有一个专门用来打开后门的tag类:
包sun.reflect
/** P MagicAccessorImpl(因与FieldAccessorImpl和
其他的,不是因为它实际上实现了一个接口)是一个
层次结构中的标记类。该类的所有子类都是
"神奇地"被虚拟机授予访问权限,否则无法访问
其他类的字段和方法。它是用来保存代码的
对于动态生成的FieldAccessorImpl和方法辅助
子类。(在本课程中避免使用"不安全"一词
名称以避免与{@link sun.misc.Unsafe}混淆/P
4486457的错误修复也需要禁用
验证该类和所有子类,而不仅仅是
SerializationConstructorAccessorImpl和子类,以避免
必须向伏特计指示这些动态生成的
众所周知,存根类能够通过验证100元/人
p不要改变这个类的名字而不改变
虚拟机的代码1000/P */
类MagicAccessorImpl {
}
那个__JVM_DefineClass__ 的来源是这里:
src/share/vm/prims/jvm.cpp
JVM _ DefineClass()和资浅代表队(Junior Varsity)的公共代码
M_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方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!