確保一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。java
單例模式有如下幾個要素:算法
單例模式根據實例化對象時機的不一樣分爲兩種:一種是餓漢式單例,一種是懶漢式單例。餓漢式單例在單例類被加載時候,就實例化一個對象交給本身的引用;而懶漢式在調用取得實例方法的時候纔會實例化對象。spring
餓漢式:數據庫
public class Singleton_Simple { private static final Singleton_Simple simple = new Singleton_Simple(); private Singleton_Simple(){} public static Singleton_Simple getInstance(){ return simple; } }
懶漢式:編程
//雙鎖機制 class Singleton { private volatile static Singleton singleton; public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { // 因爲每次調用都須要進行同步,因此能夠在前面進行判斷便可提升效率,同時注意使用的是Singleton.class的類鎖 if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
爲了提升效率。咱們能夠用double check機制,如今來看兩個問題:設計模式
(1)爲什麼用double check的檢測機制?緩存
因爲兩個線程均可以經過第一重的判斷 ,進入後,因爲存在鎖機制,因此會有一個線程進入同步塊和第二重判斷,而另外的一個線程在同步塊處阻塞。安全
當第一個線程退出同步代碼塊後,第二個進入,沒有通過第二重判斷,保證了單例。因此這裏必需要使用雙重檢查鎖定。多線程
其實沒有第一重的判斷,咱們也能夠實現單例,只是爲了考慮性能問題。併發
(2)爲什麼要對實例變量加volatile關鍵字?
1.爲對象分配內存
2.初始化實例對象
3.把引用instance指向分配的內存空間
ps:對象建立可參看《深刻理解Java虛擬機》第44頁
這個三個步驟並不能保證按序執行,處理器會進行指令重排序優化,存在這樣的狀況:
優化重排後執行順序爲:1,3,2, 這樣在線程1執行到3時,instance已經不爲null了,線程2此時判斷instance!=null,則直接返回instance引用,但如今實例對象尚未初始化完畢,此時線程2使用instance可能會形成程序崩潰。
如今要解決的問題就是怎樣限制處理器進行指令優化重排。
volatile的做用:
(1)volatile變量不會以被緩存到寄存器或者對其它處理器不可見的地方,所以在讀取volatile類型的變量時老是返回最新寫入的值。
(2)volatile關鍵字可以經過提供內存屏障,來保證某些指令順序處理器不可以優化重排,編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。
JVM會對分配內存空間的動做進行同步處理。
(1)可能實際上採用CAS(Compare And Set)配上失敗重試的方式保證更新操做的原子性
(2)把內存分配的動做按照線程劃分在不一樣的空間之中進行。就是每一個線程在Java堆中預先分配一小塊內存,
參考Java虛擬機第45頁
其實還有種寫法,以下:
public class Singleton { private static class SingletonClassInstance { // 私有靜態內部類 // 多是final解決了併發的問題,基本類型聲明就可,可是對象類型時,這個對象不能有狀態, // 若是有狀態,則必定要聲明爲final,例如String類就是一個不可變類 private static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonClassInstance.instance; // 只在這裏調用了靜態內部類,私有屬性讓他人沒法使用這個類型 } private Singleton() { } }
因爲Singletom沒有static屬性且SingletonClassInstance是私有靜態內部類,不會被其餘類調用,因此不會被提早初始化,實現了懶漢式的構造實例。
重點要知道爲何靜態內部類能保證單例:由於只有一個線程去初始化這個類,在初始化後static final就是一個常量了,固然線程安全了。
其在實際中有重要的應用,如:
提供一種方法訪問一個容器對象中各個元素,而又不暴露該對象的內部細節
interface Iterable{ public Iterator iterator(); } interface Aggregate extends Iterable{ public void add(Object obj); public void remove(Object obj); public Iterator iterator(); } class ConcreteAggregate implements Aggregate { private List list = new ArrayList(); public void add(Object obj) { list.add(obj); } public Iterator iterator() { return new ConcreteIterator(list); } public void remove(Object obj) { list.remove(obj); } }
調用iterator()方法後返回一個Iterator迭代器,實現以下:
interface Iterator { public Object next(); public boolean hasNext(); } class ConcreteIterator implements Iterator { private List list = new ArrayList(); private int cursor = 0; public ConcreteIterator(List list) { this.list = list; } public boolean hasNext() { if (cursor == list.size()) { return false; } return true; } public Object next() { Object obj = null; if (this.hasNext()) { obj = this.list.get(cursor++); } return obj; } }
測試一下:
Aggregate ag = new ConcreteAggregate(); ag.add("小明"); ag.add("小紅"); ag.add("小剛"); Iterator it = ag.iterator(); while (it.hasNext()) { String str = (String) it.next(); System.out.println(str); }
蜜蜂是昆蟲:繼承實現
蜜蜂有向前移動後攻擊人的行爲:組合實現
昆蟲:
class Insect { private String name; public Insect(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
攻擊行爲:
interface Attack { public void move(); public void attack(); } class AttackImpl implements Attack { private String move; private String attack; public Attack Impl(String move, String attack) { this.move = move; this.attack = attack; } @Override public void move() { System.out.println(move); } @Override public void attack() { move(); System.out.println(attack); } }
蜜蜂是昆蟲,有向前一步攻擊人的屬性:
class Bee extends Insect implements Attack { private Attack attack; public Bee(String name, Attack attack) { super(name); this.attack = attack; } public void move() { attack.move(); } public void attack() { attack.attack(); } }
因爲繼承機制太過依賴於父類的實現細節,若是父類變更,則子類也會跟着變更,這是糟糕的。
// Different types of function objects: interface Combiner<T> { T combine(T x, T y); } public class Functional { // Calls the Combiner object on each element to combine // it with a running result, which is finally returned: public static <T> T reduce(Iterable<T> seq, Combiner<T> combiner) { Iterator<T> it = seq.iterator(); if (it.hasNext()) { T result = it.next(); while (it.hasNext()){ result = combiner.combine(result, it.next()); } return result; } // If seq is the empty list: return null; // Or throw exception } // To use the above generic methods, we need to create // function objects to adapt to our particular needs: static class IntegerAdder implements Combiner<Integer> { public Integer combine(Integer x, Integer y) { return x + y; } } static class IntegerSubtracter implements Combiner<Integer> { public Integer combine(Integer x, Integer y) { return x - y; } } static class BigIntegerAdder implements Combiner<BigInteger> { public BigInteger combine(BigInteger x, BigInteger y) { return x.add(y); } } public static void main(String[] args) { // Generics, varargs & boxing working together: List<Integer> li = Arrays.asList(1, 2, 3, 4, 5, 6, 7); Integer result = reduce(li, new IntegerAdder()); print(result); // 28 result = reduce(li, new IntegerSubtracter()); print(result); // Use the prime-generation facility of BigInteger: List<BigInteger> lbi = new ArrayList<BigInteger>(); BigInteger bi = BigInteger.valueOf(11); for (int i = 0; i < 11; i++) { lbi.add(bi); bi = bi.nextProbablePrime(); } print(lbi); BigInteger rbi = reduce(lbi, new BigIntegerAdder()); print(rbi); } }
如上例子參考了Java編程思想關於泛型實現策略模式的例子。
下面來總結一下各個設計模式在Java中的具體應用。
結構型模式 |
|
單例模式(Single Pattern) |
一、Log4j中將日誌輸出到一個文件中必須使用單例模式,不然會覆蓋文件的原有內容 二、數據庫鏈接池中的應用 |
工廠模式(Factory) |
Spring工廠模式:經過XML文件或Java註解來表示Bean之間的依賴關係,不多直接new一個類進行代碼編寫 |
建造者模式(Builder) |
Struts2中建立容器(Container)對象 |
原型模式(Protype) |
Java的克隆clone() |
結構型模式 |
|
適配器模式(Adapter) |
一、在Java的I/O類庫中,StringReader將一個String類適配到Reader接口,InputStreamReader將InputStream適配到Reader類。 二、在Spring中的AOP中,因爲Advisor須要的是MethodInterceptor對象,因此每個Advisor中的Advice都要配成對應的MethodInterceptor對象。 |
橋接模式(Bridge) |
|
裝飾模式(Decorator) |
一、Collections.synchronizedList例子也是一個裝飾器模式 二、裝飾器在Junit中的應用。TestDecorator是Test的裝飾類,其TestDecorator有一些擴展功能的子類。如RepeatedTest類,TestSetup類等 三、通常狀況下,須要使用FileReader和StringReader,若是要增長緩存功能的類有不少,那麼子類也就須要不少,因此Java就使用了裝飾模式,BufferedReader就是這樣的裝飾類。其實Java I/O 庫中的全部輸入流、輸出流的類都採用了裝飾器模式,它們能夠無限次地進行裝飾轉換,轉換的目的就是獲得本身想要的數據類型的流對象 |
組合模式(Composite) |
|
外觀模式(Façade) |
一、在Spring中已經提供了很好的封裝,在org.springframework.jdbc.support包下提供的JdbcUtils類就是這樣的工具類 二、在Hibernate的配置工做中,有一個Configuration類,負責管理運行時須要的一些信息 |
享元模式(Flyweight) |
數據據鏈接池是享元模式的重要應用。在Java中,對於基本類型和字符串類型的實現就採用了享元模式 |
代理模式(Proxy) |
一、在Spring中已經提供了很好的封裝,在org.springframework.jdbc.support包下提供的JdbcUtils類就是這樣的工具類 二、在Hibernate的配置工做中,有一個Configuration類,負責管理運行時須要的一些信息 |
行爲型模式 |
|
模版方法模式(Template Method) |
一、在Junit中的TestCase類中 二、在MVC框架的HttpServlet中 三、Spring採用開放式的處理方式,在使用JpaTemplate時,只須要處理具體的SQL語句便可,而建立鏈接、關閉鏈接等方法都不須要編寫代碼,由於無論理是哪一種持久層的操做應運,其建立和關閉鏈接都是一致的,按照相同的順序執行,這就是模板方法模式的應用 |
命令模式(Command )
|
命令模式在Struts2中的應用。其中action就是命令模式中命令接口,ActionInvocation就是命令模式中命令的調用者 |
迭代器模式(Iterator ) |
java中的Collection,List、Set、Map等,這些集合都有本身的迭代器 |
觀察者模式(Oberver Pattern) |
|
中介者模式(Mediator) |
|
備忘錄模式(Memento) |
|
解釋器模式(Interpreter) |
|
狀態模式(State) |
|
職責鏈模式(Chain of Responsibility) |
責任鏈在Struts2中的攔截器上有重要應用。
|
策略模式(Strategy) |
一、策略模式容許在程序執行時選擇不一樣的算法.好比在排序時,傳入不一樣的比較器(Comparator),就採用不一樣的算法 二、Spring的Resource實現思想是典型的策略模式的應用 |
訪問者模式(Visitor) |