Java继承之再谈构造器
Java继承之再谈构造器
初始化基类
前面提到,继承是子类对父类的拓展。《Thinking in Java》中提到下面一段话:
当创建一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与你用基类直接创建的对象是一样的。二者区别在于,后者来自于外部,而基类的子对象被包装在导出类的对象内部。
我们在创建子类对象时,调用了父类的构造器,甚至父类的父类构造器。我们知道,构造器用于创建对象,那么突然产生疑惑:关于创建一个子类对象时,是否会先创建父类对象?
经过查找资料,得出结论:
并没有。在创建子类对象时,会把父类的成员变量和方法加载进内存,既然要加载,便调用父类构造器看看这些数据是如何进行初始化的,仅此而已,并不是创建了父类的对象。
所以,可以看作,子类对象中包含着父类的子对象。我们知道,对象的初始化是至关重要的。那么,这个父类的子对象如何正确初始化呢?对了,就是接下来要说的:在构造器中调用基类构造器来执行初始化。
注意:子类并不能继承父类的构造器,只是单纯调用了基类构造器中的初始化代码。
默认构造器
先看一段简单的测试代码:
package com.my.pac13;
/*继承中的构造*/
public class Person {
Person(){
System.out.println("Person()");
}
}
class Student extends Person{
Student(){
System.out.println("Student()");
}
}
class PrimaryStudent extends Student{
PrimaryStudent(){
//super();
System.out.println("PrimaryStudent()");
}
public static void main(String[] args) {
//创建了PrimaryStudent对象
new PrimaryStudent();
}
}
/*
Person()
Student()
PrimaryStudent()
*/
关于构造器,我们前面提到,任何没有显式构造器的类都存在着一个无参数的默认构造器。我们上面的例子在默认构造器中加入了打印输出,以便理解。
可以看到的是:
- 在创建
PrimaryStudent时,他的直接父类Student和间接父类Person中的构造器都被调用了,而且可以看到,是"自上而下"的。 - 父类在子类构造器可以访问它之前,就已经完成了初始化的操作。
- 若子类没有显式调用父类的构造器,则自动调用父类的默认(无参)构造器。
带参数的构造器
前面的代码中,每个类都含有默认的构造器,创建子类对象时,是自上而下,且子类会默认调用父类的无参构造器。那么,假设父类正好没有无参构造器或者你正想调用父类的带参构造器,这时就需要我们的super关键字。(super关键字之后还会进行总结)
我们直接在原来的基础上稍作修改,并进行测试。
package com.my.pac13;
/*调用基类构造器是子类构造器中要做的第一件事*/
public class Person {
//没有默认构造器
Person(String name){
System.out.println("Person()\t"+name);
}
}
class Student extends Person{
//也没有默认构造器,且用super显式调用
Student(String n){
//super关键字调用父类的构造器
super(n);
System.out.println("一参数Student\t"+n);
}
Student(String n,String m){
//this关键字调用同一类中重载的构造器
this(n);
System.out.println("二参数student()\t"+m);
}
}
class PrimaryStudent extends Student{
//隐式调用父类构无参数构造器,但是父类没有,所以要用super显式调用
PrimaryStudent(){
//没有下面的语句会报错
super("hello");
System.out.println("PrimaryStudent()");
}
}
class ExtendsTest{
public static void main(String[] args) {
new Person("the shy");
System.out.println("***********");
new Student("rookie");
System.out.println("***********");
new Student("the shy","rookie");
System.out.println("***********");
new PrimaryStudent();
System.out.println("***********");
}
}
/*
Person() the shy
***********
Person() rookie
一参数Student rookie
***********
Person() the shy
一参数Student the shy
二参数student() rookie
***********
Person() hello
一参数Student hello
PrimaryStudent()
***********
*/
- this是正在创建的对象,用于调用同一类中重载的构造器,可以参看我之前的文章:Java关键字之this。
- super在调用构造器时,使用方法和this相似。(但super和this本身有本质的不同,super并不是一个对象的引用!!!)
- super和this语句都必须出现在第一行,也就是说一个构造器中只能有其中之一。
子类调用父类构造器
无论是否使用super语句来调用父类构造器的初始化代码,子类构造器总是会事先调用父类构造器!这是一定要记住的!
- 子类构造器A在第一行显式使用super调用父类构造器B,格式
super(参数列表),根据参数列表选择对应的父类构造器。
//父类
Person(String name){
System.out.println("Person()\t"+name);
}
//子类
Student(String n){
//super关键字调用父类的构造器
super(n);
System.out.println("一参数Student\t"+n);
}
- 子类构造器A先用this调用本类重载的构造器B,然后B调用父类构造器。
//父类
Person(String name){
System.out.println("Person()\t"+name);
}
//子类
Student(String n){
//super关键字调用父类的构造器
super(n);
System.out.println("一参数Student\t"+n);
}
Student(String n,String m){
//this关键字调用同一类中重载的构造器
this(n);
System.out.println("二参数student()\t"+m);
}
- 子类构造器中没有super和this时,系统会隐式调用父类的无参构造器,要是没有无参的,那就报错。
//隐式调用父类构无参数构造器,但是父类没有,所以要用super显式调用
PrimaryStudent(){
//没有下面的语句会报错
super("hello");
System.out.println("PrimaryStudent()");
}
综上所述:
当调用子类构造器对子类对象进行初始化时,父类构造器总会在子类构造器之前执行。甚至,父类的父类会在父类之前执行……一直追溯到所有类的超类Object类的构造器。
参考书籍:《Thinking in Java》、《疯狂Java讲义》、《Java核心技术卷I》
Java继承之再谈构造器的更多相关文章
- Javascript继承,再谈
说到Javascript的继承,相信只要是前端开发者都有所了解或应用,因为这是太基础的知识了.但不知各位有没有深入去理解其中的玄机与奥秘.今本人不才,但也想用自己的理解来说一说这其中的玄机和奥秘. 一 ...
- Java 并发编程-再谈 AbstractQueuedSynchronizer 3 :基于 AbstractQueuedSynchronizer 的并发类实现
公平模式ReentrantLock实现原理 前面的文章研究了AbstractQueuedSynchronizer的独占锁和共享锁,有了前两篇文章的基础,就可以乘胜追击,看一下基于AbstractQue ...
- Java 并发编程-再谈 AbstractQueuedSynchronizer 2:共享模式与基于 Condition 的等待 / 通知机制实现
共享模式acquire实现流程 上文我们讲解了AbstractQueuedSynchronizer独占模式的acquire实现流程,本文趁热打铁继续看一下AbstractQueuedSynchroni ...
- Java并发编程-再谈 AbstractQueuedSynchronizer 1 :独占模式
关于AbstractQueuedSynchronizer JDK1.5之后引入了并发包java.util.concurrent,大大提高了Java程序的并发性能.关于java.util.concurr ...
- C++中数据对齐问题。struct、union、enum,类继承。再谈sizeof()
首先是struct,在C++中,结构体其实和class有很大的相似了.但是有一点不同的是,struct默认是public,而class中是private. 当然,struct继承等用法也是可以的. 共 ...
- 【Java基础】--再谈面向对象
面向对象总结 V1.0 2014.9.14 面向对象总结V2.0 2015.8.14 对照之前的J2SE总结,发现现在的似乎更加的注重联系,開始能把细节的东西都编制到知识网络里面,导图的图 ...
- 再谈Java数据结构—分析底层实现与应用注意事项
在回顾js数据结构,写<再谈js对象数据结构底层实现原理-object array map set>系列的时候,在来整理下java的数据结构. java把内存分两种:一种是栈内存,另一种是 ...
- 沉淀再出发:再谈java的多线程机制
沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于ja ...
- lesson 4 再谈继承多态,抽象类和接口
再谈多态,抽象类和接口 上一次博客已经概念性的概述了继承多态,抽象类和接口,这次来具体的谈一谈他们之间的联系和需要注意的地方. 一.继承和多态:Inheritance (继承) & Polym ...
随机推荐
- T-SQL Part IX, PIVOT and UNPIVOT
不同于CROSS JOIN, CROSS APPLY, OUTER APPLY,MSDN文档对PIVOT和UNPIVOT 想得重视了一点,单独做了一个页面来介绍. 简单来说,PIVOT用来把行转成列, ...
- 过滤条件的时候用between和<>的区别
无论是sqlsever还是oracle都支持between函数, 2个函数的基本语法是 WHERE A BETWEEN 1 AND 2/ WHERE A >=1 AND A <=2 ,be ...
- 详解JavaScript错误捕获和上报流程
怎么捕获错误并且处理,是一门语言必备的知识.在JavaScript中也是如此. 那怎么捕获错误呢?初看好像很简单,try-catch就可以了嘛!但是有的时候我们发现情况却繁多复杂. Q1: 同步可以t ...
- [springboot 开发单体web shop] 7. 多种形式提供商品列表
上文回顾 上节 我们实现了仿jd的轮播广告以及商品分类的功能,并且讲解了不同的注入方式,本节我们将继续实现我们的电商主业务,商品信息的展示. 需求分析 首先,在我们开始本节编码之前,我们先来分析一下都 ...
- 领扣(LeetCode)二叉树的右视图 个人题解
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值. 示例: 输入: [1,2,3,null,5,null,4] 输出: [1, 3, 4] 解释: 1 < ...
- source for "Android 28 platform" not found
source for "Android 28 platform" not found 解决办法:点击右上角的Download,但是下载完点击Refresh之后没有反应,这时候需要重 ...
- 20191010-5 alpha week 1/2 Scrum立会报告+燃尽图 03
此作业要求参见[https://edu.cnblogs.com/campus/nenu/2019fall/homework/8748] 一.小组情况 队名:扛把子 组长:迟俊文 组员:宋晓丽 梁梦瑶 ...
- 使用node.js开发一个生成逐帧动画小工具
在实际工作中我们已经下下来不下于一万个npm包了,像我们熟悉的 vue-cli,react-native-cli 等,只需要输入简单的命令 vue init webpack project,即可快速帮 ...
- JavaScript笔记二
1.表格 - 在网页中可以通过表格来表示一些格式化的数据 - 表格相关的标签 - <table> 用来创建一个表格 - <tr> 表示表格中的一行 - <th> 表 ...
- 快速入门函数式编程——以Javascript为例
函数式编程是在不改变状态和数据的情况下使用表达式和函数来编写程序的一种编程范式.通过遵守这种范式,我们能够编写更清晰易懂.更能抵御bug的代码.这是通过避免使用流控制语句(for.while.brea ...