问题描述
publicclassFather{intnumber=3;Father(){super();show();return;}voidshow(){System.out.println("Fathershow..."+number);}}publicclassSonsextendsFather{intnumber=8;Sons(){super();//此处是分水岭,父类初始化完毕以后,才能轮到子类成员变量的显示初始化System.out.println("Sons'sConstructorrun..."+number);return;}////voidshow(){//System.out.println("Sonsshow..."+super.number+"..."+number);//此处父类成员变量的显示初始化过程和子类一样,因为父类Father的父类是Object,必须先把Object初始化完毕以后,Father类的成员变量才能进行显示初始化//}}publicclassFatherSonsRun{publicstaticvoidmain(String[]args){Sonssons=newSons();//new对象的时候,先在堆内存中分配此对象的内存空间,此对象有一个首地址值,并对此对象中的成员变量进行默认初始化(隐式初始化)sons.show();}}这里,我如果把子类的show方法注释掉,那么将会访问父类的show方法,但是在访问父类show方法里面将会访问的是父类的成员变量,而不是子类的成员变量,有点不理解?我是从内存图的角度去理解的,有点理解不通。运行结果为:Fathershow...3Sons'sConstructorrun...8Fathershow...3因为访问number的话,应该是this.number,访问的应该是图中右边堆内存里的成员变量this.number,应该输出值为0才对,但为什么结果是父类的number成员变量3呢,高手能从内存的角度帮我分析下吗?
解决方案
解决方案二:
我覆盖后的输出结果,我按照内存图的理解,应该是输出Fathershow...0Sons'sConstructorrun...8Fathershow...8才对,就是不能理解从内存角度分析的结果为什么不对,图中的三块内存区域分别是,左上是栈内存,右上是堆内存,下边是方法区
解决方案三:
你在Debug模式下单步执行就知道了,那个intnumber=3是在类的构造方法之前就执行的。所以到了构造方法中这些成员变量已经有值了。当然了,如果你在父中构造方法中调用覆盖的子类的show方法,那么子类中的number这个时候还没有初始化,因此子类的show在被父类的构造方法调用时它得到的this.number就是0而不是8。
解决方案四:
在每一行下断点,然后debug它。以前我也没注意这个初始值的问题,后来是在debug过程中发现初始值的赋值后才进到构造方法中了。
解决方案五:
嗯,这些东西还是自己调试运行理解的会更深刻。可以在各个位置进行输出父类{//父类代码块}构造函数属性值子类{//子类代码块}构造函数属性值看看java对类加载,先运行哪些
解决方案六:
我也有个问题,为什么在初始化Fahter进入构造函数的时候调用的是Sons的show()方法啊?这样的话在开发过程中使用继承会不会出现好多问题啊?
解决方案七:
这本来就是多态的概念,当你打算在构造函数中使用可能被覆盖的方法,这本来就表示,这个被覆盖的方法不应该与状态之间的关联如此密切,(比如,最好是它不要使用要求在构造方法中初始化的成员变量),或者你在访问过程上需要明确的思考是用成员变量名访问它还是用getter/setter方法它,总之,这是设计的决策问题,只要认真思考过,并且对特定的约定之处写在文档中就没有什么疑问,毕竟别人打算使用你的API的第一步应该是看API文档而不是猜测。引用5楼mrliu0905的回复:
我也有个问题,为什么在初始化Fahter进入构造函数的时候调用的是Sons的show()方法啊?这样的话在开发过程中使用继承会不会出现好多问题啊?
解决方案八:
我想问的是,为什么Sonssons=newSons();sons.show();如果show方法没有在子类中覆盖,那么此时sons.show();其实访问的是父类中的show方法,那么访问父类中的show方法时,show方法里面访问的number为什么是父类的成员变量,而不是子类的成员变量呢?因为此时的this指向的对象,其里面存放的成员变量应该是this.number应该等于8,super.number应该等于3。难道我要理解此时new出来的对象类型是Father类型的?
解决方案九:
现在show是父类的,这个number就解释成父类的。这些是在编译时确定使用谁的方法和变量,不是等到运行时。你提到的number解释成this.number是在暗示在运行的时候。引用7楼u010021183的回复:
我想问的是,为什么Sonssons=newSons();sons.show();如果show方法没有在子类中覆盖,那么此时sons.show();其实访问的是父类中的show方法,那么访问父类中的show方法时,show方法里面访问的number为什么是父类的成员变量,而不是子类的成员变量呢?因为此时的this指向的对象,其里面存放的成员变量应该是this.number应该等于8,super.number应该等于3。难道我要理解此时new出来的对象类型是Father类型的?
解决方案十:
那多态的时候,调用了子类覆盖父类的方法,我看到相关文章说,在编译的时候不知道调用谁的成员变量和成员方法,但在运行时才知道调用谁的成员变量和成员方法,和你说的岂不是矛盾吗?
解决方案十一:
代码有个很明显的问题,一个类里面不只能有一个public么
解决方案十二:
那把这句话改一下:用谁的方法就找和谁相关的成员变量。引用9楼u010021183的回复:
那多态的时候,调用了子类覆盖父类的方法,我看到相关文章说,在编译的时候不知道调用谁的成员变量和成员方法,但在运行时才知道调用谁的成员变量和成员方法,和你说的岂不是矛盾吗?
解决方案十三:
Quote: 引用11楼humanity的回复:
那把这句话改一下:用谁的方法就找和谁相关的成员变量。这正是我无法从内存图的角度去理解的内容。
解决方案十四:
在java中只有普通方法(除static和private之外)的调用是多态的,而域的访问是前期绑定而不是动态绑定的。也就是说编译上面的代码后,在内存中son同时拥有两个版本的number,一个从基类继承而来,另一个则为默认成员,当你没有在son中覆盖show方法时,很明显son.show调用的是基类版本的show方法,由于域是前期绑定的,也就是在编译期决定了基类show方法;中的number版本是基类版本的number。显然,在Son中覆盖了show后又是另一回事了
解决方案十五:
引用9楼u010021183的回复:
那多态的时候,调用了子类覆盖父类的方法,我看到相关文章说,在编译的时候不知道调用谁的成员变量和成员方法,但在运行时才知道调用谁的成员变量和成员方法,和你说的岂不是矛盾吗?成员变量应该是前期绑定的,普通方法(非static和final)才是动态绑定的
时间: 2024-09-13 03:47:31