特创论
有时候,对于一个类来说,跟踪其创建出来的实例个数会非常有用,其典型实现是通过它的构造器递增一个私有静态字段来完成的。
[java]
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会对它进行垃圾回收。
改进后的代码如下:
[java]
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),这意味着程序在对象计数器溢出之前,不得不运行大约三千年。
另外,还需注意的是,如果在多线程中创建该对象存在线程安全性,因此此代码需对递增计数器进行同步:
[java]
class Creature {
private static long numCreated = 0;
public Creature() {
synchronized (Creature.class) {
numCreated++;
}
}
public synchronized static long numCreated() {
return numCreated;
}
}
如果使用的是5.0或更新的版本,可以使用AtomicLong实例,这样就无需再使用syncrhonized了:
[java]
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类型的计数器。
补充:软件开发 , Java ,