先看代码:
public class Test {
public static void testStatic(){
System.out.println("testStatic()");
}
public void testNonStatic(){
System.out.println("testNonStatic()");
}
public static void main(String[] args) {
Test.testStatic();
Test test = null;
test.testStatic();
test = new Test();
test.testStatic();
test.testNonStatic();
}
}
结果:
testStatic()
testStatic()
testStatic()
testNonStatic()
原因其实很简单,调用静态方法或者访问静态数据时根本不需要实例,如例子中test.testStatic(); ,这里的test是否为空根本无所谓,因为它存在的目的就是向编译器传达一个信号,"我要调用的是Test类的testStatic()方法,别给我搞错了啊!"。虽然java是面向对象的语言,但到底层执行依然是过程式的,这是计算机的本质决定的。因此,实例方法的调用不过是换了个形式而已,就是把对象引用本身作为第一个参数,比如test.testNonStatic();其实可以看作Test::testNonStatic(test);这里传入的参数test就是非静态方法中可以拿来用的this了。
换个形式可能更容易理解:
public void Test::testNonStatic(final Test this){……}
非静态方法里就可以使用this这个形参来访问数据了。调用非静态方法的时候会检查该实例是否为null。
最后,看一下main方法反编译代码:
public static void main(java.lang.String[]);
Code:
0: invokestatic #6; //Method testStatic:()V
3: aconst_null
4: astore_1
5: aload_1
6: pop
7: invokestatic #6; //Method testStatic:()V
10: new #7; //class Test
13: dup
14: invokespecial #8; //Method "":()V
17: astore_1
18: aload_1
19: pop
20: invokestatic #6; //Method testStatic:()V
23: aload_1
24: invokevirtual #9; //Method testNonStatic:()V
27: return
这里可以发现一个问题,当用类直接调用方法时,只需要1条指令:
invokestatic #6; //Method testStatic:()V
而用对象引用来调用却需要3条指令,而且前两条完全是无用功:
5: aload_1
6: pop //干嘛,耍我呢?
7: invokestatic #6; //Method testStatic:()V
刚把引用test放到栈顶就立马弹出去了,瞎折腾了一下。剩下的才是真正起作用的调用指令。很明显,test是否为null根本不用理会了。
这里发现一个问题,使用类直接调用静态方法不但是一个好的编码习惯,还可能有性能上的优势,毕竟使用对象引用来调用时白白浪费了两条指令。之所以说是"可能",因为jvm编译器完全有可能把这种无用指令优化掉。
还是非静态方法的调用规矩啊,
23: aload_1
24: invokevirtual #9; //Method testNonStatic:()V
将test作为唯一的参数,调用testNonStatic方法。invokevirtual会检查该参数,为null时抛异常。