effective java中文版 第二章 建立和銷燬物件
第1條:考慮用靜態工廠方法替代構造器
如下方法將boolean基本型別值轉換為了一個Boolean物件引用
public static Boolean valueOf(boolean b){
return b ?Boolean.TRUE:Boolean.FALSE;
}
靜態工廠方法與構造器相比的第一大優勢:它們會有名稱
第二大優勢(重點):不必在每次呼叫它們的時候都建立一個新物件。靜態工廠方法能夠為重複的呼叫返回相同的物件,這樣有助於類總能嚴格控制在某個時刻哪些例項應該存在
第三大優勢:它們可以返回原返回型別的任何子型別的物件
第四大優勢在於:在建立引數化型別例項的時候,它們讓程式碼變得更加簡潔。例如HashMap提供了這個靜態工廠
public static <K,V> HashMap<K,V> newInstance(){
return new HashMap<K,V>();
}
如下為靜態工廠方法的一些慣用名稱
1,valueOf 它實際上是型別轉換方法
2,getInstance 返回的例項通過方法的引數來描述
3,newInstance 確保返回的每個例項與其他例項不同
第2條 遇到多個構造器引數時要考慮用構建器
Bulilder模式:不直接生成想要的物件,而是讓客戶端利用所有必要的引數呼叫構造器得到一個builder物件。然後客戶端在builder物件上呼叫類似於setter的方法,來設定每個相關的可選引數。最後呼叫無參的build方法來生成不可變的物件。這個builder是它構建的類的靜態成員類。
下面為例項
public class Demo{ private final int a; private final int b; public static class Builder{ private final int a; private final int b; public Builder(int a,int b){ this.a = a; this.b = b; } public Demo bulid(){ return new Demo(this); } } private Demo(Builder builder){ a = builder.a; b = builder.b; } }
如果類的構造器或者靜態工廠中有多個引數,設計這種類時Builder模式是個不錯的選擇。
第3條 用列舉類強化Singleton屬性
public enum Elivis{
INSTANCE;
public void leaveTheBuilding(){....
}
}
這種方法更加簡潔,無償提供了序列化機制,防止多次例項化。
第4條 通過私有構造器強化不可例項化的能力
在我們日常開發工具類時,不希望被例項化,這樣的例項對它沒有任何意義。然而,在缺少顯示構造器的情況下,編譯器會自動提供一個公有的,無參的預設構造器。這種情況我們只要讓這個類包含私有構造器,它就不能被例項化了。
public class UtilityClass{
private UtilityClass(){
…
}
}
第5條 避免建立不必要的物件
直接上例子。。。
public class Person {
private final Date birthDate;
public boolean isBabyBoomer(){
//Unnecessary allocation of expensive object
Calendar a = Calendar.getInstance(TimeZone.get("GMT"));
a.set(....);
Date startDate = a.getTime();
a.set(...);
Date endDate = a.getTime();
return birthDate.compareTo(startDate) >= 0 &&
birthDate.compareTo(boomEnd) < 0;
}
}
這個例項每次被呼叫的時候都會新建一個Calendar,一個TimeZone和兩個Date。這是不必要的。下面的版本用靜態的初始化器提升了效率
class Person{
private final Date birthDate;
private static final Date START_DATE;
private static final Date END_DATE;
static{
Calendar a = Calendar.getInstance(TimeZone.get("GMT"));
a.set(...);
START_DATE = a.getTime();
a.set(...);
END_DATE = a.getTime();
}
public boolean isBabyBoomer(){
return birthDate.compareTo(START_DATE) >= 0 &&
birthDate.compareTo(END_DATE) < 0;
}
}
改進後的程式碼只在初始化的時候建立這些類的時候例項化一次,而不是每次呼叫isBabyBoomer的時候都建立這些例項。如果方法被頻繁的呼叫,將會顯著地提高效能。
java1.5發行版本中,有一種建立多餘物件的新方法,稱作自動裝箱,它允許程式設計師將基本型別和裝箱基本型別混用,按需要自動裝箱和拆箱。
上例子,哈哈哈
public static void main(String[] args){
Long sum = 0L;
for(long i = 0;i < Integer.MAX_VALUE;i++){
sum += i;
}
}
這段程式算出的答案是正確的,但是效率低。就因為變數sum被宣告為Long而不是long.
結論:要優先使用基本型別而不是裝箱基本型別,要當心無意識的自動裝箱。
第6條 消除過期物件的引用 (阿西吧。。這章沒怎麼看懂)
先上一個簡單的棧實現的例子
public class Stack{
private object[] elements;
private ine size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY ];
}
public void push(Object e){
ensureCapacity();
elements [size++] = e;
}
public void pop(){
if(size == 0){
throw new EmptyStackException();
}
return elements[--size];
}
public void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements,2*size + 1);
}
}
}
這段程式哪裡發生了記憶體洩漏呢?如果一個棧先是增長然後再收縮,那麼棧中彈出來的物件將不會被當作垃圾回收,即使程式不再使用這些物件。because棧內部維護著對這些物件的過期引用。如果一個物件引用被無意識的保留起來了,垃圾回收機制不僅不會回收這個物件,也不會處理被這個物件的所有其他物件。
修復方法:一旦物件引用過期,清空這些引用即可。
一般而言,只要類是自己管理記憶體, 程式設計師就該警惕記憶體洩漏問題。
。。。。。。(這章不好理解啊。。。。。先略。。。)
第7條 避免使用終結方法(阿西吧。。。只是理解了一部分。。。心塞)
終結方法的一個缺點在於不能保證會被及時的執行。
及時的執行終結方法正是垃圾回收演算法的一個主要功能。
結論:不應該依賴終結方法來更新重要的持久狀態。
使用終結方法有一個非常嚴重的效能損失。
如果類的物件中封裝的資源確實需要終止,這個時候只需要提供一個顯示的終結方法。並要求該類的客戶端在每個例項不再有用的時候呼叫這個方法。其中,該例項必須記錄下自己是否已經被終止。