一道有意思的“初始化”面试题

简介: 今天向大家分享一道Java面试题目,这道题是我自己设计的题目。

概述


今天向大家分享一道Java面试题目,这道题是我自己设计的题目。题目原型来自于《Thinking in Java》中的初始化与清理一章,本来是一道简单的考察初始化题目,我在上面添加了其他与初始化相关的东西,最后变成一道比较综合的考察初始化题目。在很多笔试题中都会考察初始化方面的知识,如果你能把下面这道题目理解了,应付初始化方面的笔试题相信是易如反掌

这道题目涉及到的全是很基础的知识点,但是想要做对并不容易,如果你能第一次就做对,说明你在初始化方面的知识已经很扎实。话不多说,直接看下面的题目,注:文末有答案,建议大家先尝试自己做一下,看看自己是否还记得这些基础知识。

 

题目


请写出下面代码的输出,注意main方法在最后面。


class Bowl {
    Bowl(int marker) {
        System.out.println("Bowl(" + marker + ")");
    }
}
class Tableware {
    static Bowl bowl7 = new Bowl(7);
    static {
        System.out.println("Tableware静态代码块");
    }
    Tableware() {
        System.out.println("Tableware构造方法");
    }
    Bowl bowl6 = new Bowl(6);
}
class Table extends Tableware {
    {
        System.out.println("Table非静态代码块_1");
    }
    Bowl bowl5 = new Bowl(5);    // 9
    {
        System.out.println("Table非静态代码块_2");
    }
    static Bowl bowl1 = new Bowl(1);
    static {
        System.out.println("Table静态代码块");
    }
    Table() {
        System.out.println("Table构造方法");
    }
    static Bowl bowl2 = new Bowl(2);
}
class Cupboard extends Tableware {
    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);
    Cupboard() {
        System.out.println("Cupboard构造方法");
    }
    void otherMethod(int marker) {
        System.out.println("otherMethod(" + marker + ")");
    }
    static Bowl bowl5 = new Bowl(5);
}
public class StaticInitialization {
    public static void main(String args[]) {
        System.out.println("main()");
        cupboard.otherMethod(1);
    }
    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();
}

涉及的知识点:


1.    在类的内部,变量定义的先后顺序决定了初始化顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造方法)被调用之前得到初始化。

2.    无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始化值就是null

3.    静态初始化只有在必要时刻才进行,例如:类里面的静态变量,只有当类被调用时才会初始化(执行),并且静态变量不会再次被初始化(执行),即静态变量只会初始化(执行)一次。 

4.    初始化的顺序是先静态对象,然后是非静态对象。

5.    当有父类时,完整的初始化顺序为:父类静态变量(静态代码块)->子类静态变量(静态代码块)->父类非静态变量(非静态代码块)->父类构造器 ->子类非静态变量(非静态代码块)->子类构造器

6.    即使没有显示的使用static关键字,构造器实际上也是静态方法。

 

下面是上面第5点提到的一些关键字的例子:

静态变量(类变量):static修饰的变量。

static Bowl bowl7 = new Bowl(7);


静态代码块:static修饰的代码块,可以放多个语句。看起来像个方法,实际上只是一段跟在static关键字后面的代码。

static {
    System.out.println("静态代码块");
}

 

非静态变量(实例变量):普通变量,没有static修饰。

Bowl bowl6 = new Bowl(6);

 

非静态代码块:普通代码块,没有static修饰,可以放多个语句。

{
    System.out.println("非静态代码块");
}

 

构造方法(构造器):跟类同名的方法,可以有多个,如果没有写构造方法,则会自动创建一个为空的构造器。

Tableware() {
    System.out.println("构造方法");
}

 

看完这几个知识点,你能写出正确答案了吗,请自己动手试一试。

 

题目详解:


1.    首先我们找到“public class”,此时我们看到熟悉的main()方法,但是在main()方法之外有两行static的变量定义,根据上面的知识点1可知,变量的初始化会在任何方法之前,因此,先执行第49行代码的Table初始化。注意:如果此处Table的定义不是“static”修饰,则不会执行,因为在执行main()方法时,可以理解为执行了代码“StaticInitialization.main(null)”,要执行main()方法必须加载StaticInitialization类,所以静态域(tablecupboard)会得到初始化,而非静态域只有创建类的实例时才会得到初始化,例如执行了代码“StaticInitialization s = new StaticInitialization()”


2.    此时,我们来看Table类,发现Table有父类Tableware,根据上面的知识点5,可知会先初始化父类静态变量(静态代码块),即第79行代码;接着是子类静态变量(静态代码块),即第242631行代码;接着执行父类非静态变量(非静态代码块),即第14行代码;接着执行父类构造器,即第12行代码;接着执行子类非静态变量(非静态代码块),即第182022行代码;最后执行子类构造器,即第29行代码;至此,第49行代码,table的初始化结束。


3.    接着,执行第50行代码Cupboard的初始化,过程跟Table类似。我们发现Cupboard也有父类,并且跟之前的Table一样是Tableware,此时我们要注意上面的知识点2/3静态变量只会初始化(执行)一次,因此Tableware中的静态变量在此次Cupboard的初始化中不会再初始化。根据上面的知识点5,首先执行父类静态变量(静态代码块),已经执行过,跳过;接着执行子类静态变量(静态代码块),即第3542行代码;接着执行父类非静态变量(非静态代码块),即第14行代码;接着执行父类构造器,即第12行代码;接着执行子类非静态变量(非静态代码块),即第34行代码;最后执行子类构造器,即第37行代码;至此,第50行代码,cupboard的初始化结束。


