如下所示代码:

public class Example055 {

	private static long numCreated = 0;

	public Example055() {
		numCreated++;
	}

	public static long numCreated() {
		return numCreated;
	}

	public static void main(String[] args) {
		for(int i=0;i<10;i++)
			Example055 e55 = new Example055(); //1
		System.out.println(Example055.numCreated());
		
	}
}


    输出说明:

    本段程序是无法编译通过的。问题出在了位置1。


    代码分析:

    一个本地变量声明看起来像是一条语句,但是从技术上说,它不是,它应该是一个本地变量声明语句Java语言规范不允许一个本地变量声明语句作为一条语句在 for、while或do循环中重复执行。一个本地变量声明作为一条语句只能直接出现在一个语句块中。(一个语句块是由一对花括号以及包含在这对花括展中的语句和声明构成的。)

    所以上述程序需要修改,可以通过取消声明或者添加语句块:

    /** 方法1:将声明放入语句块*/
		for(int i=0;i<10;i++){
			Example055 e55 = new Example055();
		}
		System.out.println(Example055.numCreated());
		
		/**方法2:取消声明,直接创建实例*/
		for(int i=0;i<10;i++)
			new Example055();
		
		System.out.println(Example055.numCreated());

    通过修改后,程序可以正确编译并输出结果。但是修改过的程序仍然存在问题:当使用多线程运行时,程序可能会出现错误,也就是说上述程序不是线程安全的。一种修改方法是加入线程同步:

public class Example055Safe {

	private static long numCreated = 0;

	public Example055Safe() {
		synchronized (Example055Safe.class) {
			numCreated++;
		}
	}

	public synchronized static long numCreated() {
		return numCreated;
	}
	
	public static void main(String[] args) {

		/*
		 * for(int i=0;i<10;i++) Example055 e55 = new Example055();
		 * System.out.println(Example055.numCreated());
		 */

		/** 方法1:将声明放入语句块 */
		for (int i = 0; i < 10; i++) {
			Example055Safe e55 = new Example055Safe();
		}
		System.out.println(Example055Safe.numCreated());

		/** 方法2:取消声明,直接创建实例 */
		for (int i = 0; i < 10; i++)
			new Example055Safe();

		System.out.println(Example055Safe.numCreated());
	}
}

    在JDK1.5以后,也可以通过使用AtomicLong来代替原来的long解决线程同步问题,而不需要使用如上所示的synchronized:

    private static AtomicLong numCreated = new AtomicLong();

	public Example055AtomicLong() {
		numCreated.incrementAndGet();
	}

	public synchronized static long numCreated() {
		return numCreated.get();
	}

    

    总之,一个本地变量声明不能被用作 for、 while 或 do 循环中的重复执行语句,它作为一条语句只能出现在一个语句块中。 另外,在使用一个变量来对实例的创建进行计数时,要使用 long 类型而不是 int 类型的变量,以防止溢出。最后,如果你打算在多线程中创建实例,要么将对实例计数器的访问进行同步,要么使用一个 AtomicLong 类型的计数器。



注:本【java解惑】系列均是博主阅读《java解惑》原书后将原书上的讲解和例子部分改编然后写成博文进行发布的。所有例子均亲自测试通过并共享在github上。通过这些例子激励自己惠及他人。同时本系列所有博文会同步发布在博主个人微信公众号搜索“爱题猿”或者“ape_it”方便大家阅读。如果文中有任何侵犯原作者权利的内容请及时告知博主以便及时删除如果读者对文中的内容有异议或者问题欢迎通过博客留言或者微信公众号留言等方式共同探讨。

源代码地址https://github.com/rocwinger/java-disabuse