class A {
private void a() {
System.out.println("A.a");
} private void b() {
System.out.println("A.b");
} public void ab() {
System.out.println(this.getClass());
this.a();
this.b();
}
}public class B extends A {
public void a() {
System.out.println("B.a");
} public void b() {
System.out.println("B.b");
} public static void main(String[] args) {
new B().ab();
}
}输出结果是:
class cn.icast.javaenhance.extend.B
A.a
A.b这我就步理解了,为什么this.class是B,那么this就表示对象B,那么为什么this.a(),和this.b()
输出的都是A类的方法呢?
private void a() {
System.out.println("A.a");
} private void b() {
System.out.println("A.b");
} public void ab() {
System.out.println(this.getClass());
this.a();
this.b();
}
}public class B extends A {
public void a() {
System.out.println("B.a");
} public void b() {
System.out.println("B.b");
} public static void main(String[] args) {
new B().ab();
}
}输出结果是:
class cn.icast.javaenhance.extend.B
A.a
A.b这我就步理解了,为什么this.class是B,那么this就表示对象B,那么为什么this.a(),和this.b()
输出的都是A类的方法呢?
public void a() {
System.out.println("A.a");
} public void b() {
System.out.println("A.b");
} public void ab() {
System.out.println(this.getClass());
this.a();
this.b();
}
}public class B extends A { public void a() {
System.out.println("B.a");
} public void b() {
System.out.println("B.b");
}
public static void main(String[] args) {
new B().ab();
}
}结果:
class cn.com.ylchen.demo.java.B
B.a
B.b
错错错,全错,大错特错!!!!!java编程思想说,当调用一个类的方法时,会将当前引用偷偷传给调用的方法,调用ab()方法实际等于new B().ab(引用); 而在ab方法中相当于ab(this);结果可想而知,this.getClass() 根本就是 “B”对象。
public void ab();
0 aload_0 [this]
1 invokevirtual testa.A.a() : void [30]//这里调用的invokevirtual 指令居然查找的是A类中的a()方法。
4 return
Line numbers:
[pc: 0, line: 10]
[pc: 4, line: 11]
Local variable table:
[pc: 0, pc: 5] local: this index: 0 type: testa.A
}
大家可以去试试,确实有些纳闷。我原来自认为对Java的方法调用十分了解,现在看来还不是很清楚的。期待牛人的牛解???
我们都知道,当类中的方法是static或final的时候,调用后class文件字节码指令用的是invokespecial。
这是因为static方法不属于对象,final方法无法覆盖,所以都不存在多态的问题。而所有的调用普通方法的指令都是invokevirtual,我觉得这个指令是专门用于处理多态问题的。
也就是说16楼中我对A类中ab()方法反编译出的invokevirtual testa.A.a(),虽然表面上查找的是A的a(),但应该存在一个多态问题,也就是说this实际类型是B的,invokevirtual 指令会考虑到this实际类型转而去调用B的a()方法。这个只是我的一个猜测。如果是真的是这样的话,那么invokevirtual指令的执行流程应该是这样的:
(1) 首先invokevirtual指令是A类中的ab()方法内的调用指令,所以invokevirtual指令会查找A类中的a()方法。
(2) 如果A类中的a()方法是private的,这说明此时的A中a()方法相当于final,子类无法覆盖。所以也就不去找别的可能的覆盖方法了。这时即使调用invokevirtual方法指令的实际对象this是B类型,也不管了。
(3) 如果A类中的a()方法是public的,也就是说此时的a()方法完全有可能被子类的a()覆盖,此时invokevirtual指令就会去找实际类型B中的a()方法了。我觉得应该是这个样子的。所以关键是invokevirtual指令的解释问题。
Compiled from "A.java"
class test.A extends java.lang.Object{
test.A();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: returnprivate void a();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String A.a
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: returnprivate void b();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5; //String A.b
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: returnpublic void ab();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #6; //Method java/lang/Object.getClass:()Ljava/lang/Class;
7: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: aload_0
11: invokespecial #8; //Method a:()V
14: aload_0
15: invokespecial #9; //Method b:()V
18: return}
注意a()与b()均为私有方法。所以我们看到ab()方法中的汇编代码是
10: aload_0
11: invokespecial #8; //Method a:()V
14: aload_0
15: invokespecial #9; //Method b:()V由于invokespecial指令的原因,ab()方法调用的是“invokespecial #1;”指定的A类中的方法,A类构造方法的汇编如下
test.A();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return-----------------------------------------
如果我们将A.java中的a()与b()写成公有方法,如下:class A { public void a() { System.out.println("A.a");
} public void b() { System.out.println("A.b");
} public void ab() { System.out.println(this.getClass());
this.a();
this.b();
}
}
利用javap反汇编得到的结果是Compiled from "A.java"
class test.A extends java.lang.Object{
test.A();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: returnpublic void a();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String A.a
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: returnpublic void b();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5; //String A.b
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: returnpublic void ab();
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #6; //Method java/lang/Object.getClass:()Ljava/lang/Class;
7: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: aload_0
11: invokevirtual #8; //Method a:()V
14: aload_0
15: invokevirtual #9; //Method b:()V
18: return}
我们看到ab()方法中的汇编代码是
10: aload_0
11: invokevirtual #8; //Method a:()V
14: aload_0
15: invokevirtual #9; //Method b:()V由于用到的是invokeviertual指令,所以会调用到子类中同名的方法,也就是我们所说的继承方法。
这里的问题不是this所导致的,而是private与public的不同所导致的。
public void ab() {
System.out.println(this.getClass());
this.a();
this.b();
} 在编译后、运行前的.class中。
(1) System.out.println(this.getClass());
被编译成: invokevirtual java.io.PrintStream.println(java.lang.Object) : void [34]
(2) this.a();
被编译成: invokespecial testa.A.a() : void [37]可以这样解释:在运行之前,编译器是不可能知道this指的是哪一个对象的(编译器不是JVM)。
因此this.a();被编译成invokespecial testa.A.a()。这并不表示会调用testa.A.a()方法。因为invokespecial指令是具有多态功能的,换句话说这条指令会根据运行时this的实际对象来动态绑定a()方法。所以运行时,我们看到了System.out.println(this.getClass()); 的结果是B类,因此JVM知道invokespecial需要执行动态绑定机制了。但可惜的是A中的a()方法是private的,也就是无法被子类覆盖。因此什么都免了,JVM直接就执行编译器编译的指令了invokespecial testa.A.a()。
指的就是ab方法那个类。并不是你的当前类。
随便看看吧,说不定有用呢
错误的说法真是多,理解错误还自我圆说。
楼主的问题问得很好,是一个很基本的问题。
为了更能暴露楼上种种错误说法:可将A中方法a()的private去掉,同时将类B放到与A不同的另一个包中。
这时再考虑楼主的问题就更能暴露楼上种种错误说法了。1)这样就会生成invokevirtual指令了。因此楼主的问题更主要与invokevirtual指令相关了。
认真看一下JVM规范中对invokevirtual指令的说明,比所有的那些想当然的解释权威且清楚多了。真是盲人摸象式的各种自我解释。
我对JVM没有系统学习过,我说的只是一种猜测,觉得应该和invokevirtual有关,但我并不了解这个指令。我的解释应该是不正确的。我还是回去看完JVM再说吧。