深入分析Java对象的建构顺序

标签: 分析 java 对象 | 发表时间:2013-01-26 18:58 | 作者:youngto
出处:http://www.cnblogs.com/

对于下面的代码,许多有经验的程序员都没能给出正确的答案。如果你能只看代码给出的答案和文章末尾出给出大答案一致,那么你已经掌握了Java对象的建构顺序,中间的分析可以不用看了。

1 /**
2 * 父类Foo,实现控制台输出
3 *
4 * @author youngto
5 * @since 2013-01-25
6 */
7 class Foo {
8
9 private int index = 100;
10
11 //静态代码块
12 static {
13 System.out.println("Foo static");
14 }
15
16 //初始化代码块
17 {
18 System.out.println("Foo initialization");
19 }
20
21 public Foo() {
22 System.out.println("Foo constructor");
23 System.out.println(printIndex());
24 }
25
26 protected int printIndex() {
27 return index;
28 }
29
30 }
31
32 /**
33 * 子类Bar,实现控制台输出
34 *
35 * @author youngto
36 * @since 2013-01-25
37 */
38 public class Bar extends Foo{
39
40 private int index = 100;
41 static Bar bar = new Bar();
42
43 //静态代码块
44 static{
45 System.out.println("Bar static");
46 }
47
48 //初始化代码块
49 {
50 System.out.println("Bar initialization");
51 }
52
53 public Bar() {
54 System.out.println("Bar constructor");
55 System.out.println(printIndex());
56 }
57
58 @Override
59 protected int printIndex() {
60 System.out.println(bar);
61 return index;
62 }
63
64 public static void main(String[] args) {
65 Foo foo = new Bar();
66 System.out.println(foo.printIndex());
67 foo = new Bar();
68 }
69
70 }

 

 在对象建构过程中。为确保其正确性,以下事件一定会以固定顺序发生:

a、从heap之中分配内存,用以存放全部的 instance 变量以及这个对象连同其 superclasses的实现专属数据(implementation-specific data)。所谓「实现专属数据」包括指向“class and method data的指针。

b、 对象的Instance变量被初始化为其相应的缺省值。

c、调用most derived class(最深层派生类)的构造函数(constructor)(注:事实上,构造函数被.class文件中的一个initialization method(初始化函数)替换了。Initialization method是名为<init>的特殊函数,由Java编译器安放在.class文件里。其中包含[构造函数代码]、[instance变量之初始化代码],以及[调用superclass Initialization method]之代码。)。构造函致做的第一件事就是调用superclass的构造函数。这个程序一直反复持续到 java.lang.object构适函数被调用为止。一定要记住,java.lang.object是一切java对象的base class。

d、所有对象的静态代码块或静态字段先获得执行,优先级从父类开始。

e、在构造函数本体执行之前,所有 instance 变量的初值设定式(initializers)和初始化区(initialization blocks)先获得执行,然后才执行构造函数本体。于是base class的构造函数最先执行,most derived class的构造函数最后执行。这使得任何class的构造函数都能放心大胆地使用其任何superclass 的instance 变量。

代码分析:

1、根据a:从heap分配内存,用来存放Bar的instance变量(index和bar)、Foo的instance变量(index),和一份「实现专属数据」。

2、根据b:instance变量被初始化为其相应缺省值,Bar的index被赋值为0,bar被赋值为null,Foo的index被赋值为0。

3、根据c:在代码65行准备创建Bar的一个对象,调用Bar的构造函数立即调用其superclass Foo的构造函数,Foo构造函数立即调用其superclass java.lang.Object的构造函数。

4、根据d:java.lang.Object构造函数返回后,在Foo对象中执行静态代码块输出Foo static,Foo静态代码块执行完,执行Bar静态代码此时代码执行到41行,准备创建Bar的第二个对象,针对这个对象,又从步骤1开始重复全部过程。

5、将要创建第二个Bar对象代码再次执行到Foo对象,由于静态对象只初始化一次,所以不会再次执行Foo的静态代码块,直接先执行初始化代码块输出Foo initialization,再执行Foo构造函数本体,根据e:Foo的index被赋值为100(这是来迷惑你的),输出Foo constructor;在Foo的构造函数中调用了printIndex函数,由于printIndex函数已经被子类Bar重写所以此时调用的是Bar中的printIndex函数,根据e:此时代码还未执行到Bar的构造函数本体,所以此时printIndex函数输出缺省值bar=null,返回Bar的index缺省值0,因此这一步输出null,0,Foo构造函数完成,返回。

6、继续构建第二个Bar对象,执行Bar的初始化代码块,输出Bar initialization,再执行构造函数,输出Bar constructor。此时Bar的index赋值100,Bar的bar对象还正在构建中所以为null,输出null,100,Bar构造函数完成。

7、Object reference bar指向heap之中最后创建完成的Bar对象,此时在第一个Bar的对象创建过程中而创建的第二Bar对象创建完成。

8、继续第一个Bar对象的创建、执行Bar的静态代码块,输出Bar static。

9、从4-8步静态代码执行完成,继续执行Foo的初始化代码块输出Foo initialization,再执行Foo的构造函数本体输出Foo constructor。在Foo的构造函数中调用了printIndex函数,由于printIndex函数已经被子类Bar重写所以此时调用的是Bar中的printIndex函数,由于在构建第二个Bar对象时已经为静态字段bar赋值,所以静态变量bar此时有值,但实例变量index还是为缺省值0,输出Bar@4633c1aa和0,Foo构造函数完成,返回。

