[讨论] 静态方法调用疑问

haoweishow 2011-09-19

源码:

/**
 * javac TestStaticMethod.java
 * javap -verbose TestStaticMethod
 * @author EX-HAOWEI002
 *
 */
public class TestStaticMethod {
	
	public static void foo(){
		
	}
	
	public void call1(){
		foo();
	}
	
	public void call2(){
		TestStaticMethod.foo();
	}
	
	public void call3(){
		this.foo();
	}
}

 

执行命令:

javap -verbose TestStaticMethod

 显示结果如下:

D:\ws\JVM\src>javap -verbose TestStaticMethod
Compiled from "TestStaticMethod.java"
public class TestStaticMethod extends java.lang.Object
  SourceFile: "TestStaticMethod.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #4.#15; //  java/lang/Object."<init>":()V
const #2 = Method       #3.#16; //  TestStaticMethod.foo:()V
const #3 = class        #17;    //  TestStaticMethod
const #4 = class        #18;    //  java/lang/Object
const #5 = Asciz        <init>;
const #6 = Asciz        ()V;
const #7 = Asciz        Code;
const #8 = Asciz        LineNumberTable;
const #9 = Asciz        foo;
const #10 = Asciz       call1;
const #11 = Asciz       call2;
const #12 = Asciz       call3;
const #13 = Asciz       SourceFile;
const #14 = Asciz       TestStaticMethod.java;
const #15 = NameAndType #5:#6;//  "<init>":()V
const #16 = NameAndType #9:#6;//  foo:()V
const #17 = Asciz       TestStaticMethod;
const #18 = Asciz       java/lang/Object;

{
public TestStaticMethod();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 7: 0


public static void foo();
  Code:
   Stack=0, Locals=0, Args_size=0
   0:   return
  LineNumberTable:
   line 11: 0


public void call1();
  Code:
   Stack=0, Locals=1, Args_size=1
   0:   invokestatic    #2; //Method foo:()V
   3:   return
  LineNumberTable:
   line 14: 0
   line 15: 3


public void call2();
  Code:
   Stack=0, Locals=1, Args_size=1
   0:   invokestatic    #2; //Method foo:()V
   3:   return
  LineNumberTable:
   line 18: 0
   line 19: 3


public void call3();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   pop
   2:   invokestatic    #2; //Method foo:()V
   5:   return
  LineNumberTable:
   line 22: 0
   line 23: 5


}

 

call1和call2是一样的,call3里用了this,为啥就不一样了呢?

我的理解是:不管是通过this,还是类名访问静态方法foo,因为在内存中都是同一个foo,应该是一样的。

还有就是根据字节码,是否能够断定,用this访问静态方法,多了两个指令操作,因此效率比其他两个低呢?

 

还请各位大大,帮忙解释一下。

RednaxelaFX 2011-09-19
没啥不一样的。aload_0把this压到操作数栈顶,紧接着pop就把它弹出去了。
生成这样的代码只是因为javac比较懒没有分析一些明显可以优化的点而已。实际生成的call1、call2、call3的语义是一样的。

字节码的差异不能用来断定程序效率的高低。字节码一样可以推断性能在同等水平,但字节码不一样说明不了性能问题。

在HotSpot VM的默认执行模式里的话,这仨方法被解释执行时call1与call2会比call3快一些,因为它的解释器没做什么优化;但被JIT编译的话三者应该得到一样的结果,多出来的两条指令在JIT编译的最初阶段就已经被干掉了。

唯一细微的差别是,call3的字节码指令更长,会占用更多的内联预算——也就是说如果call3在同一个方法内被多次内联的话,能被内联的次数会少些。不考虑这种边角情况的话,三者是一样的。
haoweishow 2011-09-19
谢谢你的回复,又学习啦。
googya 2011-09-19
看到实例对象调用静态方法,我刚开始看着很不习惯呢。。。我想知道为什么这样做是没有问题的???

在ruby中就会出问题的。。c#中貌似也会。。Java怎么就。。。。。


真是很困惑。
RednaxelaFX 2011-09-20
googya 写道
看到实例对象调用静态方法,我刚开始看着很不习惯呢。。。我想知道为什么这样做是没有问题的???

在ruby中就会出问题的。。c#中貌似也会。。Java怎么就。。。。。


真是很困惑。

你的感觉没错。Java的这种设计就是挖坑让人容易掉进去。

例如说这样:
Thread t = new Thread(new Runnable() {
  // ...
});

t.start();
t.sleep(5000); // 注意这里

// ...


这样的话,代码看起来像是让t睡眠了5秒,实际上却是让当前线程睡眠了5秒,因为sleep是个静态方法。超级坑爹。
Global site tag (gtag.js) - Google Analytics