关于java继承的一个很多人都搞不清楚的问题

peizhi051287 2009-12-06 11:15:49
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类的方法呢?

...全文
491 56 打赏 收藏 转发到动态 举报
写回复
用AI写文章
56 条回复
切换为时间正序
请发表友善的回复…
发表回复
cz61919345 2009-12-08
  • 打赏
  • 举报
回复
帮顶吧!
heartraid86 2009-12-08
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 peizhi051287 的回复:]
我感觉  heartraid86  你说的实在是太好了,你对问题的研究程度另我敬佩,我真的很感谢你为我解答这个问题,看来我得深入的了解java的指令集和jvm的工作原理,由于刚毕业忙着找工作,所以不得不给自己灌输很多框架的知识,其实很多基本的东西还是没有理解透彻的!谢谢你!
[/Quote]

我对JVM没有系统学习过,我说的只是一种猜测,觉得应该和invokevirtual有关,但我并不了解这个指令。我的解释应该是不正确的。我还是回去看完JVM再说吧。
heartraid86 2009-12-08
  • 打赏
  • 举报
回复
[Quote=引用 52 楼 jiangnaisong 的回复:]
所有的楼上的种种说法,
错误的说法真是多,理解错误还自我圆说。
楼主的问题问得很好,是一个很基本的问题。
为了更能暴露楼上种种错误说法:可将A中方法a()的private去掉,同时将类B放到与A不同的另一个包中。
这时再考虑楼主的问题就更能暴露楼上种种错误说法了。

1)这样就会生成invokevirtual指令了。因此楼主的问题更主要与invokevirtual指令相关了。
认真看一下JVM规范中对invokevirtual指令的说明,比所有的那些想当然的解释权威且清楚多了。

真是盲人摸象式的各种自我解释。
[/Quote]

多了解虚拟机才行呀,不然理解不到深层的东西。回去看书去....
iowntheworld 2009-12-07
  • 打赏
  • 举报
回复
学习了,顺便说下自己总结上述牛人的看法

调用new B().ab()换个写法就是object.ab(new B());this指针所指向的是new B()。所以this.class 就是B.class。

调用this.a(),this.b()。其实是编译器在编译期间就确定下来的。当编译器在首次编译this.a()时候,去查看该函数的定义,发现是private,private无法向下继承,所以就编译成为 invokespecial testa.A.a()。此时和this就一点关系都没啦,已经确定执行的是A.a();

Over~我可能说的有问题,欢迎大家指出
shine333 2009-12-07
  • 打赏
  • 举报
回复
[Quote=引用 32 楼 qin59218 的回复:]
A.a()与B.a()是两个不同的方法,因此this没有动态绑定B.a()方法,在调用this.a的时候this是A类的引用,他不知道B类有什么方法,所以就调用了A.a();
[/Quote]
你可以修改父类的private->public试一下
qin59218 2009-12-07
  • 打赏
  • 举报
回复
A.a()与B.a()是两个不同的方法,因此this没有动态绑定B.a()方法,在调用this.a的时候this是A类的引用,他不知道B类有什么方法,所以就调用了A.a();
peizhi051287 2009-12-07
  • 打赏
  • 举报
回复
我感觉 heartraid86 你说的实在是太好了,你对问题的研究程度另我敬佩,我真的很感谢你为我解答这个问题,看来我得深入的了解java的指令集和jvm的工作原理,由于刚毕业忙着找工作,所以不得不给自己灌输很多框架的知识,其实很多基本的东西还是没有理解透彻的!谢谢你!
peizhi051287 2009-12-07
  • 打赏
  • 举报
回复
还是没明白我的问题是什么?
kael_fk_ 2009-12-07
  • 打赏
  • 举报
回复
支持14楼!!!
lovepay1413 2009-12-07
  • 打赏
  • 举报
回复
或者重载ab方法。。。
lovepay1413 2009-12-07
  • 打赏
  • 举报
回复
关键就是父类A的a跟b方法都是private,的,所以父类A的ab方法调用的一定是A的a跟b方法,不会是子类的,除非把a跟b方法改成public咯。。。那就会调用重载的方法
heartraid86 2009-12-07
  • 打赏
  • 举报
回复
还有一点要搞清楚:

这是LZ在A中的ab()方法
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()。
darxin 2009-12-07
  • 打赏
  • 举报
回复
[Quote=引用楼主 peizhi051287 的回复:]
为什么this.class是B,那么this就表示对象B,那么为什么this.a(),和this.b()
输出的都是A类的方法呢?
[/Quote]

这里的问题不是this所导致的,而是private与public的不同所导致的。
ydm305365 2009-12-07
  • 打赏
  • 举报
回复
学习..帮顶
darxin 2009-12-07
  • 打赏
  • 举报
回复
楼主的A.java经 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: return

private 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: return

private 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: return

public 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: return

public 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: return

public 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: return

public 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指令,所以会调用到子类中同名的方法,也就是我们所说的继承方法。
ScAREcrOw_ss 2009-12-07
  • 打赏
  • 举报
回复
public final native Class<? extends Object> getClass();
zhao_tk 2009-12-07
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 justinavril 的回复:]
很简单的道理啊。

this.getClass()方法返回的是你运行时this的类型。这个方法是继承于Object。new B()这个对象运行时是B,没有问题。

但是this.a()为啥返回的是A.a?因为子类没有重写ab()方法,并且父类的a()方法是private修饰的。这样子类对象只能调用父类的ab方法。同时,a方法是private的,这就意味着不可能被子类重写,这样父类就无法知道子类是否有a方法了。于是,只能调用父类的private方法。如果你把private换成public,就会输出B.a了。
[/Quote]
学习了,顶.
云上飞翔 2009-12-07
  • 打赏
  • 举报
回复
即使方法a()去掉private,只加上final,this.a()调用时仍是invokevirtual指令。
heartraid86 2009-12-07
  • 打赏
  • 举报
回复
我查看了《thinking in java 中文第四版》P151,《Core Java 中文第7版》P148-P151,感觉都没有说清楚。我还看了《深入JVM 中文第二版》P329页。

我有这么一个感觉:
我们都知道,当类中的方法是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指令的解释问题。
qin59218 2009-12-07
  • 打赏
  • 举报
回复
java继承机制与权限机制还是他妈有玄机的
加载更多回复(35)

62,621

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