4.    两个静态变量初始化完成后,接着执行main()方法,首先执行第46行代码,输出“main()”,接着执行47行代码,即第40行代码。至此,整个过程全部执行完毕。


答案:


下面是标注了执行顺序的代码。

class Bowl {
    Bowl(int marker) {
        System.out.println("Bowl(" + marker + ")");
    }
}
class Tableware {
    static Bowl bowl7 = new Bowl(7);    // 1
    static {
        System.out.println("Tableware静态代码块");   // 2
    }
    Tableware() {
        System.out.println("Tableware构造方法");    // 7、15
    }
    Bowl bowl6 = new Bowl(6);   // 6、14
}
class Table extends Tableware {
    {
        System.out.println("Table非静态代码块_1");    // 8
    }
    Bowl bowl5 = new Bowl(5);   // 9
    {
        System.out.println("Table非静态代码块_2");    // 10
    }
    static Bowl bowl1 = new Bowl(1);    // 3
    static {
        System.out.println("Table静态代码块");   // 4
    }
    Table() {
        System.out.println("Table构造方法");    // 11
    }
    static Bowl bowl2 = new Bowl(2);    // 5
}
class Cupboard extends Tableware {
    Bowl bowl3 = new Bowl(3);   // 16
    static Bowl bowl4 = new Bowl(4);    // 12
    Cupboard() {
        System.out.println("Cupboard构造方法"); // 17
    }
    void otherMethod(int marker) {
        System.out.println("otherMethod(" + marker + ")");  // 19
    }
    static Bowl bowl5 = new Bowl(5);    // 13
}
public class StaticInitialization {
    public static void main(String args[]) {    // 第三执行
        System.out.println("main()");   // 18
        cupboard.otherMethod(1);    
    }
    static Table table = new Table();   // 第一执行
    static Cupboard cupboard = new Cupboard();  // 第二执行
}

 

最后输出:


Bowl(7)
Tableware静态代码块
Bowl(1)
Table静态代码块
Bowl(2)
Bowl(6)
Tableware构造方法
Table非静态代码块_1
Bowl(5)
Table非静态代码块_2
Table构造方法
Bowl(4)
Bowl(5)
Bowl(6)
Tableware构造方法
Bowl(3)
Cupboard构造方法
main()
otherMethod(1)

最后


我将我最近的原创的文章进行了汇总:原创汇总,其中有不少面试高频题目解析,很多都是我自己在面试大厂时遇到的,我在对每个题目解析时都会按较高的标准进行深入探讨,可能只看一遍并不能完全明白,但是相信反复阅读,定能有所收获。

 

原创不易,如果你觉得本文写的还不错,对你有帮助,请通过【点赞】让我知道,支持我写出更好的文章。

 

推荐阅读


921天,咸鱼到阿里的修仙之路

两年Java开发工作经验面试总结

4 Java 经验,阿里网易拼多多面试总结、心得体会

5 Java 经验,字节、美团、快手核心部门面试总结(真题解析)

复习2个月拿下美团offer,我都做了些啥

如何写一份让 HR 眼前一亮的简历(附模板)

面试阿里,HashMap 这一篇就够了

面试必问的 MySQL,你懂了吗?

面试必问的线程池,你懂了吗?

跳槽,如何选择一家公司

如何准备好一场大厂面试

面试必问的分布式锁,你懂了吗?

面试必问的 Redis:数据结构和基础概念

 

相关文章
|
2月前
|
SQL 数据库
SQL面试50题------(初始化工作、建立表格)
这篇文章提供了SQL面试中可能会遇到的50道题目的建表和初始化数据的SQL脚本,包括学生、教师、课程和成绩表的创建及数据插入示例。
SQL面试50题------(初始化工作、建立表格)
|
3月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
**Kotlin中的`by lazy`和`lateinit`都是延迟初始化技术。`by lazy`用于只读属性,线程安全,首次访问时初始化;`lateinit`用于可变属性,需手动初始化,非线程安全。`by lazy`支持线程安全模式选择,而`lateinit`适用于构造函数后初始化。选择依赖于属性特性和使用场景。**
127 5
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
|
3月前
|
前端开发 Java 编译器
Java面试题:描述Java类的加载过程,包括加载、链接、初始化等阶段。
Java面试题:描述Java类的加载过程,包括加载、链接、初始化等阶段。
28 0
|
5月前
|
开发工具 Python
2024年最新【Python】Python的二维数组初始化,2024年最新15个经典面试问题及答案例子
2024年最新【Python】Python的二维数组初始化,2024年最新15个经典面试问题及答案例子
2024年最新【Python】Python的二维数组初始化,2024年最新15个经典面试问题及答案例子
|
12月前
|
安全 Java 编译器
【面试题精讲】JVM-类的生命周期-初始化阶段
【面试题精讲】JVM-类的生命周期-初始化阶段
|
Java 编译器
04-面试:类的初始化做了什么?初始化的时机是?
类的初始化是指在首次使用类时,JVM对类进行的初始化操作。在类初始化阶段,JVM会执行一系列的步骤。
78 0
04-面试:类的初始化做了什么?初始化的时机是?
|
Java
第一季:3类和实例初始化【Java面试题】
第一季:3类和实例初始化【Java面试题】
64 0
|
Java Spring
面试了才知道初始化Bean不仅只有new那么简单
面试了才知道初始化Bean不仅只有new那么简单
55 0
|
Java
JavaSE面试题05:类初始化和实例初始化
JavaSE面试题05:类初始化和实例初始化
194 0
JavaSE面试题——类初始化 & 实例初始化
JavaSE面试题——类初始化 & 实例初始化
JavaSE面试题——类初始化 & 实例初始化