关于Java中基类构造器的调用问题
在《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中基类构造器的调用问题的更多相关文章
- 【JAVA零基础入门系列】Day11 Java中的类和对象
今天要说的是Java中两个非常重要的概念--类和对象. 什么是类,什么又是对象呢?类是对特定集合的概括描述,比如,人,这个类,外观特征上,有名字,有年龄,能说话,能吃饭等等,这是我们作为人类的相同特征 ...
- 详解C++中基类与派生类的转换以及虚基类
很详细!转载链接 C++基类与派生类的转换在公用继承.私有继承和保护继承中,只有公用继承能较好地保留基类的特征,它保留了除构造函数和析构函数以外的基类所有成员,基类的公用或保护成员的访问权限在派生类中 ...
- 基础知识(05) -- Java中的类
Java中的类 1.类的概念 2.类中的封装 3.对象的三大特征 4.对象状态 5.类与类之间的关系 ------------------------------------------------- ...
- JAVA中的类和接口
1.类: 类是具有相同属性和方法的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和方法两个主要部分.在面向对象的编程语言中,类是一个独立的程序单位,它应该有一个类名并包括属 ...
- java中Color类的简单总结
java中Color类的简单总结 1.颜色的常识 任何颜色都是由三原色组成(RGB),JAVA中支持224为彩色,即红绿蓝分量取值 介于0-255之间(8位表示) 2.Color类中的常量 publi ...
- java中的类和对象
Java中的类是一个模板,它用于描述一类对象的行为和状态. 对象则是类中的一个实例,对象有状态(属性)和行为(方法).例如一条狗就是一个对象,他的状态就是他的颜色,名字,品种:他的行为就是叫,摇尾巴, ...
- C++中基类的析构函数为什么要用virtual虚析构函数
知识背景 要弄明白这个问题,首先要了解下C++中的动态绑定. 关于动态绑定的讲解,请参阅: C++中的动态类型与动态绑定.虚函数.多态实现 正题 直接的讲,C++中基类采用virtual虚析构函数是 ...
- 第四节:详细讲解Java中的类和面向对象思想
前言 大家好,给大家带来详细讲解Java中的类和面向对象思想的概述,希望你们喜欢 类和面向对象 在Java中怎样理解对象,创建对象和引用:什么是引用,对于基础学习的同学,要深入了解引用.示例:Stri ...
- Java中Optional类的使用
从 Java 8 引入的一个很有趣的特性是 Optional 类.Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都 ...
随机推荐
- Android项目中独立Git项目分库后的编译调试时Gradle的配置
基于AS开发项目,对于特定的功能模块,往往抽取成独立的库进行管理,然后上传到Marven库中,通过Gradle依赖的方式进行引用. 其优势体现在: 1,独立的Git项目库,模块功能,及职责界定清晰: ...
- C# 将object对象转换为实体对象
C# 将object对象转换为实体对象.一共两种方法. 第一种方法,代码如下: /// <summary> /// 将object对象转换为实体对象 /// </summary> ...
- swagger Failed to load Api definition 的问题
这个问题是由于Tomcat乱码问题导致的,修改server.xml文件的编码格式修改成UTF-8
- 【学习笔记Part 2● MySQL】
数据库 为什么要用数据库 如何去存放数据?生活中有各种各样的数据.比如说人的姓名.年龄.成绩等.平时我们记录这些信息都是记在大脑中.人的记忆力有限,不可能什么都记住.所以后来人们把数据记录在石头上–& ...
- [ArcGIS API for JavaScript 4.8] Sample Code-Get Started-MapView,SceneView简介
[官方文档:https://developers.arcgis.com/javascript/latest/sample-code/index.html] 一.Intro to MapView(2D ...
- 20170310 - Python 3 下 SQLAlchemy 的 MySQL 数据库 URI 配置
MySQL-Python 只用于 Python 2,URI配置为 mysql://username:password@server/db Python 3 下要使用另一个 PyMySQL 包,相应的U ...
- Codeforces Round #539 (Div. 2) - D. Sasha and One More Name(思维)
Problem Codeforces Round #539 (Div. 2) - D. Sasha and One More Name Time Limit: 1000 mSec Problem ...
- 快速构建H5单页面切换应用
在Web App和Hybrid App横行的时代,为了拥有更好的用户体验,单页面应用顺势而生,单页面应用简称`SPA`,即Single Page Application,就是只有一个HTML页面的应用 ...
- Vs 中关于项目中的某 NuGet 程序包还原失败:找不到“xxx”版本的程序包“xxx”
问题: 首先出现这个bug的是在我的vs2017社区版的ide上,这两天使用了出现了一个非常神奇的问题,就是我程序中的nuget包总提示找不到源文件,并且我点击Nuget还原的话还一直提示着一 ...
- #Java学习之路——基础阶段(第十一篇)
我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...