Num1:考慮用靜態工廠方法代替構造器
對於類而言,常見的方法是提供一個公有的構造器,但其實還有一種方法叫做靜態工廠方法(static factory method),它只是一個返回類的例項靜態方法。
目前比較流行的規範是把靜態工廠方法命名為valueOf
或者getInstance
。
valueOf
:該方法返回的例項與它的引數具有同樣的值,例如:
Integer a=Integer.valueOf(100); //返回取值為100的Integer物件
從上面程式碼可以看出,valueOf()方法能執行型別轉換操作,在本例中,把int型別的基本資料轉換為Integer物件。
getInstance
:返回的例項與引數匹配,例如:
Calendar cal=Calendar.getInstance(Locale.CHINA); //返回符合中國標準的日曆
優勢:
- 靜態工廠方法與構造器不同的第一大優勢在於,他們有名稱,更有可讀性。
- 靜態工廠方法與構造器不同的第二大優勢在於,不必每次呼叫它們的時候都建立一個新物件。
- 靜態工廠方法與構造器不同的第三大優勢在於,它們可以返回原返回型別的任何子型別的物件。
- 靜態工廠方法與構造器不同的第四大優勢在於,在建立引數化例項的時候,它們使程式碼變得更加簡潔。
缺點:
- 類如果不含公有的或者受保護的構造器,就不能被子類化。
- 它們與其他的靜態方法實際上沒有任何區別。
Num2:遇到多個構造器引數時要考慮用構造器
一般有以下三種構造器的方式
- 重疊構造器模式
- JavaBeans模式
- Builder模式
重疊構造器模式
public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat,
int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings, int calories, int fat,
int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
}
}
JavaBeans模式
在這種模式下,呼叫一個無參構造器來建立物件,然後呼叫setter
方法來設定每個必要的引數。如下:
public class NutritionFacts {
// Parameters initialized to default values (if any)
private int servingSize = -1; // Required; no default value
private int servings = -1; // " " " "
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() {
}
// Setters
public void setServingSize(int val) {
servingSize = val;
}
public void setServings(int val) {
servings = val;
}
public void setCalories(int val) {
calories = val;
}
public void setFat(int val) {
fat = val;
}
public void setSodium(int val) {
sodium = val;
}
public void setCarbohydrate(int val) {
carbohydrate = val;
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);
}
}
Builder模式
不直接生成想要的物件,而是讓呼叫者利用所有必要的引數呼叫構造器,得到一個builder物件,然後客戶端在builder物件上呼叫類似於setter
的方法,來設定每個相關的可選引數,最後客戶端呼叫無參的build
方法來生成一個不可變的物件。
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();
}
}
小結
與構造器相比,builder模式的優勢在於,builder可以有多個可變的引數,構造器就像方法一樣,只能有一個可變引數。總之,如果類的構造器或靜態工廠中具有多個引數,設計這種類的時候,builder模式就是一種不錯的選擇。
Num3:用私有構造器或者列舉型別強化Singleton屬性
Singleton
簡單的說就是僅僅被例項化一次的類。實現Singleton
有三種方式。
Field方式
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
Method方式
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public static Elvis getInstance() {
return INSTANCE;
}
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.getInstance();
elvis.leaveTheBuilding();
}
}
特別要說明下,如果要使得類支援可序列化,僅僅加上implements Serializable
是不夠的,需要在方法里加上這麼一個方法。
private Object readResolve() {
// Return the one true Elvis and let the garbage collector
// take care of the Elvis impersonator.
return INSTANCE;
}
列舉類方式
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
Num4:消除過期的物件引用
所謂過期引用:是指永遠也不會再被解除的引用。在支援垃圾回收的語言中,記憶體洩露是很隱蔽的,如果一個物件引用被這個物件無意識地保留起來,那麼,垃圾回收機制不僅不會處理這個物件,而且也不會處理被這個物件所引用的所有其他物件,從而對效能造成潛在的重大影響。
那麼該如何修復呢,很簡單:一旦物件引用已經過期了,只需要清空這些引用即可。
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
一般而言,只要類是自己管理記憶體,程式設計師就應該警惕記憶體洩露問題。
記憶體洩露的另一個常見問題是快取,所以一般可以弱引用weak reference
代表快取,當快取過期後,它們會自動被刪除,記住快取項的生命週期是外部引用而不是值引用的。
記憶體洩露的第三個常見來源是監聽器和其他回撥。如果你實現了一個API,客戶端在這個API中註冊回撥,卻沒有顯式地取消註冊,那麼除非採取某些動作,否則就會積聚,確保回撥立即被當作垃圾回收的最佳方法是隻儲存它們的弱引用。