黑马程序员技术交流社区

标题: 【Leo干货店】对象初始化顺序常见错误辨析 [打印本页]

作者: fantacyleo    时间: 2015-2-28 11:01
标题: 【Leo干货店】对象初始化顺序常见错误辨析
本帖最后由 fantacyleo 于 2015-3-14 23:54 编辑

初始化对象时,静态/实例初始化代码块、静态/实例变量定义、构造函数的执行顺序是论坛上常见的讨论话题。我发现大家普遍认为它们之间有固定的执行顺序,通常是:静态初始化块-静态变量-实例初始化块-实例变量-构造函数 我不知道这样的观念来自何处,印象里老毕视频没有这样说过,有帖子提到国产教材云云。不幸的是,这种观点是错误的。如果它们之间有固定的执行顺序,那么如下两段代码的输出结果应该一模一样:
  1. public class Test {
  2. {
  3.                 System.out.println("initialization block");
  4.         }

  5.         String str = printStr();
  6.         
  7.         static String printStr() {
  8.                 System.out.println("printStr");
  9.                 return "str";
  10.         }
  11.         public static void main(String[] args){
  12.                 new Test();
  13.         }
  14. }
复制代码


  1. public class Test {
  2.         String str = printStr();
  3.         {
  4.                 System.out.println("initialization block");
  5.         }

  6.         
  7.         static String printStr() {
  8.                 System.out.println("printStr");
  9.                 return "str";
  10.         }
  11.         public static void main(String[] args){
  12.                 new Test();
  13.         }
  14. }
复制代码

但是,大家只要运行一下就会发现,两段代码的输出顺序恰好相反。这就证伪了“固定顺序说”。

那么,正确的初始化顺序到底是什么样的呢?下面我将引用Java语言最权威(没有之一)的参考文档:The Java Language Specification,作出3点归纳:

1. 静态成员的初始化(包括静态变量和静态初始化块)先于其他内容的初始化。这一点大家应该都没什么问题。
2. 静态变量和静态初始化块的初始化顺序由二者在代码中的出现顺序决定。类似地,实例变量和实例初始化块的初始化顺序由二者在代码中的出现顺序决定。The Java Language Specification的原话是:
Initialization consists of execution of any class variable initializers and static initializers of the class, in textual order.
Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class

我在上面给出的代码例子验证了实例变量和实例初始化块的初始化顺序由二者在代码中的出现顺序决定,静态变量和静态初始化块的顺序

3. 实例初始化块和实例变量定义优先于大多数而非全部构造函数代码。因为构造函数中调用父类构造函数的代码是优先于构造代码块和成员变量定义执行的。比如下面这段代码:
  1. public class Test {

  2.         public static void main(String[] args){
  3.                 new B();
  4.         }
  5. }

  6. class A {
  7.         public A() {
  8.                 System.out.println("A's constructor");
  9.         }
  10.         
  11. }

  12. class B extends A {
  13.         {
  14.                 System.out.println("B's initialization block");
  15.         }
  16.         
  17.         public B() {
  18.                 System.out.println("B's constructor");
  19.         }
  20.         
  21. }
复制代码

输出结果是:
A's constructor
B's initialization block
B's constructor

好,先说这么多,不足和错误之处恳请大家批评指出。祝大家的黑马梦早日实现!
PS
1. 类加载会使得对象初始化顺序变得更复杂,过段时间我将另帖说明。
2. 有意报黑马上海首期班的同学可加Q群:302602799


作者: w239983684    时间: 2015-2-28 21:40
楼主总结得不错。
作者: 张思语    时间: 2015-2-28 22:40
请问上海黑马有安卓班吗?是不是还得绕一圈走流程?
作者: fantacyleo    时间: 2015-2-28 23:03
张思语 发表于 2015-2-28 22:40
请问上海黑马有安卓班吗?是不是还得绕一圈走流程?

5月18日的66期安卓班就是上海首期黑马呀,流程是一样的,不过有学费减3000优惠
作者: doomsday    时间: 2015-3-14 21:00
一直以为代码块先于其它非静态代码先加载
作者: mark.tian    时间: 2015-3-14 22:52
受教了。。。。。。。。。。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2