面的程序使用了一個Counter類來跟蹤每一種家庭寵物叫喚的次數(shù)。那么該程序會打印出什么呢?
class Counter {
private static int count = 0;
public static final synchronized void increment() {
count++;
}
public static final synchronized int getCount() {
return count;
}
}
class Dog extends Counter {
public Dog() { }
public void woof() { increment(); }
}
class Cat extends Counter {
public Cat() { }
public void meow() { increment(); }
}
public class Ruckus {
public static void main(String[] args) {
Dog dogs[] = { new Dog(), new Dog() };
for (int i = 0; i < dogs.length; i++)
dogs[i].woof();
Cat cats[] = { new Cat(), new Cat(), new Cat() };
for (int i = 0; i < cats.length; i++)
cats[i].meow();
System.out.print(Dog.getCount() + " woofs and ");
System.out.println(Cat.getCount() + " meows");
}
}
我們聽到兩聲狗叫和三聲貓叫——肯定是好一陣喧鬧——因此,程序應(yīng)該打印2 woofs and 3 meows,不是嗎?不:它打印的是5 woofs and 5 meows。所有這些多出來的吵鬧聲是從哪里來的?我們做些什么才能夠阻止它?
該程序打印出的犬吠聲和貓叫聲的數(shù)量之和是10,它是實際總數(shù)的兩倍。問題在于Dog和Cat都從其共同的超類那里繼承了count域,而count又是一個靜態(tài)域。每一個靜態(tài)域在聲明它的類及其所有子類中共享一份單一的拷貝,因此Dog和Cat使用的是相同的count域。每一個對woof或meow的調(diào)用都在遞增這個域,因此它被遞增了5次。該程序分別通過調(diào)用Dog.getCount和Cat.getCount讀取了這個域兩次,在每一次讀取時,都返回并打印了5。
在設(shè)計一個類的時候,如果該類構(gòu)建于另一個類的行為之上,那么你有兩種選擇:一種是繼承,即一個類擴展另一個類;另一種是組合,即在一個類中包含另一個類的一個實例。選擇的依據(jù)是,一個類的每一個實例都是另一個類的一個實例,還是都有另一個類的一個實例。在第一種情況應(yīng)該使用繼承,而第二種情況應(yīng)該使用組合。當(dāng)你拿不準(zhǔn)時,優(yōu)選組合而不是繼承[EJ Item 14]。
一條狗或是一只貓都不是一種計數(shù)器,因此使用繼承是錯誤的。Dog和Cat不應(yīng)該擴展Counter,而是應(yīng)該都包含一個計數(shù)器域。每一種寵物都需要有一個計數(shù)器,但并非每一只寵物都需要有一個計數(shù)器,因此,這些計數(shù)器域應(yīng)該是靜態(tài)的。我們不必為Counter類而感到煩惱;一個int域就足夠了。
下面是我們重新設(shè)計過的程序,它會打印出我們所期望的2 woofs, 3 meows:
class Dog {
private static int woofCounter;
public Dog() { }
public static int woofCount() { return woofCounter; };
public void woof() { woofCounter++; }
}
class Cat {
private static int meowCounter;
public Cat() { }
public static int meowCount() { return meowCounter; };
public void meow() { meowCounter++; }
class Counter {
private static int count = 0;
public static final synchronized void increment() {
count++;
}
public static final synchronized int getCount() {
return count;
}
}
class Dog extends Counter {
public Dog() { }
public void woof() { increment(); }
}
class Cat extends Counter {
public Cat() { }
public void meow() { increment(); }
}
public class Ruckus {
public static void main(String[] args) {
Dog dogs[] = { new Dog(), new Dog() };
for (int i = 0; i < dogs.length; i++)
dogs[i].woof();
Cat cats[] = { new Cat(), new Cat(), new Cat() };
for (int i = 0; i < cats.length; i++)
cats[i].meow();
System.out.print(Dog.getCount() + " woofs and ");
System.out.println(Cat.getCount() + " meows");
}
}
我們聽到兩聲狗叫和三聲貓叫——肯定是好一陣喧鬧——因此,程序應(yīng)該打印2 woofs and 3 meows,不是嗎?不:它打印的是5 woofs and 5 meows。所有這些多出來的吵鬧聲是從哪里來的?我們做些什么才能夠阻止它?
該程序打印出的犬吠聲和貓叫聲的數(shù)量之和是10,它是實際總數(shù)的兩倍。問題在于Dog和Cat都從其共同的超類那里繼承了count域,而count又是一個靜態(tài)域。每一個靜態(tài)域在聲明它的類及其所有子類中共享一份單一的拷貝,因此Dog和Cat使用的是相同的count域。每一個對woof或meow的調(diào)用都在遞增這個域,因此它被遞增了5次。該程序分別通過調(diào)用Dog.getCount和Cat.getCount讀取了這個域兩次,在每一次讀取時,都返回并打印了5。
在設(shè)計一個類的時候,如果該類構(gòu)建于另一個類的行為之上,那么你有兩種選擇:一種是繼承,即一個類擴展另一個類;另一種是組合,即在一個類中包含另一個類的一個實例。選擇的依據(jù)是,一個類的每一個實例都是另一個類的一個實例,還是都有另一個類的一個實例。在第一種情況應(yīng)該使用繼承,而第二種情況應(yīng)該使用組合。當(dāng)你拿不準(zhǔn)時,優(yōu)選組合而不是繼承[EJ Item 14]。
一條狗或是一只貓都不是一種計數(shù)器,因此使用繼承是錯誤的。Dog和Cat不應(yīng)該擴展Counter,而是應(yīng)該都包含一個計數(shù)器域。每一種寵物都需要有一個計數(shù)器,但并非每一只寵物都需要有一個計數(shù)器,因此,這些計數(shù)器域應(yīng)該是靜態(tài)的。我們不必為Counter類而感到煩惱;一個int域就足夠了。
下面是我們重新設(shè)計過的程序,它會打印出我們所期望的2 woofs, 3 meows:
class Dog {
private static int woofCounter;
public Dog() { }
public static int woofCount() { return woofCounter; };
public void woof() { woofCounter++; }
}
class Cat {
private static int meowCounter;
public Cat() { }
public static int meowCount() { return meowCounter; };
public void meow() { meowCounter++; }