Java学习笔记--构造器的调用顺序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Liuhe_5656/article/details/78166486

前言

工作之余回头巩固一下Java知识,只有基础牢固了才能走的更远。

  基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接, 以使每个基类的构造器都能得到调用。导出类只能访问自己的成员,访问不了基类中private的成员,只有基类的构造器有权限对自己的元素进行初始化。因此,必须令所有的构造器都得到调用,否则就不可能正确的构造完整的对象。

示例:

class Meal{
    Meal(){
        System.out.println("Meal()");
    }
}

class Chesses{
    Chesses(){
        System.out.println("Chesses()");
    }
}

class Bread{
    Bread(){
        System.out.println("Bread()");
    }
}

class Lettuce{
    Lettuce(){
        System.out.println("Lettuce()");
    }
}

class Lunch extends Meal{
    Lunch() {
        System.out.println("Lunch()");
    }
}

class ProtableLunch extends Lunch{
    ProtableLunch() {
        System.out.println("ProtableLunch");
    }
}

public class Sandwich extends ProtableLunch{

    private Bread bread = new Bread();
    private Chesses chess = new Chesses();
    private Lettuce lettuce = new Lettuce();
    public Sandwich() {
        System.out.println("Sandwich()");
    }

    public static void main(String[] args) {
        Sandwich sandwich = new Sandwich();
    }

}
---------------------------------------------------
Output:

Meal()
Lunch()
ProtableLunch
Bread()
Chesses()
Lettuce()
Sandwich

由输出结果可见,构造器的大致初始化顺序如下:

  • 1)调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层的导出类,等等,知道最底层的导出类。
  • 2) 按照声明顺序调用成员的初始化方法。
  • 3)调用导出类构造器的主体。

按照这样的规则就能保证所有的基类成员以及当前对象的成员对象都得到了初始化。但是有一种特殊情况。

示例:

class Glyph{

    void draw(){
        System.out.println("Glyph.Draw()");
    }

    Glyph(){
        System.out.println("Glyph() before draw()");
        draw();
        System.out.println("Glyph() after draw()");
    }

}

class RoundGlyph extends Glyph{
    private int radius = 1;
    RoundGlyph(int i){
        radius = i;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }

    @Override
    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}

public class PloyConstructors {

    public static void main(String[] args) {
        new RoundGlyph(5);
    }

}

-------------------------------------------------------
Output:

Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5

在输出结果当中radius的输出结果是0,并不是默认的初始值1。所以上面所说的初始化顺序并不完整。

构造器的实际初始化顺序如下:

  • 1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。
  • 2)如前所述那样调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤2的缘故,我么此时会发现radius的值为0。
  • 3)按照声明顺序调用成员的初始化方法。
  • 4)调用导出类构造器的主体。

因为我们在Glyph的构造器中调用draw()方法的时候,RoundGlyph的构造器还没有得到调用,所以他的存储空间此时是初始化为二进制0的,所以radius的输出结果是0。这样的错误编译器不会报错,我们也很难发现。编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,尽量避免调用其他方法。”在构造器当中唯一能够安全调用的那些方法就是基类当中的final方法(也适用于private方法,他们自动属于final方法)。这些方法不会被覆盖,因此不会出现上面的问题。
  
  
  
原文地址:http://blog.csdn.net/Liuhe_5656/article/details/78166486
  
  
  

猜你喜欢

转载自blog.csdn.net/Liuhe_5656/article/details/78166486