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继承之再谈构造器的更多相关文章

  1. Javascript继承,再谈

    说到Javascript的继承,相信只要是前端开发者都有所了解或应用,因为这是太基础的知识了.但不知各位有没有深入去理解其中的玄机与奥秘.今本人不才,但也想用自己的理解来说一说这其中的玄机和奥秘. 一 ...

  2. Java 并发编程-再谈 AbstractQueuedSynchronizer 3 :基于 AbstractQueuedSynchronizer 的并发类实现

    公平模式ReentrantLock实现原理 前面的文章研究了AbstractQueuedSynchronizer的独占锁和共享锁,有了前两篇文章的基础,就可以乘胜追击,看一下基于AbstractQue ...

  3. Java 并发编程-再谈 AbstractQueuedSynchronizer 2:共享模式与基于 Condition 的等待 / 通知机制实现

    共享模式acquire实现流程 上文我们讲解了AbstractQueuedSynchronizer独占模式的acquire实现流程,本文趁热打铁继续看一下AbstractQueuedSynchroni ...

  4. Java并发编程-再谈 AbstractQueuedSynchronizer 1 :独占模式

    关于AbstractQueuedSynchronizer JDK1.5之后引入了并发包java.util.concurrent,大大提高了Java程序的并发性能.关于java.util.concurr ...

  5. C++中数据对齐问题。struct、union、enum,类继承。再谈sizeof()

    首先是struct,在C++中,结构体其实和class有很大的相似了.但是有一点不同的是,struct默认是public,而class中是private. 当然,struct继承等用法也是可以的. 共 ...

  6. 【Java基础】--再谈面向对象

    面向对象总结 V1.0     2014.9.14 面向对象总结V2.0   2015.8.14 对照之前的J2SE总结,发现现在的似乎更加的注重联系,開始能把细节的东西都编制到知识网络里面,导图的图 ...

  7. 再谈Java数据结构—分析底层实现与应用注意事项

    在回顾js数据结构,写<再谈js对象数据结构底层实现原理-object array map set>系列的时候,在来整理下java的数据结构. java把内存分两种:一种是栈内存,另一种是 ...

  8. 沉淀再出发:再谈java的多线程机制

    沉淀再出发:再谈java的多线程机制 一.前言 自从我们学习了操作系统之后,对于其中的线程和进程就有了非常深刻的理解,但是,我们可能在C,C++语言之中尝试过这些机制,并且做过相应的实验,但是对于ja ...

  9. lesson 4 再谈继承多态,抽象类和接口

    再谈多态,抽象类和接口 上一次博客已经概念性的概述了继承多态,抽象类和接口,这次来具体的谈一谈他们之间的联系和需要注意的地方. 一.继承和多态:Inheritance (继承) & Polym ...

随机推荐

  1. Spring 框架常用语法进行总结

    Spring 框架常用语法进行总结: spring框架的二大主要的功能就是IOC和AOP. IOC: 控制反转(依赖注入) AOP: 面向切面编程 学习spring最好的方法就是去看官网,里面有详细的 ...

  2. 总结一下 IEnumerable 的例子

    本篇将围绕 <试试 IEnumerable 的 10 个小例子>和<试试 IEnumerable 的另外 6 个小例子>给出的例子,总结一下对于 IEnumerable 接口的 ...

  3. 2019CSP day1t1 格雷码

    题目描述 通常,人们习惯将所有 \(n\) 位二进制串按照字典序排列,例如所有 \(2\) 位二进制串按字典序从小到大排列为:\(00,01,11,10\). 格雷码(\(Gray Code\))是一 ...

  4. 《Java多线程面试题》系列-创建线程的三种方法及其区别

    1. 创建线程的三种方法及其区别 1.1 继承Thread类 首先,定义Thread类的子类并重写run()方法: package com.zwwhnly.springbootaction.javab ...

  5. Java自动生成数据

    最近在造数据库中的表数据,写了些数据生成类 可以随机生成姓名.性别,民族,出生日期,身份证号,手机号,邮箱,身高,文化程度,地址,单位,日期时间,编码等 package com.util.create ...

  6. ES6学习 let const

    1.前言 发现网易云笔记 单纯的记笔记没什么意思,所以今天来博客园写学习感受了,毕设做了休息时间就来写写新学的知识 哈哈哈 !! 2.ES6 就是JavaScript 语言的下一代标准,2015年6月 ...

  7. ZeroC ICE的远程调用框架 代理引用地址

    在官方文档中称为Binding,协议-地址对的绑定.在Proxy模式中,一般地有三个参与者,Proxy,Subject以及RealSubject.Subject定义了Proxy(代理)和RealSub ...

  8. SpringSecurity动态加载用户角色权限实现登录及鉴权

    很多人觉得Spring Security实现登录验证很难,我最开始学习的时候也这样觉得.因为我好久都没看懂我该怎么样将自己写的用于接收用户名密码的Controller与Spring Security结 ...

  9. Pod镜像拉取策略imagePullPolicy

    默认值是IfNotPresent Always 总是拉取: 首先获取仓库镜像信息, 如果仓库中的镜像与本地不同,那么仓库中的镜像会被拉取并覆盖本地. 如果仓库中的镜像与本地一致,那么不会拉取镜像. 如 ...

  10. PHP抓取远程图片教程(包含不带后缀图片)

    之前做微信登录开发时候,发现微信头像图片没有后缀名,传统的图片抓取方式不奏效,需要特殊的抓取处理.所以,后来将各种情况结合起来,封装成一个类,分享出来. 创建项目 作为演示,我们在www根目录创建项目 ...