特创论

来源:互联网 发布:美工抠图总结 编辑:程序博客网 时间:2024/05/01 14:42

有时候,对于一个类来说,跟踪其创建出来的实例个数会非常有用,其典型实现是通过它的构造器递增一个私有静态字段来完成的。

public class Creator {public static void main(String[] args) {for(int i = 0; i < 100; i++)Creature creature = new Creature();System.out.println(Creature.numCreated());}}class Creature {private static long numCreated = 0;public Creature() {numCreated++;}public static long numCreated() {return numCreated;}}

这个程序会打印什么呢?

因为循环一共创建了100次,所以应该打印100。其实该程序根本不能编译。因为在main方法中的循环中,只有一条局部变量声明语句,而JLS[JLS 14.4]规定在for,while,do循环中不允许重复执行局部变量声明语句,局部变量声明语句只能放在语句块中,也就是放在大括号中。另外,由于创建了对象并没有使用,因此,没有必要保存其引用,在适当时机GC会对它进行垃圾回收。

改进后的代码如下:

public class Creator {public static void main(String[] args) {for(int i = 0; i < 100; i++) new Creature();System.out.println(Creature.numCreated());}}class Creature {private static long numCreated = 0;public Creature() {numCreated++;}public static long numCreated() {return numCreated;}}

这样编译后运行,打印出了预期的值:100.

这里保存实例个数的变量类型设为long是有依据的。目的是防止溢出。因为int是有符号的整型,它的最大值为pow(2,31)-1。而一个程序每秒钟创建1亿个对象是有可能的,因此可能存在溢出现象。如果将int类型改为long,long的最大值为pow(2,63)-1,约为9.2*pow(10,18),这意味着程序在对象计数器溢出之前,不得不运行大约三千年。

另外,还需注意的是,如果在多线程中创建该对象存在线程安全性,因此此代码需对递增计数器进行同步:

class Creature {private static long numCreated = 0;public Creature() {synchronized (Creature.class) {numCreated++;}}public synchronized static long numCreated() {return numCreated;}}

如果使用的是5.0或更新的版本,可以使用AtomicLong实例,这样就无需再使用syncrhonized了:

class Creature {private static AtomicLong numCreated = new AtomicLong();public Creature() {numCreated.incrementAndGet();}public static long numCreated() {return numCreated.get();}}

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

原创粉丝点击