您的位置:首页 > 编程语言 > Java开发

Java的初始化块、静态初始化块、构造函数的执行顺序及用途探究

2016-07-24 17:02 393 查看
  Java与C++有一个不同之处在于,Java不但有构造函数,还有一个”初始化块“(Initialization Block)的概念。下面探究一下它的执行顺序与可能的用途。

执行顺序

  首先定义A, B, C三个类用作测试,其中B继承了A,C又继承了B,并分别给它们加上静态初始化块、非静态初始化块和构造函数,里面都是一句简单的输出。

  主类Main里面也如法炮制。

Compiled from "Main.java"
class C extends B {
C();
Code:
0: aload_0
1: invokespecial #1                  // Method B."<init>":()V
4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc           #3                  // String Instance init C.
9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc           #5                  // String Constructor C.
17: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return

static {};
Code:
0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc           #6                  // String Static init C.
5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}


C.class的反编译结果

  静态初始化块仍然单独分出一部分,输出了我们的调试语句。而另一部分,仍然还是类C的构造函数C();,可以看到它先调用了父类B的构造函数,接着输出了我们初始化块中的语句,然后才输出我们写在构造函数中的语句,最后返回。多次试验也都是如此。于是我们能够推断:初始化块的代码是被加入到子类构造函数的前面,父类初始化的后面了。

可能的用途:

  既然执行顺序和大概原理都摸清了,那么就要探讨一下初始化块的可能的用途。

 静态初始化块
  1. 用于初始化静态成员变量

  比如给类C增加一个静态成员变量sub,我们在static块里面给它赋值为5:

class C extends B {

static public int a;

static {
a = 5;
System.out.println("Static init C.");
}

......

}


  main函数里输出这个静态变量C.sub:

public static void main(String[] args) {
System.out.println("Value of C.sub: " + C.sub);
}


  则输出结果:

Static init Main.
Static init A.
Static init B.
Static init C.
Value of C.sub: 5


  符合类被第一次加载时执行静态初始化块的结论,且C.sub被正确赋值为5并输出了出来。

  但是乍一看似乎没有什么用,因为静态成员变量在定义时就可以顺便赋值了。因此在赋值方面有点鸡肋。

  2. 执行初始化代码

  比如可以记录第一次访问类的日志,或方便单例模式的初始化等。对于单例模式,可以先用static块初始化一些可能还被其他类访问的基础参数,等到真正需要加载大量资源的时候(getInstance)再构造单体,在构造函数中加载资源。

 非静态初始化块
  这个就没什么好说的了,基本跟构造函数一个功能,但比构造函数先执行。最常见的用法应该还是代码复用,即多个重载构造函数都有若干段相同的代码,那么可以把这些重复的代码拉出来放到初始化块中,但仍然要注意它的执行顺序,对顺序有严格要求的初始化代码就不适合使用了。

总结:

静态初始化块的优先级最高,也就是最先执行,并且仅在类第一次被加载时执行;

非静态初始化块和构造函数后执行,并且在每次生成对象时执行一次;

非静态初始化块的代码会在类构造函数之前执行。因此若要使用,应当养成把初始化块写在构造函数之前的习惯,便于调试;

静态初始化块既可以用于初始化静态成员变量,也可以执行初始化代码

非静态初始化块可以针对多个重载构造函数进行代码复用

本文基于

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
发布,欢迎引用、转载或演绎,但是必须保留本文的署名BlackStorm以及本文链接/article/11921747.html,且未经许可不能用于商业目的。如有疑问或授权协商请与我联系
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: