《effective java》可謂是java學習者心中的一本絕對不能不拜讀的好書,她對於目標讀者(有一點編程基礎和開發經驗)的人來說,由淺入深,言簡意賅。每一章節都分為若干的條目,完全可以利用平時的零碎時間片來閱讀和思考。僅僅是閱讀了第二章,建立和銷毀對象,從靜態工廠、構造器等基礎得不能再基礎的知識,卻使我有一種特別的感覺,就如同見到一位會使我怦然心動的女生,那種驚喜,是那麼的美妙,卻也是那麼的震撼。原來女生還能夠如此地美,原來書,所謂讓人頭暈的編程類書籍,可以寫得如此地好,直擊心靈,讓人有相見恨晚的感覺。正如我在朋友圈寫到的,閱書如品茗,品酒,如人生。她完全不同於《21天精通xxoo》的粗製濫造,也不像《Think
in java》一樣對於誰都高不可攀(我讀起來有絲絲的苦澀)。
好了說太多了,現在開始本文吧。
更多精彩,請關註:http://muscle1990.com/?p=295
本章主題是講述建立和銷毀對象,何時以及如何建立,何時以及如何銷毀對象,如何確保他們能夠適時讀銷毀,,以及如何管理對象銷毀之前必須進行的各種清理工作。
我們先來看看這個靜態Factory 方法,返回累的執行個體:
public static Boolean valueOf(boolean b){return b?Boolean.TRUE:Boolean.FALSE; }
通過調用該方法,就可以非常方便地利用工廠得到執行個體。
靜態工廠和構造器不同的優勢在於:
- 她們有名稱,as what you can see。
- 不需要每次調用他們的時候都建立一個新的對象。
- 她們可以靈活地返回原類型的任何子類型對象。
- 在建立參數化執行個體的時候,她使得代碼變得更加的簡潔。
for example:
在我們使用帶參構造器建立一個map時,需要這樣:
Map<String,String> stringMap=new HashMap<String,String>();
但是,假設HashMap提供以下靜態工廠:
public static <K,V> HashMap<K,V> newInstance(){return new HashMap<K,V>(); }
那麼我們在建立map的時候,就可以這樣寫了:
Map<String,String> stringMap =HashMap.newInstance();
倘若你在工作中常需要做建立對象的事,會不會有一絲的驚喜呢?
靜態工廠和構造器有個共同的局限性,都不能很好地擴增到大量可選屬性。
考慮一個人的資料,有些是必須要的,如Id,name,有些是可選的,如地區、身高,體重等。
方法一,帶參構造器
package new_and_destory_object;/* * @author 莫仕豪 * moshihao@gmail.com muscle1990.com * @version:2013-7-31 下午9:53:36 * */public class Person { private String id;//必選 private String name;//必選 private int height;//可選 private int weight;//可選 //private xxx ooo;//可選 //and so on...//可選 public Person(String id,String name){this.id=id;this.name=name; } public Person(String id,String name,int height){this.id=id;this.name=name;this.height=height; } public Person(String id,String name,int height,int weight){this.id=id;this.name=name;this.height=height;this.weight=weight; } public Person(String id,String name,int height,int weight,xxx ooo,...){this.id=id;this.name=name;this.height=height;this.weight=weight;... } }
然而,在我們建立對象的時候,就會這樣:
Person Muscle=new Person("helloworld","Muscle",172,55,........);
如果有很多屬性呢?我參與的實際項目中,遇到過超過150個屬性的。使用構造器設參方法,難道我需要寫上百個?再慢慢選?慢慢填資料?再save?oh my god!
方法二:javaBean setter 模式
package new_and_destory_object;/* * @author 莫仕豪 * moshihao@gmail.com muscle1990.com * @version:2013-7-31 下午9:53:36 * */public class Person { private String id;//必選 private String name;//必選 private int height;//可選 private int weight;//可選 //private xxx ooo;//可選 //and so on...//可選 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } //... }
這種方法深信大家也不會陌生,然而,同樣的情況,屬性多呢。。。
Person Muscle = new Person();Muscle.setId("helloworld");...
除此之外,因為構造過程被分到幾次的調用,所以javaBean會產生不一致的可能狀態。
方法三:builder模式
幸運的是,還有第三種替代方法,既能夠保持像重疊構造器那樣安全,又能像javaBean那樣易讀靈活。
package new_and_destory_object;/* * @author 莫仕豪 * moshihao@gmail.com muscle1990.com * @version:2013-7-31 下午9:53:36 * */public class Person { private String id;// 必選 private String name;// 必選 private int height;// 可選 private int weight;// 可選 // and so on... public static class Builder {// 必選屬性private final String id;private final String name;// 可選private int height =0;private int weight =0;// and so on... public Builder(String id,String name){ this.id=id; this.name=name;}public Builder height(int height){ this.height=height; return this;}public Builder weight(int weight){ this.weight=weight; return this;}// and so on... public Person build(){ return new Person(this);} } public Person(Builder builder) {id =builder.id;name =builder.name;height =builder.height;weight =builder.weight; }}
注意Person是不可變的。下面的建立對象的實現:
Person Muscle = new Person.Builder("helloworld","Muscle").height(173).weight(55).build();
這樣的代碼,就會變得非常容易編寫,並且簡潔易懂。
這部分暫時沒多大感覺,單例用得比較少,不過也學習了。
- 第四條:通過私人構造器,強化不可執行個體化的能力。
這個講得不錯,在HSBC Gltc的Interview的時候有問到。
這部分寫得特別受用,不多說,來碼:
String s=new String("String");//永遠不要這樣寫代碼,求你了
賦值的String已經為對象執行個體,這樣就是等於多了一個執行個體,如果是在比較頻繁的String操作,這樣會產產生千上萬無用的執行個體。
String s="String";//這樣就可以了,簡潔明了不浪費
還需要將可重用的重用,正如同書中例子一樣,起點時間,終點時間設為static final,這樣就可以了,完全沒有必要每一次調用都執行個體化。
裝箱拆箱問題:
public static void main(String args[]){//需要拆箱時間:16s//基本類型時間:2sLong sum =0L;for(long i=0;i<Integer.MAX_VALUE;i++){ sum +=1;}System.out.println(sum); }
倘若你還不知道Long和long的區別的話,簡單點說就是Long是對象,long是基本類型,他們轉換需要裝箱拆箱。而效率從上可見一般,優先使用基本類型,不要手滑寫錯類型。。。
消除到期對象引用
package new_and_destory_object;import java.util.Arrays;import java.util.EmptyStackException;/* * @author 莫仕豪 * moshihao@gmail.com muscle1990.com * @version:2013-7-31 下午11:13:52 * */public class Stack { public Object[] elements; private int size =0; private static final int DEFAULT_ININAL_CAPACITY =16; public Stack(){elements=new Object[DEFAULT_ININAL_CAPACITY]; } public void push(Object e){ensureCapacity();elements[size++]=e; } public Object pop(){if(0==size){ throw new EmptyStackException();} return elements[--size];} private void ensureCapacity() {if(elements.length == size){ elements =Arrays.copyOf(elements, 2 *size+1);} }}
因為棧會維護對象的到期引用,瞭解JVM記憶體管理的應該明白,有引用的對象,就算明知道是垃圾,一樣不會回收。所以這段代碼就算運行沒有一點問題,但是會有導致記憶體溢出的風險。
我們需要修改彈出棧的操作:
public Object pop(){if(0==size){ throw new EmptyStackException(); }Object result =elements[--size];elements[size] =null;return result;}
一般而言,只要是類自己管理記憶體,我們就需要警惕記憶體流失的發生。
除此之外,我們還需要注意,記憶體流失的另一個常見的來源是緩衝。一旦你把對象放到緩衝中,就很容易被淡忘。
老實說,沒用過,但是瞭解一下也是好的
effective java讀書小記(一)建立和銷毀對象,這篇讀書筆記連想帶碼,用了差不多兩個小時的時間,很久沒有這樣寫過讀書筆記了,學海無涯,書山有路,加油!
Muscle Mo 2013.7.31.晚寫於大學城宿舍