10、执行Bar的初始化代码块输出:Bar initialization,再执行Bar的构造函数输出Bar constructor,Bar@4633c1aa和100,Bar构造函数完成。

11、Object reference bar指向heap之中最后创建完成的Bar对象,此时第一个Bar的对象创建完成。

12、66行调用bar对象函数输出Bar@4633c1aa和100.

13、67行此时给bar引用从新赋值,又从步骤一重复全部过程,由于静态只实例化一次所以输出为:Foo initialization、Foo constructor、Bar@4633c1aa、0、Bar initialization、Bar constructor、Bar@4633c1aa、100

输出结果为:

//代码在65行建构第一个Bar对象
Foo static
//代码执行到41行建构第二个Bar对象
Foo initialization
Foo constructor
null
0
Bar initialization
Bar constructor
null
100
//代码执行到56行第二个Bar对象建构完,继续建构第一个Bar对象
Bar static
Foo initialization
Foo constructor
Bar@4633c1aa
0
Bar initialization
Bar constructor
Bar@4633c1aa
100
//66行调用第一个Bar对象的函数输出结果
Bar@4633c1aa
100
//67行此时第一个Bar对象执行完,给第一个Bar引用从新建构对象
Foo initialization
Foo constructor
Bar@4633c1aa
0
Bar initialization
Bar constructor
Bar@4633c1aa
100

Java Developers 67844123

本文链接

相关 [分析 java 对象] 推荐:

深入分析Java对象的建构顺序

- - 博客园_首页
对于下面的代码,许多有经验的程序员都没能给出正确的答案. 如果你能只看代码给出的答案和文章末尾出给出大答案一致,那么你已经掌握了Java对象的建构顺序,中间的分析可以不用看了. 2 * 父类Foo,实现控制台输出. 33 * 子类Bar,实现控制台输出. 为确保其正确性,以下事件一定会以固定顺序发生:.

Java的对象驻留

- - Java译站
Java会将源代码中的字符串常量存储到常量池中. 这不只是说它俩的值是一样的,而是说就是同一个字符串对象. 用Java的话来说就是a==b的结果是true. 然而这个只对字符串以及小的整型或者长整型有效. 其它的对象是不会被驻留的,也就是说如果你创建了两个对象而他们的值是相等的,但他们并不是同一个对象.

[Java] Java 多线程案例分析

- - V2EX
现要从 hbase中导出 2016 年整年的,大约 10w只股票行情数据,数据总量约 100t. 汇总到 hdfs中供需求方使用. 已知数据量规模大概是 100t,那么单台机器处理肯定不是不行的,先不说大多数磁盘都没这么大,即便磁盘有这么大,单台机器处理对于内存和 cpu 要求也很高,所以我们将问题一般化,使用数量有限的低配机器.

理解Java对象序列化

- - 博客 - 伯乐在线
来源: jiangshapub 的博客( @jiangshapub). 关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结. 此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制. 在撰写本文时,既参考了 Thinking in Java, Effective Java,JavaWorld,developerWorks中的相关文章和其它网络资料,也加入了自己的实践经验与理解,文、码并茂,希望对大家有所帮助.

java bean对象之间复制属性

- - Java - 编程语言 - ITeye博客
在现在的企业级Java应用程序中, Java Bean被广泛的应用. 一堆的相关的划分也应运而生, 如DTO, DAO, BO, POJO, VO等. 这里不去管这些概念的细节, 如果你感兴趣,可以google之, 比如 这篇文章. 这里要讨论的问题是如何在不同的Java Bean对象之间复制它们的属性.

java 类和对象的初始化

- - Web前端 - ITeye博客
  在Java中,类装载器把java类装载到虚拟机中,经过装载,链接和初始化三个步骤来完成. 其中链接中包括 校验、准备和解析. 下面对这些概念进行解析:. 装载:查找和导入类或接口的二进制数据,常用的是根据类的路径加载,还有根据网络的地址加载. 链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的;.

java线程池分析

- - BlogJava-首页技术区
    在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提于交和执行是同时进行的,如果你想对任务的执行进行调度或是控制 同时执行的线程数量就需要额外编写代码来完成. 5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接 池的线程池来执行任务.

JAVA集合分析概述

- - ITeye博客
       前段时间在QQ空间写了篇关于JAVA集合分析的日志,现搬到这里与大家分享. 其中有链接可查看各种集合的具体介绍.  最基本的数据结构有数组、链表2种.         1)数组的长度是固定的,容量是有限的(除非扩容). 可以用索引访问,因此访问速度比较快.         2)链表中每个节点指向下一个节点,容量是无限的(不作限制且不考虑硬件).

Java ClassLoader原理分析

- - Java - 编程语言 - ITeye博客
一、JDK默认提供的三个ClassLoader. JDK 默认提供了如下几种ClassLoader. Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载 %JAVA_HOME%/jre/lib, -Xbootclasspath参数指定的路径以及 %JAVA_HOME%/jre/classes中的类.

[Json]json-lib简单处理java对象变为json对象

- - CSDN博客编程语言推荐文章
由于js对json的原生支持,所以现在很多项目的数据传输都喜欢用json. 怎么样把数据从java对象转化为json对象,有怎么把前台的json对象转化成java对象去处理. json-lib是sourceforge的一个开源项目,常用来解决java json数据转换的问题. 下载地址是 http://sourceforge.net/projects/json-lib/.