`
wxl24life
  • 浏览: 290733 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

探讨Java类中成员变量的初始化方式

    博客分类:
  • Java
阅读更多

在 Java 里定义一个类的时候,很多时候我们需要提供成员变量,成员变量专业叫法是 Memeber Variable 或者干脆的叫作 Field. 根据是否使用 static 关键字修饰,可以将 Field 分为两种:

  1. static field:也称作 class variable,这种 filed 属于 class,并不属于单个 instance,所有该 class 的 intance 共享内存中的同一份 class field。
  2. non-static field:也称作 instance variable,它属于每一个具体的 instance,class 的每一个 instance 独享一份 non-static field。

接下来进入本文的主题:java 中 field 的初始化方式。

从初始化代码所在的位置看,可以粗略的分为三种:

  1. 在声明 field 的地方进行初始化。
  2. 在类的构造方法(constructor) 里对 field 进行初始化
  3. 在初始化块(initialization block) 中对已声明的 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 操作符 ):

  1. static 先于 non-static, non-static 先于 constructor。这里的 static 统指 static field 和 static initialization block 两种初始化方式,non-static 同上。
  2. static 初始化代码按照在源代码中定义的顺序从上往下以此执行,non-static 同上。
  3. 存在继承关系时,优先执行基类中的初始化语句。

执行顺序测试代码的输出结果:

call 1
call 2
call 3
call 4
call 10
call 5
call 6
call 7
call 8
call 9
call 11
call 12

 

参考阅读:

0
2
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics