標籤:size vat 不同 factory 類型 dal public 耦合 exce
編寫高品質代碼:改善Java程式的151個建議 --[78~92]HashMap中的hashCode應避免衝突多線程使用Vector或HashTable
Vector是ArrayList的多線程版本,HashTable是HashMap的多線程版本。
非穩定排序推薦使用List
對於變動的集合排序
- set=new TreeSet
- 使用TreeSet是希望實現自動排序,即使修改也能自動排序,既然它無法實現,那就用List來代替,然後使用Collections.sort()方法對List排序
import java.util.ArrayList;import java.util.SortedSet;import java.util.TreeSet;public class Client69 { public static void main(String[] args) { SortedSet<Person> set = new TreeSet<Person>(); // 身高180CM set.add(new Person(180)); // 身高175CM set.add(new Person(175)); set.first().setHeight(185); set=new TreeSet<Person>(new ArrayList<Person>(set)); for (Person p : set) { System.out.println("身高:" + p.getHeight()); } } static class Person implements Comparable<Person> { // 身高 private int height; public Person(int _height) { height = _height; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } // 按照身高排序 @Override public int compareTo(Person o) { return height - o.height; } }}
在項目中推薦使用枚舉常量代替介面常量或類常量
enum Season { Spring, Summer, Autumn, Winter; public static Season getComfortableSeason(){ return Spring; } }
使用建構函式協助描述枚舉項
enum Role { Admin("管理員", new LifeTime(), new Scope()), User("普通使用者", new LifeTime(), new Scope()); private String name; private LifeTime lifeTime; private Scope scope; /* setter和getter方法略 */ Role(String _name, LifeTime _lifeTime, Scope _scope) { name = _name; lifeTime = _lifeTime; scope = _scope; }}class LifeTime {}class Scope {}
name:表示的是該角色的中文名稱
lifeTime:表示的是該角色的生命週期,也就是多長時間該角色失效
scope:表示的該角色的許可權範圍
小心switch帶來的null 指標異常在switch的default代碼塊中增加AssertionError錯誤使用valueOf前必須進行校正
枚舉.valueOf(name)
沒有匹配找到指定值報錯:
Exception in thread "main" java.lang.IllegalArgumentException: No enum ... at java.lang.Enum.valueOf(Unknown Source)
兩種避免的方式:
(1)、使用try......catch捕捉異常
try{ 枚舉.valueOf(name) }catch(Exception e){ e.printStackTrace(); System.out.println("無相關枚舉項"); }
(2)、擴充枚舉類
enum Season { Spring, Summer, Autumn, Winter; // 是否包含指定的枚舉項 public static boolean isContains(String name) { // 所有的枚舉值 Season[] season = values(); for (Season s : season) { if (s.name().equals(name)) { return true; } } return false; } }
用枚舉實現Factory 方法模式更簡潔
枚舉非靜態方法實現Factory 方法模式
enum CarFactory { // 定義生產類能生產汽車的類型 FordCar, BuickCar; // 生產汽車 public Car create() { switch (this) { case FordCar: return new FordCar(); case BuickCar: return new BuickCar(); default: throw new AssertionError("無效參數"); } }}
通過抽象方法產生產品
enum CarFactory { // 定義生產類能生產汽車的類型 FordCar{ public Car create(){ return new FordCar(); } }, BuickCar{ public Car create(){ return new BuickCar(); } }; //抽象生產方法 public abstract Car create();}
使用枚舉類型的Factory 方法模式三個優點:
- 避免錯誤調用的發生:一般Factory 方法模式中的生產方法(也就是createCar方法),可以接收三種類型的參數:型別參數(如我們的例子)、String參數(生產方法中判斷String參數是需要生產什麼產品)、int參數(根據int值判斷需要生產什麼類型的的產品),這三種參數都是寬泛的資料類型,很容易發生錯誤(比如邊界問題、null值問題),而且出現這類錯誤編譯器還不會警示。
- 效能好,使用簡潔:枚舉類型的計算時以int類型的計算為基礎的,這是最基本的操作,效能當然會快,至於使用便捷,注意看用戶端的調用。
- 降低類間耦合:不管生產方法接收的是Class、String還是int的參數,都會成為用戶端類的負擔,這些類並不是用戶端需要的,而是因為Factory 方法的限制必須輸入的,例如Class參數,對用戶端main方法來說,他需要傳遞一個FordCar.class參數才能生產一輛福特汽車,除了在create方法中傳遞參數外,業務類不需要改Car的實作類別。這嚴重違背了迪米特原則(Law of Demeter 簡稱LoD),也就是最少知識原則:一個對象應該對其它對象有最少的瞭解。
而枚舉類型的Factory 方法就沒有這種問題了,它只需要依賴工廠類。
枚舉項的數量限制在64個以內
為了更好地使用枚舉,Java提供了兩個枚舉集合:EnumSet和EnumMap,這兩個集合使用的方法都比較簡單,EnumSet表示其元素必須是某一枚舉的枚舉項,EnumMap表示Key值必須是某一枚舉的枚舉項,由於枚舉類型的執行個體數量固定並且有限,相對來說EnumSet和EnumMap的效率會比其它Set和Map要高。
當枚舉項數量小於等於64時,建立一個RegularEnumSet執行個體對象,大於64時則建立一個JumboEnumSet執行個體對象。
枚舉項數量不要超過64,否則建議拆分。
import java.util.EnumSet;public class EnumSetTest { //普通枚舉項,數量等於64 enum Const{ A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z, AA,BB,CC,DD,EE,FF,GG,HH,II,JJ,KK,LL,MM,NN,OO,PP,QQ,RR,SS,TT,UU,VV,WW,XX,YY,ZZ, AAA,BBB,CCC,DDD,EEE,FFF,GGG,HHH,III,JJJ,KKK,LLL } //大枚舉,數量超過64 enum LargeConst{ A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z, AA,BB,CC,DD,EE,FF,GG,HH,II,JJ,KK,LL,MM,NN,OO,PP,QQ,RR,SS,TT,UU,VV,WW,XX,YY,ZZ, AAAA,BBBB,CCCC,DDDD,EEEE,FFFF,GGGG,HHHH,IIII,JJJJ,KKKK,LLLL,MMMM } public static void main(String[] args) { EnumSet<Const> cs = EnumSet.allOf(Const.class); EnumSet<LargeConst> lcs = EnumSet.allOf(LargeConst.class); //列印出枚舉數量 System.out.println("Const的枚舉數量:"+cs.size()); System.out.println("LargeConst的枚舉數量:"+lcs.size()); //輸出兩個EnumSet的class System.out.println(cs.getClass()); System.out.println(lcs.getClass()); }}
allOf調用noneOf
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) { //產生一個空EnumSet EnumSet<E> result = noneOf(elementType); //加入所有的枚舉項 result.addAll(); return result; }
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { //獲得所有的枚舉項 Enum[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); //枚舉數量小於等於64 if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else //枚舉數量大於64 return new JumboEnumSet<>(elementType, universe); }
注意@Override不同版本的區別
interface Foo { public void doSomething();}class FooImpl implements Foo{ @Override public void doSomething() { }}
這段代碼在Java1.6版本上編譯沒問題,雖然doSomething方法只是實現了介面的定義,嚴格來說並不是覆寫,但@Override出現在這裡可減少代碼中出現的錯誤。
可如果在Java1.5版本上編譯此段代碼可能會出現錯誤:The method doSomeThing() of type FooImpl must override a superclass method。
Java1.5版本的@Override是嚴格遵守覆寫的定義:子類方法與父類方法必須具有相同的方法名、輸出參數、輸出參數(允許子類縮小)、存取權限(允許子類擴大),父類必須是一個類,不能是介面,否則不能算是覆寫。而這在Java1.6就開放了很多,實現介面的方法也可以加上@Override註解了,可以避免粗心大意導致方法名稱與介面不一致的情況發生。
Java1.6版本的程式移植到1.5版本環境中,就需要刪除實現介面方法上的@Override註解。
編寫高品質代碼:改善Java程式的151個建議 --[78~92]