零碎的知識&技能-持續更新

1、Java的Builder模式-20161004

    在看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

2、JavaBean的多繼承-20161006

    以前遇到過一個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的。設計模式

3、關於對象

3.1 靜態工廠方法代替構造函數-20161007

優點:數組

    1.靜態工廠方法有名稱。構造函數的參數有時並不能正確描述返回對象,因此使用帶有具體名稱的靜態工廠方法能夠避免。同時也可解決類中須要多個簽名相同的構造函數。

    2.沒必要每次調用時建立一個新的對象。能夠預先構造好實例,或將構造好的的實例緩存起來,避免建立沒必要要的重複對象。若是程序常常請求建立相同的對象,而且建立對象代價很高,使用靜態工廠方法能夠提升性能。這麼作能夠確保類是單例或不可實例化的。使得不可變類不存在兩個相同的實例,a==b <——> a.equals(b)。

    3.能夠返回原返回類型的任何子類類型對象。

    4.簡化代碼。

劣勢:

    1.類不含有public或protected的構造函數,此類不能子類化。但這樣能夠更多地使用複合,即持有其餘類的對象,而不是繼承。

    2.與其餘靜態方法沒有任何區別。劣勢在於,靜態工廠方法並無在API中像構造函數同樣標明出來,使用可能不方便。彌補的方法是使用慣用名稱,例如:valueOf、getInstance、newInstance。

3.2 避免建立沒必要要對象-20161008

    通常來講,最好能重用對象而不是每次須要的時候建立一個功能相同的新對象,這個道理固然是很淺顯的。例如String、不可變對象。固然在編碼中主要對象的重用性以外,如下幾點也很重要:

    1. Calendar實例代價昂貴;

    2. 延遲初始化,是不推薦的。即執行構造函數時可能建立了一些暫時不會調用到的對象、常量,將這些對象和常量在具體方法調用時再初始化。不推薦的緣由是,使方法實現變得複雜,且沒法顯著提升性能。我以爲在工做中,尤爲是新員工會常常會遇到這種問題,那麼正確的方法如上所述;

    3. 自動裝箱會付出必定代價,要優先使用基本類型而不是裝箱基本類型;

    4.小對象建立和回收很是廉價,適當附加這種對象可提升程序可讀性;

    5.只有相似數據庫鏈接池這種大對象須要對象池,JVM垃圾回收器性能優於輕量對象池。

3.3 消除失效的對象引用-20161009

    失效的對象引用,例如實現一個相似棧的對象,這個對象本身管理內存(是出現內存泄漏的重要緣由),例如執行pop,但不將被pop位置的引用置爲null,則出現內存泄漏。

    1. Java中錯誤的操做會引發內存泄漏;

    2. 清空對象引用是一種例外,而不是規範,消除失效引用的最好方法是結束其生命週期;

    3. 若是類本身管理內存,應警戒內存泄漏問題;

    4. 使用緩存時,能夠將緩存對象做爲key存入WeakHashMap(真的沒見過,也腦補不出應該怎麼用,感受很雞肋),或將緩存對象存入LinkedHashMap,實現removeEldestEntry()做爲淘汰策略。

4、關於類和接口

4.1 儘量下降類的可訪問性-20161023

    儘量使每一個類或者類的成員不被外界訪問到,在設計類和成員時,關注這個類的可見性範圍,正確使用訪問級別修飾符。private及包級私有(沒有修飾符)都是一個類的實現中的一部分,通常不會影響他的API。當同一個包中的另外一個類真正須要訪問一個成員時,纔將private升級成爲包級私有。

    實例中不能包含公共域(或字段、屬性)。即公有類中不能包含公有域。包中含有公共可變域是線程不安全的。類中有共有靜態final域是錯誤的,客戶端能夠直接修改這種域的值。若是類是包級私有或私有的嵌套類,直接暴露域是沒問題的。

    其實看過Effective Java這一條才發現以前寫的大部分代碼都是不規範的,但沒出問題的緣由一是由於本身對本身的系統熟悉,不會亂寫,二是缺對乏類、成員可見性的意識。

4.2 不可變類-20161023

    不可變類是實例不能被修改的。jdk中String、基本類型的包裝類、BigInteger、BigDecimal是不可變的。不可變類遵循如下五條規則:

  • 不提供任何修改對象狀態的方法;
  • 保證不會被擴展,通常作法是使類成爲final的,另外一種方法是不提供顯式的構造函數,而是提供靜態工廠方法構建實例,被繼承必需顯式的構造函數;
  • 全部域都是final的;
  • 全部域都是private的;
  • 確保對任何可變組件的互斥訪問。若是類中具備可變對象的引用,確保客戶端不煩得到此對象引用,也不能使用客戶端提供的引用初始化改可變對象引用,也不要在任何方法中返回此對象引用。在構造器和readObject方法中使用保護性拷貝。

    不可變對象是線程安全的,不須要同步。所以能夠被自由共享。除非有好的理由讓類成爲可變的,不然就應該是不可變的。若是類不能被作成不可變的,也應該儘量限制他的可變性

4.3 複合優於繼承-20161023

    專門設計用來繼承的類,並有好的文檔,在使用繼承時很是安全,對於普通類進行跨包繼承是很是危險的。繼承打破了封裝性,即超類隨着版本變化會破壞子類,所以子類必須跟着超類一塊兒變化。這也體現了專門用於繼承類的優點。

    書中介紹的一個例子是擴展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,因此也稱爲包裝類。

5、批量替換文件中字符串20161014

    今天作了一次項目文件拷貝,全部代碼的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 後面的第一個參數便可

6、從protected到super.clone()-20161016

    忽然發覺對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的本質。

7、Spring mvc發送和接受複雜對象數組

    直接上代碼了:

前端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;
}
相關文章
相關標籤/搜索