概述
今天向大家分享一道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类,所以静态域(table和cupboard)会得到初始化,而非静态域只有创建类的实例时才会得到初始化,例如执行了代码“StaticInitialization s = new StaticInitialization()”。
2. 此时,我们来看Table类,发现Table有父类Tableware,根据上面的知识点5,可知会先初始化“父类静态变量(静态代码块)”,即第7、9行代码;接着是“子类静态变量(静态代码块)”,即第24、26、31行代码;接着执行“父类非静态变量(非静态代码块)”,即第14行代码;接着执行“父类构造器”,即第12行代码;接着执行“子类非静态变量(非静态代码块)”,即第18、20、22行代码;最后执行“子类构造器 ”,即第29行代码;至此,第49行代码,table的初始化结束。
3. 接着,执行第50行代码Cupboard的初始化,过程跟Table类似。我们发现Cupboard也有父类,并且跟之前的Table一样是Tableware,此时我们要注意上面的知识点2/3,“静态变量只会初始化(执行)一次”,因此Tableware中的静态变量在此次Cupboard的初始化中不会再初始化。根据上面的知识点5,首先执行“父类静态变量(静态代码块)”,已经执行过,跳过;接着执行“子类静态变量(静态代码块)”,即第35、42行代码;接着执行“父类非静态变量(非静态代码块)”,即第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)
最后
我将我最近的原创的文章进行了汇总:原创汇总,其中有不少面试高频题目解析,很多都是我自己在面试大厂时遇到的,我在对每个题目解析时都会按较高的标准进行深入探讨,可能只看一遍并不能完全明白,但是相信反复阅读,定能有所收获。
原创不易,如果你觉得本文写的还不错,对你有帮助,请通过【点赞】让我知道,支持我写出更好的文章。
推荐阅读
5 年 Java 经验,字节、美团、快手核心部门面试总结(真题解析)