Java 类的构造器中this()和super()的困惑
关于构造器中super的使用,书本上这样写:
“super是指向父类的引用,如果构造方法没有显示地调用父类的构造方法,那么编译器会自动为它加上一个默认的super()方法调用。如果父类由没有默认的无参构造方法,编译器就会报错,super()语句必须是构造方法的第一个子句。”
首先我要纠正一个我刚刚才发现的印象流错误,我之前一直以为,无论有没有自定义构造器,编译器为自动为每个类生成一个“方法体为空的无参构造方法”。但上面那段话,指出了,一个类是有可能没有默认的无参构造方法的。于是我往回翻了书,书上的原文为:
“类可以不定义构造方法,这时编译器会为类隐含声明一个方法体为空的无参构造方法。但当类有明确声明的构造方法时,编译器就不会自动生成无参构造方法了。”
说明一旦你自定义了构造方法,编译器就不会帮你自动生成方法体为空的无参构造方法。我之前的印象流是错的。
我们再来看书本上关于this的描述:
“this表示一个对象的引用,它指向正在执行方法的对象。特别的,在构造方法中通过this关键字调用其他构造方法时,必须放在第一行,否则编译器会报错。且在构造方法中,只能通过this调用一次其他构造方法。”
综合上面的描述,会有一个疑问,能否在同一个构造器中同时显示地使用this和super?
(这个问题在知乎上有讨论,详情请转义阵地。在这里我直接搬运过来了)
假定这里讨论的构造器都没有显式的super()调用,则:
- 有显式this()调用的构造器就会抑制掉该构造器里隐式的super()调用;
- 没有显式this()调用的构造器则会得到隐式的super()调用。
this()调用会借助别的重载版本的构造器来做部分初始化,而一连串this()最后来到的构造器必然是没有显式this()调用的,这里就会有显式或隐式的super()调用。举个例子:
public class Base {
private int x;
public Base() {
// super(); // 这里开头没有this()调用,编译器会合成隐式的super()调用
this.x = 42;
}
} public class Derived extends Base {
private char c;
private Object o; public Derived() {
this('a'); // 这里开头有this()调用,编译器不会合成隐式的super()调用
} public Derived(char c) {
this(c, null); // 同上,不会合成super()调用
} public Derived(char c, Object o) {
// super(); // 这里开头没有this()调用,编译器会合成隐式的super()调用
this.c = c;
this.o = 0;
}
} 作者:RednaxelaFX
链接:https://www.zhihu.com/question/41810504/answer/117132716
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在这个例子里,如果有 new Derived(),则一连串的构造器调用会是:
Derived() // 最初的new
Derived(char) // 经过this()
Derived(char, Object) // 经过this()
Base() // 经过super()
Object() // 来到Object()
这就是我想说的,一连串this()的最后一个必然不再会在开头有this()调用,而这个开头没有this()调用的构造器就会得到编译器给隐式合成的super()调用。
这里没有直接回答“能不能在同一构造器同时显示调用super()和this()”的问题,但考虑下,如果能的话,最终会有两次super()调用,也就是说我们没有理由,在同一个构造器中同时调用super()和this()。
那么强行在同一个构造器中同时调用super()和this()会发生什么呢?
结果是编译器禁止这样做,无论把哪个放在构造器的第一行,都会出现以下其中一个错误:
Error:(6, 13) java: call to this must be first statement in constructor
Error:(6, 14) java: call to super must be first statement in constructor
那么super()的调用究竟完成了什么工作?难道它创建了一个父类对象吗?
首先,super是指向“父类”的引用,不是像“this”一样指向一个对象。
由于一个子类的实例会包含其所有基类所声明的字段(所有噢,包括私有,这些字段也需要初始化),外加自己声明的字段。而super()是父类封装对自己声明的字段进行初始化的手段。
所以,并没有创建一个父类对象,因为仅仅有那些父类声明的字段构不成一个完整的实例。
Java对象是这样组织的(概念上):
一个Derived实例就是一个Derived实例。它的this()会负责初始化自己所声明的字段的部分,而它通过(递归-)调用super()来初始化基类祖先们所声明的字段的部分。
然后每个类的元数据会包括结构信息,例如说自己的superClass是谁。这里Derived类会知道自己的superClass是Base,而Base知道自己的superClass是Object。
当我们需要执行instanceof、checkcast、arraystore之类的涉及类型检查的操作时,类的元数据里的结构信息就派上用场了。
Java 类的构造器中this()和super()的困惑的更多相关文章
- Java类继承关系中的初始化顺序
Java类初始化的顺序经常让人犯迷糊,现在本文尝试着从JVM的角度,对Java非继承和继承关系中类的初始化顺序进行试验,尝试给出JVM角度的解释. 非继承关系中的初始化顺序 对于非继承关系,主类Ini ...
- Java 类在 Tomcat 中是如何加载的?
作者 :xingoo https://www.cnblogs.com/xing901022/p/4574961.html 说到本篇的Tomcat类加载机制,不得不说翻译学习Tomcat的初衷. 之前实 ...
- java类集开发中一对多和多对多的关系的实现
摘自<java开发实战经典>李兴华.著 一对多的关系 一个学校可以包含多个学生,一个学生属于一个学校,那么这就是一个典型的一对多关系,此时就可以通过类集进行关系的表示. 在定义Studen ...
- 【深入理解JVM】:Java类继承关系中的初始化顺序
尝试着仔细阅读thinking in java 看到一篇很好的文章http://blog.csdn.net/u011080472/article/details/51330114
- Java 类的构造器的调用顺序
规则如下: 对于一个复杂的对象,构建器的调用遵照下面的顺序: (1) 调用父类构建器.这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然后是下一个子类,等等.直到抵达最深一层的子类. (2) ...
- java 中 this 和 super 说明及在构造器中super()和this()相互调用执行顺序
this this 表示当前对象 使用上细分的话,this有 this. 和this()的使用情况 ,下面我们开始细撸 this . 使用场景一: 在成员方法中,this.变量名 指带当前对象的变量, ...
- 【Java基础】面向对象中
面向对象中 这一章主要涉及面向对象的三大特征,包括封装.继承.多态.(抽象). 封装 程序设计追求"高内聚,低耦合": 高内聚 :类的内部数据操作细节自己完成,不允许外部干涉: 低 ...
- 韩顺平Java(持续更新中)
原创上课笔记,转载请注明出处 第一章 面向对象编程(中级部分) PDF为主 1.1 IDEA 删除当前行,ctrl+y 复制当前行,ctrl+d 补全代码,alt+/ 添加或者取消注释,ctrl+/ ...
- java类和对象的基础(笔记)
在Java类的设计中,有时希望一个类在任何时候只能有一个实例.这时可以将该类设计为单例模式(singleton).要将一个类设计为单例模式,需要把类的构造方法的访问修饰符声明为private,然后在类 ...
随机推荐
- Padding Oracle 和 CBC字节翻转攻击学习
以前一直没时间来好好研究下这两种攻击方式,虽然都是很老的点了= =! 0x01:Padding oracle CBC加密模式为分组加密,初始时有初始向量,密钥,以及明文,明文与初始向量异或以后得到中间 ...
- VLC-Qt 入门指南
关于 VLC-Qt VLC-Qt:一个结合了 Qt 应用程序和 libVLC 的免费开源库.它包含了用于媒体播放的核心类,以及用于更快速地进行媒体播放器开发的一些 GUI 类. 官网地址:http ...
- 左键双击关闭pagecontrol中的一个分页即一个tabsheet,功能像遨游浏览器一样
左键双击关闭pagecontrol中的一个分页即一个tabsheet,功能像遨游浏览器一样 procedure TfrmServerSetup.PageControl1MouseDown(Sender ...
- Helm chart仓库官方仓库不能使用解决方法
Helm chart仓库官方仓库不能使用解决方法 k8s中的官方helm chart仓库在国内可能使用不了,但是我们又需要使用,这里推荐几个方法. 使用其他的chart仓库 微软的chart仓库 ht ...
- Host x.x.x.x not found in /root/.ssh/known_hosts
候解决办法是,只要找到电脑里“.ssh” 文件夹,将文件夹里的文件”known_hosts”删除掉或者担心删除了会有风险,改个名字,然后在重新提交的时候,就能正确提交了 将known_hosts删掉或 ...
- flask_sqlalchemy的session线程安全源码解读
flask_sqlalchemy是如何在多线程中对数据库操作不相互影响 数据库操作隔离 结论:使用scoped_session实现数据库操作隔离 flask的api.route()接收一个请求,就会创 ...
- zabbix客户端监控
1.安装zabbix客户端软件: yum install -y zabbix20-agent2.修改配置文件vim /etc/zabbix_agentd.conf修改如下: (1)更改Server,S ...
- vue-template-compiler作用
vue-template-compiler的作用是什么: 看起来 template-compiler是给parse函数使用的, 那么parse函数是干什么的呢 先看一下parse的结果: 结论:使用v ...
- Bloomber 新建基金账户步骤
--Bloomber 新建基金账户步骤0.执行FIRM命令1.新建account group2.新建account3.将account加入到account group4.将account group授 ...
- HBase 批量删除表 disable_all drop_all
这两命令可以匹配正则表达式,对表进行批量操作,也可以对确定名字的单表操作,在表名不存在时,也不会返回exception,只会有提示信息.\ny是为了实现自动确认,因为这两命令需要用户交互确认. 例子, ...