在 Java 里定义一个类的时候,很多时候我们需要提供成员变量,成员变量专业叫法是 Memeber Variable 或者干脆的叫作 Field. 根据是否使用 static 关键字修饰,可以将 Field 分为两种:
接下来进入本文的主题:java 中 field 的初始化方式。
从初始化代码所在的位置看,可以粗略的分为三种:
第一种方式主要用于简单的赋值,使用这种方式的前提是作为初始化变量的值是已知的并且通常可以使用单行的赋值语句完成(例外?参见 Double Brace Initialization)。
?
public class Foo {
// class variable initializer
private static Logger logger = LoggerFactory.getLogger(Foo.class);
// instance variable initializer
private List<String> fooList = new ArrayList<String>();
}
?
对于复杂的初始化语句,如包含异常处理(try-catch)、使用循环结构初始化等,则需要考虑另外两种初始化方式:constructor 和?initialization?block。其中 initialization block 根据是否由 static 关键字修饰,又可分为 static(class)?initialization?block 和 instance(object)?initialization?block,前一种只能初始化 class variable,用它进行 instance variable 的初始化会导致编译错误。
?
构造方法(constructor)可以用于初始化 instance variable。除此之外,少数情况下,instance variable 的初始化需要考虑使用 instance?initialization?block 完成。例如,在匿名类中的初始化(因匿名类无构造方法),或者类中包含了多个 constructor,而它们有公共的一些复杂初始化操作,此时可以考虑将这些操作提取到 instance?initialization?block 里。除了这两种情况,在你的代码中应该尽量少使用 instance?initialization?block。
?
static initialization block 用于处理带有 class variable 的初始化操作。
?
public class BarClass {
private static Properties propTable;
static {
try {
propTable.load(new FileInputStream("/data/user.prop"));
} catch (Exception e) {
propTable.put("user", System.getProperty("user"));
propTable.put("password", System.getProperty("password"));
}
}
}
?
static initialization block ?的另一个功能与线程安全(thread safe)相关。JVM 保证使用同一个 ClassLoader 加载的类中的?static initialization block 只被执行一次,因而它是线程安全的。也正因为这一点,很多时候我们可以利用?static initialization block 执行一些初始化(write)操作,而无需对该 block 使用任何同步机制。
?
最后来看一下初始化代码的执行顺序问题。在此之前,先看下面这段代码,它可以完整执行,请试着分析一下最后的输出是什么。
?
/**
* @author wxl24life
*
*/
public class ClassInitializerOrderTest{
public static void main(String[] args) {
B a = new B();
}
}
class A {
static int a = initA();
static int initA() {
System.out.println("call 1");
return 0;
}
{
System.out.println("call 5");
}
{
System.out.println("call 6");
}
static {
System.out.println("call 2");
}
static {
System.out.println("call 3");
}
static int b = initB();
static int initB() {
System.out.println("call 4");
return 0;
}
A() {
System.out.println("call 7");
}
}
class B extends A {
{
System.out.println("call 8");
}
String str = initStr();
String initStr() {
System.out.println("call 9");
return "call 8";
}
static {
System.out.println("call 10");
}
B() {
super();
System.out.println("call 12");
}
{
System.out.println("call 11");
}
}
?
用几句话概括下初始化顺序规则(假设调用方式类似于上面代码,即使用 new caozuofu.html" target="_blank">操作符 ):
执行顺序测试代码的输出结果:
call 1 call 2 call 3 call 4 call 10 call 5 call 6 call 7 call 8 call 9 call 11 call 12
?
参考阅读: