在《Java编程思想》第7章复用类中有这样一段话,值得深思。当子类继承了父类时,就涉及到了基类和导出类(子类)这两个类。从外部来看,导出类就像是一个与基类具有相同接口的新类,或许还会有一些额外的方法和域。但继承并不只是复制基类的接口。当创建一个导出类对象时,该对象包含了一个基类的子对象,这个子对象与你用基类直接创建的对象是一样的,二者区别在于,后者来自于外部,而基类的子对象是被包裹在导出类对象内部。

这就引发出了一个很重要的问题,对基类子对象的正确初始化也是至关重要的(我们可能在子类的使用基类中继承的方法和域),而且也仅有一种方法来保证这一点:在子类构造器中调用基类构造器来执行初始化。

无参的基类构造器

我们知道,当一个类你没有给他构造函数,Java会自动帮你调用无参的构造器,同时Java也会在导出类的构造器中插入对基类构造器的调用。下面的代码说明了这个工作机制:

//: reusing/Cartoon.java
// Constructor calls during inheritance.
import static net.mindview.util.Print.*; class Art {
Art() { print("Art constructor"); }
} class Drawing extends Art {
Drawing() { print("Drawing constructor"); }
} public class Cartoon extends Drawing {
public Cartoon() { print("Cartoon constructor"); }
public static void main(String[] args) {
Cartoon x = new Cartoon();
}
} /* Output:
Art constructor
Drawing constructor
Cartoon constructor
*///:~

观察上述代码的运行结果,在创建Cartoon对象时,会先调用其父类Drawing的构造器,而其父类又继承自Art类,所以又会调用Art类的构造器,就像层层往上。虽然在其构造器中都没有显式调用其父类构造器,但是Java会自动调用其父类的构造器。即使不为Cartoon()创建构造器,编译器也会合成一个默认的无参构造器,该构造器将调用基类的构造器。

带参数的基类构造器

当基类中的构造器都是带有参数时,编译器就不会自动调用,必须用关键字super显式地调用基类构造器,并且传入适当的参数,相应的例子代码如下:

//: reusing/Chess.java
// Inheritance, constructors and arguments.
import static net.mindview.util.Print.*; class Game {
Game(int i) {
print("Game constructor");
}
} class BoardGame extends Game {
BoardGame(int i) {
super(i);
print("BoardGame constructor");
}
} public class Chess extends BoardGame {
Chess() {
super(11);
print("Chess constructor");
}
public static void main(String[] args) {
Chess x = new Chess();
}
} /* Output:
Game constructor
BoardGame constructor
Chess constructor
*///:~

从上述代码中可以观察到,必须在子类Chess构造器中显示的使用super调用父类构造器并传入适当参数。而且,调用基类构造器必须是在子类构造器中做的第一件事。

基类构造器的调用顺序问题

在此之前,我们先来探讨一下对象引用的初始化问题。在Java中,类中域为基本类型时能够自动被初始化为零,但是对象引用会被初始化为null。我们往往需要在合适的位置对其进行初始化,下面是几个可以进行初始化的位置:

1.在定义对象的地方。这意味着它们总是能够在构造器被调用之前被初始化

2.在类的构造器中。

3.就在正要使用这些对象之前,这种方式称为惰性初始化。

记住上面的第1点,下面看一个比较复杂的例子来看一下基类构造器的调用顺序问题。

// reusing/Ex7/C7.java
// TIJ4 Chapter Reusing, Exercise 7, page 246
/* Modify Exercise 5 so that A and B have constructors with arguments instead
* of default constructors. Write a constructor for C and perform all
* initialization within C's constructor.
*/ import static org.greggordon.tools.Print.*; class A {
A(char c, int i) { println("A(char, int)");}
} class B extends A {
B(String s, float f){
super(' ', 0);
println("B(String, float)");
}
} class C7 extends A {
private char c;
private int i;
C7(char a, int j) {
super(a, j);
c = a;
i = j;
}
B b = new B("hi", 1f); // will then construct another A and then a B
public static void main(String[] args) {
C7 c = new C7('b', 2); // will construct an A first
}
}

上述这段代码输出:

A(char, int)

A(char, int)

B(String, float)

注意基类构造器、子类构造器、类的成员对象初始化的顺序:

1.在new一个类的对象时,首先调用其父类构造器(可以是无参的和有参的,无参的系统会自动调用,有参的需要自己指定)。如上述C7中的super(a, j)

2.然后执行其成员对象初始化语句,调用B类构造器,如上述中的

B b = new B("hi", 1f),而B的构造器又会先调用基类A的构造器。

3.最后返回到C7中的构造器,继续执行c=a,i=j。

参考:

Java编程思想复用类练习7

https://www.zhihu.com/question/49196023

