今晚是阿里巴巴 2013 校园招聘的杭州站笔试。下午匆忙看了两张历年试卷,去现场打了瓶酱油。

题目总体考察点偏基础,倒数第二题(Java 附加题)比较有趣,考察了 Java 初始化机制的细节,在此摘录出来。

题目

求如下 java 代码的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class T  implements Cloneable{
public static int k = 0;
public static T t1 = new T("t1");
public static T t2 = new T("t2");
public static int i = print("i");
public static int n = 99; public int j = print("j");
{
print("构造快");
} static {
print("静态块");
} public T(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n; ++ i;
} public static int print(String str){
System.out.println((++k) +":" + str + " i=" + i + " n=" + n);
++n;
return ++ i;
} public static void main(String[] args){
T t = new T("init");
}
}

分析

代码主要考察的是类、变量初始化的顺序。

一般的,我们很清楚类需要在被实例化之前初始化,而对象的初始化则是运行构造方法中的代码。

本题的代码显然没有这么简单了。本题中涉及到了static {…}{…}这种形式的代码块,以及在类的静态变量中初始化该类的对象这种交错的逻辑,容易让人焦躁(类似于密集恐惧症吧=()。实际上,按照类的装载、链接和初始化逻辑,以及对象初始化的顺序来思考,不难得到答案。

代码组成

  • 成员变量
    2~6 行的变量是 static 的,为类 T 的静态成员变量,需要在类加载的过程中被执行初始化;第 8 行的int j则为实例成员变量,只再类被实例化的过程中初始化。

  • 代码段
    9~11 行为实例化的代码段,在类被实例化的过程中执行;13~15 行为静态的代码段,在类被加载、初始化的过程中执行。

  • 方法
    方法public static int print(String str) 为静态方法,其实现中牵涉到 k,i,n 三个静态成员变量,实际上,这个方法是专门用来标记执行顺序的方法;T 的构造方法是个实例化方法,在 T 被实例化时调用。

  • main 方法
    main 方法中实例化了一个 T 的实例。

执行顺序分析

在一个对象被使用之前,需要经历的过程有:类的装载 -> 链接(验证 -> 准备 -> 解析) -> 初始化 -> 对象实例化。(详情参见《Java 类的装载、链接和初始化》),这里需要注意的点主要有:

  • 在类链接之后,类初始化之前,实际上类已经可以被实例化了。

    就如此题代码中所述,在众多静态成员变量被初始化完成之前,已经有两个实例的初始化了。实际上,此时对类的实例化,除了无法正常使用类的静态承运变量以外(还没有保证完全被初始化),JVM 中已经加载了类的内存结构布局,只是没有执行初始化的过程。比如第 3 行public static T t1 = new T("t1");,在链接过程中,JVM 中已经存在了一个 t1,它的值为 null,还没有执行new T("t1")。又比如第 5 行的public static int i = print("i");,在没有执行初始化时,i 的值为 0,同理 n 在初始化前值也为 0.

  • 先执行成员变量自身初始化,后执行static {…}{…}代码块中的内容。

    如此策略的意义在于让代码块能处理成员变量相关的逻辑。如果不使用这种策略,而是相反先执行代码块,那么在执行代码块的过程中,成员变量并没有意义,代码块的执行也是多余。

  • 类实例化的过程中,先执行隐式的构造代码,再执行构造方法中的代码
    这里隐式的构造代码包括了{}代码块中的代码,以及实例成员变量声明中的初始化代码,以及父类的对应的代码(还好本题中没有考察到父类这一继承关系,否则更复杂;))。为何不是先执行显示的构造方法中的代码,再执行隐式的代码呢?这也很容易解释:构造方法中可能就需要使用到实例成员变量,而这时候,我们是期待实例变量能正常使用的。

有了如上的分析,也就能推到出最终的输出结果了。实际上,这几个原则都不需要死记硬背,完全能通过理解整个 JVM 的执行过程来梳理出思路的。

答案

1
2
3
4
5
6
7
8
9
10
11
1:j   i=0   n=0
2:构造快 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造快 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造快 i=9 n=101
11:init i=10 n=102

参考:

Java类、实例的初始化顺序的更多相关文章

  1. Java类的成员初始化顺序

    Java类的成员初始化顺序 2017-06-01 代码: public class InitializeSequence { public static void main(String[] args ...

  2. java类的成员初始化顺序和初始化块知识

    java类的成员初始化顺序和初始化块知识 转自:http://blog.csdn.net/lgfeng218/article/details/7606735 属性.方法.构造方法和自由块都是类中的成员 ...

  3. Java 类成员的初始化顺序

    Java 类成员的初始化顺序 前言:开发中碰到一个Java文件中有很多的成员变量,包括静态和非静态的,还有很多的初始化方法,很好奇这些成员的初始化顺序,在这里作个研究.   1  无继承情况下的Jav ...

  4. Java类的变量初始化顺序

    大家在去参加面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台 ...

  5. java类中元素初始化顺序

    结论:对于静态变量.静态初始化块.变量.初始化块.构造器,它们的初始化顺序依次是(静态变量.静态初始化块)>(变量.初始化块)>构造器. public class Test4 { @Tes ...

  6. Java 类的成员初始化顺序

    做个简单笔录,就当是重温下基础知识. 1.先看代码: package com.test; public class Test { public static void main(String[] ar ...

  7. java类对象的初始化顺序

    在下面这个例子中,我们分别在父类和子类中测试了静态代码块.普通代码块.静态成员变量.普通成员变量.构造器.静态内部类. 一:代码块及变量测试 class Field{ public static St ...

  8. java类中元素初始化顺序详解

    父类静态变量父类静态块子类静态变量子类静态块父类普通变量父类普通块父类构造方法子类普通变量子类普通块子类构造方法

  9. java中的多构造函数以及类字段的初始化顺序

    1.同一个类可以有多个构造函数,多个构造函数之间通过参数来区分.这是方法重载的一个实例.构造函数之间可以相互调用. 2.类的初始化块:可以在类中使用“{”和“}”将语句包围起来,直接将其作为类的成员. ...

  10. Java类的基本运行顺序

    不看不知道,一看还真吓了一跳!Java类的基本运行顺序你真的知道吗?如果你有疑惑,那就看下面这篇文章吧.   原文出自:[url]http://column.ibeifeng.com/chenchen ...

随机推荐

  1. element ui源码解析 -- button篇

    要看源码就得从最简单的开始,button够简单的了,就从他开始吧. 安装依赖后源码目录在:node_modules/element-ui/packages中,可以看到这里的文件夹命名是不是很熟悉,就是 ...

  2. DBeaver笔记-快捷键篇

    公司使用的是PostgreSQL数据库,可以使用pgAdmin或者DBeaver进行连接该数据库.个人更喜欢用DBeaver,因为其界面更加美观,操作也相对简单.对于习惯了eclipse的开发者来说, ...

  3. iOS改动UIButton setTitle字体颜色和调整字体位置

    调整Title字体位置 [button setTitleEdgeInsets:UIEdgeInsetsMake(10, 0, 0, 0)]; 四个參数分别代表:上边界,左边界.下边界,右边界 改动UI ...

  4. heredoc(实现模板与代码的分离)

    heredoc(实现模板与代码的分离) 一.总结 heredoc实现模板与代码的分离,实现的是在后台编程语言中批量输出html代码,在这个批量输出的html代码中又可以嵌套编程语言变量.所以很方便. ...

  5. gdb查看线程堆栈信息

    查看堆栈:gdb -quiet -batch -ex='thread apply all bt' -p pid查看运行位置:gdb -quiet -batch -ex='thread apply al ...

  6. 【32.70%】【poj 2492】A Bug's Life

    Time Limit: 10000MS Memory Limit: 65536K Total Submissions: 34687 Accepted: 11344 Description Backgr ...

  7. AVR第5课:蜂鸣器

    下面是蜂鸣器的电路图. 代码:蜂鸣器代码. <span style="font-size:18px;">/* *info:buzzer *author:chenlu * ...

  8. Fragment之一:基本原理 分类: H1_ANDROID 2013-11-18 14:15 1642人阅读 评论(0) 收藏

    1.低版本API对Fragment的支持 Fragment必须被加载进Acitivity中,才能呈现.而在低于3.0版本的API中,由于不存在Fragment,因此必须使用support包: (1)对 ...

  9. Error while trying to retrieve text for error ORA-12705

    今天, 按照以前的学习笔记, 配置ProC 但是, 却发生了如题的错误. Google一下, 都是NLS_LANG环境变量设置有问题, 我核一下没有问题. 问题在哪? 原来是ORACLE_HOME环境 ...

  10. Django---MVC设计模式

    把数据存储逻辑.业务逻辑和表现逻辑组合在一起的概念被称为软件架构的 Model-View-Controller (MVC)模式. 在这个模式中, Model 代表数据存层,View 代表的是系统中选择 ...