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

前言

工作之余回头巩固一下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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 本章将会介绍 存储属性的初始赋值自定义构造过程默认构造器值类型的构造器代理类的继承和构造过程可失败构造器必要构造器...
    寒桥阅读 4,134评论 0 0
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 32,875评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,058评论 19 139
  • 构造过程是使用类、结构体或枚举类型的实例之前的准备过程。在新实例可用前必须执行这个过程,具体操作包括设置实例中每个...
    莽原奔马668阅读 3,947评论 0 3
  • 去年,简书开始推出“作者签约计划”,彭小六成了简书首批签约作者,随后,在简书的签约作者们之间,小六很快有了一个外号...
    刘淼阅读 7,896评论 9 23

友情链接更多精彩内容