构造方法内部的多态方法的行为

序:
希望此文可给站在java起点上的J友们一点启示
在此感谢MARS,久城等朋友给我的帮助

引子:昨天在一群里看到如下一段代码,让我郁闷了一下午:
class Super{
int i = 10;
public Supper(){
print();
i = 20 ;
}

void print(){
System.out.println(i);
}
}
class Sub extends Supper{
int i = 30;
public Sub(){
print();
super.print();
i = 40;
}

void print(){
System.out.println(i);
}
}
public class TestExtends{
public static void main(String [] args){
new Sub();
}
}
对于这道题,我的答案是:10 30 20
可运行的结果是: 0 30 20
看到答案让我很是费解,按照方法的调用顺序:在调用子类的构造方法之前先调用父类的构造方法,在执行构造方法的语句之前先初始化成员变量。按照这个顺序得到的应该是我预期的答案。
在解答此问题之前,让我们先回顾一下多态的几个知识点:
多态:在运行时刻接口匹配的对象能互换的能力,即,多态使得你可以向对待父类的成员那样对待派生类的成员。简单的说:就是用基类的引用指向之类的对象
多态的三个必要条件:
1)要有继承
2)要有重写(覆盖)
3)父类引用指向子类对象
在Java语言机制中,多态表现为后绑定(或者叫动态绑定、晚期绑定),有两种形式:继承父类并重载父类中具有相同签名的方法(即overriding或者overwriting);实现抽象父类(或接口)中具有相同签名的方法。当你对由父类声明的对象调用该方法时,对应的子类中的方法就会被调用。即运行时会根据对象类型找覆盖之后的方法;
一般的方法内部,动态绑定的调用是在运行时才决定的,因为对象无法知道它是属于方法所在的那个类,还是属于那个类的子类。
如果要调用构造方法内部的一个动态绑定方法,就要用到那个方法的被覆盖后的定义。然而这个调用的效果可能相当难以预料,因此被覆盖的方法在对象被完全构造之前就会被调用
从概念上讲,构造方法的工作实际上是创建对象。在任何构造方法内部,整个对象可能只是部分形成--我们知道基类已经进行初始化。如果构造方法只是在构建对象过程中的一个步骤,并该对象所属于的类是从构造方法所属的类导出的,那么导出部分在当前构造方法被调用的时刻仍旧是没有被初始化的。然而,一个动态绑定的方法调用却会向外深入到继承层次结构内部,它可以调用子类里的方法。如果我们是在构造方法内部这样做,那么就可能会调用某个方法,而这个方法所操纵的成员可能还未进行初始化。
论证:
class Glyph{
void draw(){ print("Glyph.draw()");}
Glyph(){
print("Glyph() before draw()");
draw();
print("Glyph() before draw()");
}

public static void print(String s){
System.out.println(s);
}
}
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){
radius = r;
print("RoundGlyph.RoundGlyph(), radius = " + radius);
}
void draw(){
print("RoundGlyph.draw(), radius = " + radius);
}
}
public class PolyConstructors{
public static void main(String[] args){
new RoundGlyph(5);
}
}
输出结果:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() before draw()
RoundGlyph.RoundGlyph(), radius = 5
说明:Glyph.draw()方法设计为将要被覆盖,这种覆盖是在RoundGlyph中发生的。但是Glyph构造方法会调用这个方法,结果导致了对RoundGlyph.,draw()的调用,这看起来似乎是我们的目的。但是如果看输出结果,我们会发现当Glyph的构造方法调用draw()方法时,radius不是默认初始值1,而是0。
揭秘:初始化的实际过程:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
2)调用基类构造方法。此时,被覆盖后的draw()方法(要在调用RoundGlyph构造方法之前调用),由于步骤1的缘故,我们此时会发现radius的值为0.
3)按照声明的顺序调用成员的初始化方法。
4)调用子类的构造方法主体
这样做的优点是什么呢?那就是所有东西都至少初始化为零(或者是某些特殊数据类型中与“零”等价的值)而不是仅仅留作垃圾。其中包括通过组合而嵌入一个类内部的对象引用其值是null。
到这里,也就明白了开头那道问题的输出,调用Sub的时候先调用其构造方法。由于Sub继承了Super,所以又调用Super的构造方法,而print方法被重写了,调用Sub的print方法,这时候Sub还没有加载,i还没被赋值,所以输出默认值0。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值