关于Java中基类构造器的调用问题的更多相关文章

  1. 【JAVA零基础入门系列】Day11 Java中的类和对象

    今天要说的是Java中两个非常重要的概念--类和对象. 什么是类,什么又是对象呢?类是对特定集合的概括描述,比如,人,这个类,外观特征上,有名字,有年龄,能说话,能吃饭等等,这是我们作为人类的相同特征 ...

  2. 详解C++中基类与派生类的转换以及虚基类

    很详细!转载链接 C++基类与派生类的转换在公用继承.私有继承和保护继承中,只有公用继承能较好地保留基类的特征,它保留了除构造函数和析构函数以外的基类所有成员,基类的公用或保护成员的访问权限在派生类中 ...

  3. 基础知识(05) -- Java中的类

    Java中的类 1.类的概念 2.类中的封装 3.对象的三大特征 4.对象状态 5.类与类之间的关系 ------------------------------------------------- ...

  4. JAVA中的类和接口

    1.类: 类是具有相同属性和方法的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和方法两个主要部分.在面向对象的编程语言中,类是一个独立的程序单位,它应该有一个类名并包括属 ...

  5. java中Color类的简单总结

    java中Color类的简单总结 1.颜色的常识 任何颜色都是由三原色组成(RGB),JAVA中支持224为彩色,即红绿蓝分量取值 介于0-255之间(8位表示) 2.Color类中的常量 publi ...

  6. java中的类和对象

    Java中的类是一个模板,它用于描述一类对象的行为和状态. 对象则是类中的一个实例,对象有状态(属性)和行为(方法).例如一条狗就是一个对象,他的状态就是他的颜色,名字,品种:他的行为就是叫,摇尾巴, ...

  7. C++中基类的析构函数为什么要用virtual虚析构函数

    知识背景 要弄明白这个问题,首先要了解下C++中的动态绑定. 关于动态绑定的讲解,请参阅:  C++中的动态类型与动态绑定.虚函数.多态实现 正题 直接的讲,C++中基类采用virtual虚析构函数是 ...

  8. 第四节:详细讲解Java中的类和面向对象思想

    前言 大家好,给大家带来详细讲解Java中的类和面向对象思想的概述,希望你们喜欢 类和面向对象 在Java中怎样理解对象,创建对象和引用:什么是引用,对于基础学习的同学,要深入了解引用.示例:Stri ...

  9. Java中Optional类的使用

    从 Java 8 引入的一个很有趣的特性是 Optional  类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都 ...

随机推荐

  1. 卷积神经网络之VGG

    2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发出了新的深度卷积神经网络:VGGNet,并取得了ILSVRC2014比 ...

  2. C# 设置Excel中的数字字符串格式

    在Excel中,数字字符串用不同格式表示,可代表不同数据意义.例如在财务报表里需要用特定的数字字符串格式来反映金额信息.货币币种.数据精确程度.增减趋势等等.下面分享如何通过C#编程来设置Excel表 ...

  3. 史诗级Java资源大全中文版

    本文来自GitHub 上 Awesome - java 系列的资源整理.awesome-java 就是 akullpp 发起维护的 Java 资源列表,内容包括:构建工具.数据库.框架.模板.安全.代 ...

  4. 入门rocketmq从浅到深

    目录 一.引言 二.介绍 三.Rocketmq关键概念 1.主题与标签 2.发送与订阅群组 3.Broker与NameServer 4.广播消费与集群消费 5.消息队列 6.集群方式 7.顺序消息 8 ...

  5. Cocos Creator—最佳构建部署实践

    这篇文章主要是我们团队在使用Cocos Creator过程中的一些关于部署方面的实践总结,标题党了一回,严格来说,应该是<快看漫画游戏研发团队使用Cocos Creator构建部署最佳实践> ...

  6. Dubbo 支持哪些序列化协议?

    面试题 dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的? 面试官心理分析 上一个问题,说说 dubbo 的基本工作原理 ...

  7. python实现列表的排序

    群里有同行遇到这样一个面试题:有一个整数构成的列表,需要给这个列表进行从小到大存入到另一个列表中. 本身排序可以用python的内置函数sort和sorted,但题目的要求是手动实现. 看起来很简单, ...

  8. Java数据结构和算法 - 高级排序

    希尔排序 Q: 什么是希尔排序? A: 希尔排序因计算机科学家Donald L.Shell而得名,他在1959年发现了希尔排序算法. A: 希尔排序基于插入排序,但是增加了一个新的特性,大大地提高了插 ...

  9. 你连Nginx怎么转发给你请求都说不清楚,还好意思说自己不是CRUD工程师?

    目录 一.Nginx工作原理二.Nginx进程模型三.Nginx处理HTTP请求流程 Nginx 工作原理 Nginx由内核和模块组成,Nginx本身做的工作实际很少,当它接到一个HTTP请求时,它仅 ...

  10. 从一张图开始,谈一谈.NET Core和前后端技术的演进之路

    从一张图开始,谈一谈.NET Core和前后端技术的演进之路 邹溪源,李文强,来自长沙.NET技术社区 一张图 2019年3月10日,在长沙.NET 技术社区组织的技术沙龙<.NET Core和 ...