Java语法专题1: 类的构造顺序
- 合集目录
- Java语法专题1: 类的构造顺序
问题
下面的第二个问题来源于Oracle的笔试题, 非常经典的一个问题, 我从07年开始用了十几年. 看似简单, 做对的比例不到2/10.
- 描述一下多级继承中类的构造顺序
- 给定两段代码, 分别是父类和子类, 写出(或选择)正确的输出
代码如下
public class Base {
public Base() {
method(100);
}
public void method(int i) {
System.out.println("Base::method " + i);
}
}
public class Sub extends Base {
public Sub() {
super.method(70);
}
public void method(int j) {
System.out.println("Sub::method " + j);
}
public void method(String j) {
System.out.println("Sub::passed " + j);
}
public static void main(String[] args) {
Base b1 = new Base();
Base b2 = new Sub();
}
}
分析
这是属于Java中常见的基础概念问题, 正确回答这些问题, 需要对类的这些知识有清晰的了解:
- Java类实例的初始化顺序
- Java类方法的重写, 以及和重载的区别
以下通过具体场景说明
场景一
先看下面代码, 执行Sub.java时的屏幕输出是什么?
Base.java
public class Base {
public Base() {
method(100);
}
public void method(int i) {
System.out.println("Base::method " + i);
}
}
Sub.java
public class Sub extends Base {
public void method(int j) {
System.out.println("Sub::method " + j);
}
public static void main(String args[]) {
Base b2 = new Sub();
}
}
这里有两个很重要的概念:
- 类初始化时隐藏的构造顺序: 先调用父类的默认构造函数(即不带参数的构造函数), 然后再执行当前构造函数, 因为本例中Sub.java的构造函数为空(未定义), 因此实际执行的是父类的默认构造函数
- 父类的函数, 会被子类定义的同参数方法覆盖, 这叫方法重写. 本例中初始化的类是Sub, Sub中的method(int i), 已经重新定义, 因此父类的默认构造函数中调用的是Sub的method(int i).
输出
Sub::method 100
场景二
保持父类Base.java不变, 在Sub.java中增加子类的构造函数, 执行Sub.java时的屏幕输出是什么?
public class Sub extends Base {
public Sub() {
super.method(70);
}
public void method(int j) {
System.out.println("Sub::method " + j);
}
public static void main(String args[]) {
Base b2 = new Sub();
}
}
第一行的输出可以参照场景一的说明, 初始化Sub时, 会隐藏调用父类的默认构造函数, 第二行则是子类构造函数中, 在super.method(70);中指定使用父类的method(int i)方法产生的结果.
这个输出验证了前面说的类初始化时的隐藏初始化顺序: 会先调用父类的默认构造函数(即不带参数的构造函数), 然后再执行当前构造函数里的逻辑. 这是最容易出错地方, 漏了第一行, 忘记了即使子类定义了构造函数, 父类构造函数一样会执行.
输出是
Sub::method 100
Base::method 70
场景三
再验证一下类初始化时的隐藏初始化顺序, 如果父类和子类都增加了带变量的构造函数, 执行Sub.java时的屏幕输出是什么?
Base.java
public class Base {
public Base() {
method(100);
}
public Base(int i) {
method(i);
}
public void method(int i) {
System.out.println("Base::method " + i);
}
}
Sub.java
public class Sub extends Base {
public Sub(int i) {
super.method(70);
}
public void method(int j) {
System.out.println("Sub::method " + j);
}
public static void main(String args[]) {
//Base b1 = new Base();
Base b2 = new Sub(100);
}
}
依然先调用了父类的默认构造函数(不带参数), 再调用子类的构造函数, 输出是
Sub::method 100
Base::method 70
场景四
父类不带默认构造函数
Base.java
public class Base {
public Base(int i) {
method(i);
}
public void method(int i) {
System.out.println("Base::method " + i);
}
}
此时Sub.java如果还使用前一个场景的代码, 就会无法通过编译, 因为找不到父类的默认构造函数了, 此时隐藏的初始化顺序就失效了, 需要指定使用哪个父类构造函数
Sub.java
public class Sub extends Base {
public Sub(int i) {
super(100); // <------- 需要显式指定构造函数
super.method(70);
}
public void method(int j) {
System.out.println("Sub::method " + j);
}
public static void main(String args[]) {
//Base b1 = new Base();
Base b2 = new Sub(100);
}
}
执行Sub.java时的屏幕输出依然是
Sub::method 100
Base::method 70
总结
问题: 描述一下多级继承中类的构造顺序
解答:
- 类初始化时的隐藏逻辑: 先调用父类的默认构造函数, 再调用子类的默认构造函数
- 当父类定义了带参数的构造函数, 又不定义默认构造函数时, 上一条就失效了, 子类必须显式指定父类的构造函数
Java语法专题1: 类的构造顺序的更多相关文章
- Java语法专题2: 类变量的初始化顺序
合集目录 Java语法专题2: 类变量的初始化顺序 问题 这也是Java面试中出镜率很高的基础概念问题 描述一下多级继承中字段初始化顺序 描述一下多级继承中类变量初始化顺序 写出运行以下代码时的控制台 ...
- c++ 类的构造顺序
在单继承的情况下,父类构造先于子类,子类析构先于父类,例: class A { public: A() { cout << "A" << endl; } ~ ...
- 从Java虚拟机角度分析类的实例化顺序
1.首先展示一下实例代码(Son.java & Father.java) public class Father { public static int a=10;//父类的静态变量 stat ...
- c# 衍生类和基类的构造顺序
public class MyDeriveClass :MyBaseClass { public MyDeriveClass() :base() { } int derive_int = 1; } p ...
- java类的初始化顺序
在java中,当我们new一个对象时,对象中的成员,初始化块以及构造方法的加载是有一定的顺序的,看下面一副图: 一.单类(无基类)下的初始化顺序: public class Parent { stat ...
- java中带继承类的加载顺序详解及实战
一.背景: 在面试中,在java基础方面,类的加载顺序经常被问及,很多时候我们是搞不清楚到底类的加载顺序是怎么样的,那么今天我们就来看看带有继承的类的加载顺序到底是怎么一回事?在此记下也方便以后复习巩 ...
- C++类继承中,基类/当前对象属性/当前对象的构造顺序
[1]中提到,规范的派生类构造函数三个要点: 首先创建基类对象 应通过成员初始化列表,创建基类对象 应该初始化本派生类新增的成员变量 那在构造派生类实例的过程中,其基类(以及多继承的时候多个基类)/当 ...
- java学习(一)静态代码块 构造代码块 构造方法的执行顺序及注意问题
今天我总结了一下java中静态代码块 构造代码块 构造方法的执行顺序及其注意问题 首先要知道静态代码块是随着类的加载而加载,而构造代码块和构造方法都是随着对象的创建而加载 当时做了这么一个小案例(想必 ...
- 比较C++、Java、Delphi声明类对象时候的相关语法
同学们在学习的时候经常会遇到一些问题,C++.Java.Delphi他们到底有什么不一样的呢?今天我们来比较C++.Java.Delphi声明类对象时候的相关语法.希望对大家有帮助! C++中创建对象 ...
随机推荐
- 【LeetCode】950. Reveal Cards In Increasing Order 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 模拟 日期 题目地址:https://leetcod ...
- 圆桌问题(hdu4841)
圆桌问题 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) Total Submi ...
- 如何让 Spring Security 「少管闲事」
记两种让 Spring Security「少管闲事」的方法. 遇到问题 一个应用对外提供 Rest 接口,接口的访问认证通过 Spring Security OAuth2 控制,token 形式为 J ...
- CSS实现水平垂直居中的方式有哪些?
CSS实现水平垂直居中的方式有哪些? 基本结构样式: .box { width: 400px; height: 400px; background-color: red; } .inner { wid ...
- CAS学习笔记一:CAS 授权服务器简易搭建
什么是CAS CAS是Central Authentication Service的缩写,中央认证服务,一种独立开放指令协议.CAS 是 耶鲁大学(Yale University)发起的一个开源项目, ...
- Codeforces 567C:Geometric Progression(DP)
time limit per test : 1 second memory limit per test : 256 megabytes input : standard input output : ...
- mod4最优路径问题(转载)
原文链接:https://blog.csdn.net/ACdreamers/article/details/18501855 mod4最优路径问题 如下图: 从1到4找出一条路径,要求路径的总长度mo ...
- 第二十三个知识点:写一个实现蒙哥马利算法的C程序
第二十三个知识点:写一个实现蒙哥马利算法的C程序 这次博客我将通过对蒙哥马利算法的一个实际的实现,来补充我们上周蒙哥马利算法的理论方面.这个用C语言实现的蒙哥马利算法,是为一个位数为64的计算机编写的 ...
- C语言string操作
创建方式 字符数组:空间已定 字符指针:未分配空间 初始化 字符数组: 创建与赋值必须在同一行 指定大小:未填满部分用'\0'填充 用字符串初始化:末尾自动添加'\0' 不初始化赋值则乱值 字符指针: ...
- ActiveMQ基础教程(三):C#连接使用ActiveMQ消息队列
接上一篇:ActiveMQ基础教程(二):安装与配置(单机与集群) 安装部署好集群环境:192.168.209.133:61616,192.168.209.134:61616,192.168.209. ...