在看Netty時,做者提到了Builder模式,曾經看過設計模式屢次,但都沒什麼感受,看過以後理解了,但很快就忘掉了,由於當時沒有應用或深入的思考過,但Builder模式是第一個讓我比較「深入」的設計模式。javascript
先看代碼:前端
public class Item { private String name; private int price; private Date date; public Item(Builder builder) { this.name = builder.name; this.price = builder.price; this.date = builder.date; } public static class Builder { private String name; private int price = 0; private Date date = new Date(); public Builder(String name) { this.name = name; } public Builder price(int price) { this.price = price; return this; } public Builder date(Date date) { this.date = date; return this; } public Item build() { return new Item(this); } }// end of Builder /* Item getter & setter */ }
Builder模式應用於構造具備多參數的對象,這些參數的特色是比較靈活,能夠自定義設置字段的值。這裏已經很好理解了,不用再解釋了。若是要構造Item對象:java
Item item = new Item.Builder("name").price(10).date(new Date()).build();
這種靈活的構造方式也能夠用JavaBean的setter實現,即便用無參的構造函數,經過set方法賦值,但這種用法的缺點是在多線程併發時不能保證對象參數的一致性,不能確保線程安全。但使用Builder模式能夠保證線程安全,所以Builder模式是既保證可讀性,又保證安全性。web
以前遇到過一個JavaBean須要繼承兩個父類,兩個父類分別存表明Jmx屬性和Page屬性。實現多繼承的方式也很簡單,中間加一層「代理」ProxyBean,持有兩個父類對象,子類只需繼承ProxyBean便可。ajax
JmxBean:數據庫
public class JmxBean{ private Object jxm; public Object getJmx(){ return this.jxm; } public void setJmx(Object jmx){ this.jmx = jmx; } }
PageBean:json
public class PageBean{ private Object page; public Object getPage(){ return this.page; } public void setPage(Object page){ this.page = page; } }
ProxyBean:後端
public class Proxybean{ private JmxBean jmxBean; private PageBean pageBean; public Object getJmx(){ return this.jxmBean.getJmx(); } public void setJmx(Object jmx){ this.jmxBean.setJmx(jmx); } /* pageBean同理 */ }
同時又得出另外一個結論,序列化框架在序列化和反序列化時調用getter、setter方法,我之前一直不理解這是爲何,而不是直接給屬性。這個例子就是反例。固然如今想一想這種想法仍是很nc的。設計模式
優點:數組
1.靜態工廠方法有名稱。構造函數的參數有時並不能正確描述返回對象,因此使用帶有具體名稱的靜態工廠方法能夠避免。同時也可解決類中須要多個簽名相同的構造函數。
2.沒必要每次調用時建立一個新的對象。能夠預先構造好實例,或將構造好的的實例緩存起來,避免建立沒必要要的重複對象。若是程序常常請求建立相同的對象,而且建立對象代價很高,使用靜態工廠方法能夠提升性能。這麼作能夠確保類是單例或不可實例化的。使得不可變類不存在兩個相同的實例,a==b <——> a.equals(b)。
3.能夠返回原返回類型的任何子類類型對象。
4.簡化代碼。
劣勢:
1.類不含有public或protected的構造函數,此類不能子類化。但這樣能夠更多地使用複合,即持有其餘類的對象,而不是繼承。
2.與其餘靜態方法沒有任何區別。劣勢在於,靜態工廠方法並無在API中像構造函數同樣標明出來,使用可能不方便。彌補的方法是使用慣用名稱,例如:valueOf、getInstance、newInstance。
通常來講,最好能重用對象而不是每次須要的時候建立一個功能相同的新對象,這個道理固然是很淺顯的。例如String、不可變對象。固然在編碼中主要對象的重用性以外,如下幾點也很重要:
1. Calendar實例代價昂貴;
2. 延遲初始化,是不推薦的。即執行構造函數時可能建立了一些暫時不會調用到的對象、常量,將這些對象和常量在具體方法調用時再初始化。不推薦的緣由是,使方法實現變得複雜,且沒法顯著提升性能。我以爲在工做中,尤爲是新員工會常常會遇到這種問題,那麼正確的方法如上所述;
3. 自動裝箱會付出必定代價,要優先使用基本類型而不是裝箱基本類型;
4.小對象建立和回收很是廉價,適當附加這種對象可提升程序可讀性;
5.只有相似數據庫鏈接池這種大對象須要對象池,JVM垃圾回收器性能優於輕量對象池。
失效的對象引用,例如實現一個相似棧的對象,這個對象本身管理內存(是出現內存泄漏的重要緣由),例如執行pop,但不將被pop位置的引用置爲null,則出現內存泄漏。
1. Java中錯誤的操做會引發內存泄漏;
2. 清空對象引用是一種例外,而不是規範,消除失效引用的最好方法是結束其生命週期;
3. 若是類本身管理內存,應警戒內存泄漏問題;
4. 使用緩存時,能夠將緩存對象做爲key存入WeakHashMap(真的沒見過,也腦補不出應該怎麼用,感受很雞肋),或將緩存對象存入LinkedHashMap,實現removeEldestEntry()做爲淘汰策略。
儘量使每一個類或者類的成員不被外界訪問到,在設計類和成員時,關注這個類的可見性範圍,正確使用訪問級別修飾符。private及包級私有(沒有修飾符)都是一個類的實現中的一部分,通常不會影響他的API。當同一個包中的另外一個類真正須要訪問一個成員時,纔將private升級成爲包級私有。
實例中不能包含公共域(或字段、屬性)。即公有類中不能包含公有域。包中含有公共可變域是線程不安全的。類中有共有靜態final域是錯誤的,客戶端能夠直接修改這種域的值。若是類是包級私有或私有的嵌套類,直接暴露域是沒問題的。
其實看過Effective Java這一條才發現以前寫的大部分代碼都是不規範的,但沒出問題的緣由一是由於本身對本身的系統熟悉,不會亂寫,二是缺對乏類、成員可見性的意識。
不可變類是實例不能被修改的。jdk中String、基本類型的包裝類、BigInteger、BigDecimal是不可變的。不可變類遵循如下五條規則:
不可變對象是線程安全的,不須要同步。所以能夠被自由共享。除非有好的理由讓類成爲可變的,不然就應該是不可變的。若是類不能被作成不可變的,也應該儘量限制他的可變性。
專門設計用來繼承的類,並有好的文檔,在使用繼承時很是安全,對於普通類進行跨包繼承是很是危險的。繼承打破了封裝性,即超類隨着版本變化會破壞子類,所以子類必須跟着超類一塊兒變化。這也體現了專門用於繼承類的優點。
書中介紹的一個例子是擴展HashSet,並重寫了add() & addAll()方法,在執行這兩個方法時作累加操做,記錄集合中增長的數量。錯誤的代碼此處就不寫了。這樣作有很是多問題,首先HashSet的addAll方法調用了add,若是調用子類的addAll方法就作了兩次計數。這種「自用性」的實現細節首先須要開發者在開發以前清楚,而且jdk不保證其餘的實現也是具備自用性的,不能保證隨着jdk版本的升級不發生變化,所以這個子類的實現是錯誤的,同時也是脆弱的。
另外一個脆弱的緣由是,隨着jdk版本的上升,超類可能會增長方法。而對於子類,設計子類每每是由於須要增長某些特殊的操做和限制,例如上面的例子中累加計數就是一種。若是超類增長方法,子類沒有改變,那麼就能夠經過子類,將不符合限制的數據,沒作特定操做的動做,直接經過父類提供的方法執行。而且超類新增的方法簽名可能會與子類本身實現的方法簽名衝突。
複合能夠解決以上問題。複合composition,即不擴展示有的類,而是在新的類中增長私有域,它引用現有類的實例,現有類成爲了新類中的一個組件。新類中能夠調用現有類對象的方法,並返回結果,這被稱爲「轉發」,forwarding,新類中的方法被稱爲「轉發方法」。在書中提供的代碼中,將複合和轉發分紅兩個類,以下:
public class InstrumentSet<E> extends ForwardingSet<E> { private int count; public InstrumentSet(Set<E> s) { super(s); } @Override public boolean add(E e) { count++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { count += c.size(); return super.addAll(c); } public int count(){ return count; } } class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } @Override public boolean add(E e) { // TODO Auto-generated method stub return s.add(e); } @Override public boolean addAll(Collection<? extends E> c) { // TODO Auto-generated method stub return s.addAll(c); } /* 其餘實現省略 */ }
InstrumentSet將一個Set(構造函數傳入的Set)包裝成InstrumentSet,因此也稱爲包裝類。
今天作了一次項目文件拷貝,全部代碼的package都出了問題,因此想寫個腳本批量替換。命令以下:
sed -i "" "s/package com\.hippoconsoleweb/package com\.pinganfu\.hippoconsoleweb/g" `grep package -rl *`
個人操做系統是MacOS,因此跟網上大部分的例子有點不一樣,mac的sed -i 須要提供一個文件後綴,作備份,若是是上面的命令,直接會覆蓋全部的文件。
sed -i:直接在文件中修改;
grep -rl *:遞歸遍歷全部文件並打印出來
若是是CentOs,去掉-i 後面的第一個參數便可
忽然發覺對Java中clone的使用很模糊,在上一個項目中用過clone方法,但如今再看發現有些不懂。因而看書、百度。
在論壇中看到這樣一句「Object的clone方法由於是protected的,因此不能直接調用」。這句話的結論是沒錯的,隨隨便便搞個對象obj.clone(),會報出method invisible的錯誤。當時以爲雖然結論試驗出來是沒錯的,但他說的好像有點怪怪的。因而本身作實驗,(此處腦補代碼)寫一個父類,子類繼承,父類中有一個protected方法,固然我是寫在同一個.java文件中,這也是錯誤的開始。這樣實驗的結果一定是能夠訪問的,由於protected方法跟調用方法的類在同一個包中。做爲初學者,在看到protected時每每只考慮到了繼承關係,而忘記了包關係。把父類移到另外一個包中,一樣報出了method invisible。最後再明確一下protected的做用域:子類或同一個包中的非子類。
再說說Cloneable接口。編碼中咱們一般的作法是implements Cloneable,並實現:
@Override public Object clone(Object o){}
通常狀況將clone方法定義爲public,沒有違反子類的訪問修飾權限不能小於父類的 。CLoneable接口中並無定義任何方法,他是一個標誌,相似於Serializable。但若是不實現Cloneable接口,在調用Object的clone方法時,會報出CloneNotSupportedException。
通常會在重寫的clone方法中調用super.clone()。雖然調了父類的方法(其實就是Object的clone),但拷貝的並非父類,而是子類。網上有「專家」解釋了爲何會這樣,算了我記不住就不轉述了,反正我以爲這不是特別難理解。
調用super.clone(),即所謂的淺拷貝。我之前一直錯誤的認爲,淺拷貝是建立引用,而不是複製引用指向的對象。其實這種想法如今以爲很愚蠢,很容易就能舉出反例。淺拷貝將原有內存,原封不動複製到另外一塊內存中。若是對象沒有重寫過equals和hashCodet方法,那麼不管是==仍是equals都返回false,這點其實徹底能夠說明淺拷貝的工做方式。引用不一樣,對象也不一樣。固然對象的不一樣指的是內存,不是邏輯不一樣。
這樣的結論能夠爲深拷貝、淺拷貝提供基礎。既然內存同樣,那麼拷貝的複雜對象中若是包含對其餘對象的引用,也就是所謂的拷貝的是複雜對象,就會出現引用不一樣但指向的對象相同這個問題。這也是深拷貝存在的緣由。這一段中不會再繼續說明深拷貝,由於前面的結論已經搞清楚了clone的本質。
直接上代碼了:
前端Js:
//migrationResultList是一個複雜對象數組,對象中包含多個字段 var migrationResultList = []; $.ajax({ type : "POST", url : "submitReduce", contentType: "application/json; charset=utf-8", data : JSON.stringify(migrationResultList), dataType : "json", success : function(data){ }, });
後端Java:
public @ResponseBody Map<String, Object> submitReduce(@RequestBody List<MigrationResult> migrationResultList) { return null; } //或者入參使用數組 public @ResponseBody Map<String, Object> submitReduce(@RequestBody MigrationResult[] migrationResultList) { return null; }