做者:Greyhtml
原文地址:java
Githubnode
語雀git
博客園github
UML圖算法
代碼數據庫
類加載的時候就會初始化這個實例, JVM保證惟一實例,線程安全, 可是能夠經過反射破壞編程
方式一設計模式
public class Singleton1 { private final static Singleton1 INSTANCE = new Singleton1(); private Singleton1() { } public static Singleton1 getInstance() { return INSTANCE; } }
方式二安全
public class Singleton2 { private static final Singleton2 INSTANCE; static { INSTANCE = new Singleton2(); } public static Singleton2 getInstance() { return INSTANCE; } }
雖然能夠實現按需初始化,可是線程不安全, 由於在判斷INSTANCE == null的時候,若是是多個線程操做的話, 一個線程尚未把INSTANCE初始化好,另一個線程判斷INSTANCE==null 獲得true,就會繼續初始化
public class Singleton3 { private static Singleton3 INSTANCE; private Singleton3() { } public static Singleton3 getInstance() { if (INSTANCE == null) { // 模擬初始化對象須要的耗時操做 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Singleton3(); } return INSTANCE; } }
爲了防止線程不安全,能夠在getInstance方法上加鎖,這樣既實現了按需初始化,又保證了線程安全,可是加鎖可能會致使一些性能的問題
public class Singleton4 { private static Singleton4 INSTANCE; private Singleton4() { } public static synchronized Singleton4 getInstance() { if (INSTANCE == null) { // 模擬初始化對象須要的耗時操做 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Singleton4(); } return INSTANCE; } }
爲了提高一點點性能,能夠不給getInstance整個方法加鎖,而是對INSTANCE判空這段代碼加鎖, 可是又帶來了線程不安全的問題
public class Singleton5 { private static Singleton5 INSTANCE; private Singleton5() { } public static Singleton5 getInstance() { if (INSTANCE == null) { synchronized (Singleton5.class) { // 模擬初始化對象須要的耗時操做 try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Singleton5(); } } return INSTANCE; } }
Double Check Locking模式,就是雙加鎖檢查模式
這種方式中,Volatile是必需的,目的爲了防止指令重排,生成一個半初始化的的實例,致使生成兩個實例
具體可參考 雙重檢索(DCL)的思考: 爲何要加volatile?
說了這個問題
public class Singleton6 { private volatile static Singleton6 INSTANCE; private Singleton6() { } public static Singleton6 getInstance() { if (INSTANCE == null) { synchronized (Singleton6.class) { if (INSTANCE == null) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } INSTANCE = new Singleton6(); } } } return INSTANCE; } }
如下兩種更爲優雅的方式,既保證了線程安全,又實現了按需加載
方式一:靜態內部類方式,JVM保證單例,加載外部類時不會加載內部類,這樣能夠實現懶加載
public class Singleton7 { private Singleton7() { } public static Singleton7 getInstance() { return Holder.INSTANCE; } private static class Holder { private static final Singleton7 INSTANCE = new Singleton7(); } }
方式二: 使用枚舉, 這是實現單例模式的最佳方法。它更簡潔,自動支持序列化機制,絕對防止屢次實例化,這種方式是 Effective Java 做者 Josh Bloch
提倡的方式,它不只能避免多線程同步問題,並且還自動支持序列化機制,防止反序列化從新建立新的對象,絕對防止屢次實例化。
public enum Singleton8 { INSTANCE; }
實例: 假設咱們有一個貓類,這個類裏面有體重和身高這兩個屬性,給你一個貓的集合,而後須要你按貓的體重從小到大排序
思路: 咱們能夠把體重從小到大這個當作是一個策略,後續可能衍生其餘的策略,好比: 按身高從高到低 按體重從小到大,體重同樣的身高從高到低
以身高從低到高排序這個策略爲例
public class CatSortStrategy implements Comparator<Cat> { @Override public int compare(Cat o1, Cat o2) { return o1.getHeight() - o2.getHeight(); } }
假設咱們定義貓排序的方法是: sort 那麼這個方法必然須要傳入一個排序策略的參數(不然我怎麼知道要怎麼排序貓?) 因此定義的sort方法能夠是:
public class Sorter { public Cat[] sort(Cat[] items, Comparator<Cat> strategy) { int length = items.length; for (int i = 0; i < length; i++) { for (int j = i + 1; j < length; j++) { if (strategy.compare(items[i], items[j]) > 0) { Cat tmp = items[i]; items[i] = items[j]; items[j] = tmp; } } } return items; } }
進一步抽象,若是我想讓Sorter這個工具類不只能夠對貓進行各類策略的排序(基於比較的排序算法),還能夠對狗進行各類策略的排序(基於比較排序算法),能夠將Sorter定義成泛型
public class Sorter<T> { public T[] sort(T[] items, Comparator<T> strategy) { int length = items.length; for (int i = 0; i < length; i++) { for (int j = i + 1; j < length; j++) { if (strategy.compare(items[i], items[j]) > 0) { T tmp = items[i]; items[i] = items[j]; items[j] = tmp; } } } return items; } }
調用的時候, 泛型版本的Sorter能夠對貓和狗都進行基於特定排序策略的排序。
Sorter<Cat> sorter=new Sorter<>(); Cat[]sortedCats=sorter.sort(cats,new CatSortStrategy()); Sorter<Dog> sorter=new Sorter<>(); Dog[]sortedCats=sorter.sort(dogs,new DogSortStrategy());
[TODO] 能夠有更好的例子
假設咱們有一個需求,是在畫板上畫圓形和矩形,「暴力」寫法就是:
public static void main(String[]args){ Circle circle=new Sharp(); Rectangle rectangle=new Sharp(); circle.draw(); rectangle.draw(); }
若是後續要改變畫圓和畫矩形邏輯,咱們就須要動到這個主方法, 用門面模式的方式,咱們能夠經過一個SharpMarker來完成畫圓形和矩形的邏輯(看成外部調用的門面),那麼在改變畫圓和畫矩形邏輯的時候,就不須要改動主方法了
public class SharpMarker { private Sharp circle; private Sharp rectangle; public SharpMarker(Sharp circle, Sharp rectangle) { this.circle = circle; this.rectangle = rectangle; } public void draw() { circle.draw(); rectangle.draw(); } }
主方法只須要:
public class Main { public static void main(String[] args) { SharpMarker marker = new SharpMarker(new Rectangle(), new Circle()); marker.draw(); } }
門面模式的UML圖以下
舉個簡單的例子,若是一個聊天室裏面的用戶1和用戶2要聊天,聊天室就至關於中介的地位,用戶1和用戶2只管調用發消息方法,聊天室便可把消息給對方
public class ChatRoom { public static void showMessage(User user, String content) { System.out.println("user :" + user.getName() + " send a message, content is " + content); } }
以上代碼表示,聊天室將user說的content展現出來
主方法只須要以下調用便可:
public class Main { public static void main(String[] args) { User user = new User("Peter"); user.sendMessage("Hello "); user = new User("Harry"); user.sendMessage("Hi"); } }
User中的sendMessage方法
public void sendMessage(String content){ ChatRoom.showMessage(this,content); }
有一段文本須要過濾敏感字,咱們能夠經過責任鏈模式來設計這個功能,假設文本是:scripts Hell World! 996
咱們有多個過濾規則,好比第一個規則是:過濾 scripts 這個關鍵字(實際的規則可能很複雜,目前只是舉這個簡單例子來講明狀況)
第二個規則是:過濾 996 這個關鍵字
咱們能夠抽象一個Filter接口,各類過濾規則無非就是實現這個接口便可
public interface Filter { boolean doFilter(Msg msg); }
過濾 996 的規則:
public class SensitiveFilter implements Filter { @Override public boolean doFilter(Msg msg) { msg.setContent(msg.getContent().replace("996", "")); return true; } }
過濾 scripts 的規則:
public class HTMLFilter implements Filter { @Override public boolean doFilter(Msg msg) { msg.setContent(msg.getContent().replace("scripts", "")); return true; } }
主方法調用的時候,就直接New 相應的Filter來處理便可:
Msg msg=new Msg(); msg.setContent("scripts Hell World! 996"); System.out.println("before filter , the content is : "+msg.getContent()); Filter html=new HTMLFilter(); Filter sensitive=new SensitiveFilter(); html.doFilter(msg); sensitive.doFilter(msg); System.out.println("after filter , the content is : "+msg.getContent());
不過,更爲優雅的一種方式是設計一個FilterChain,咱們把全部的Filter都加入到這個FilterChain裏面,對於Msg直接去調用FilterChain的過濾方法便可把FilterChain中的全部Filter都執行(
並且還能夠很靈活指定Filter順序)
public class FilterChain implements Filter { // 這裏存全部須要應用的Filter private List<Filter> filters = new ArrayList<>(); public FilterChain addFilter(Filter filter) { filters.add(filter); return this; } @Override public boolean doFilter(Msg msg) { // 這裏能夠靈活指定Filter的執行順序 for (Filter filter : filters) { if (!filter.doFilter(msg)) { return false; } } return true; } }
那麼主方法在調用的時候,能夠直接經過以下的方式:
public class Main { public static void main(String[] args) { FilterChain filterChain = new FilterChain(); filterChain.addFilter(new HTMLFilter()).addFilter(new SensitiveFilter()); Msg msg = new Msg(); msg.setContent("scripts Hell World! 996"); System.out.println("before filter , the content is : " + msg.getContent()); filterChain.doFilter(msg); System.out.println("after filter , the content is : " + msg.getContent()); } }
應用
顧名思義,就是對某個方法或者對象進行裝飾,舉個簡單的例子,有個圓形類(Circle),我須要把這個圓形的塗上紅色,其實就是新增一個裝飾器來裝飾這個圓形類。
若是要讓裝飾器通用一些,能夠處理圓形類對應的抽象類 Sharpe,那麼對於任意Shape的子類,均可以用紅色裝飾器來塗紅色。
咱們先定義Sharp這個抽象類:
public abstract class Sharp { protected abstract void draw(); }
而後咱們定義Sharp的裝飾類:SharpDecorator,這個類是全部裝飾器類的抽象類,後續的裝飾器只須要實現這個抽象類就能夠對Sharp進行各類裝飾了,
public abstract class SharpDecorator extends Sharp { protected Sharp decoratedSharp; public SharpDecorator(Sharp decoratedSharp) { this.decoratedSharp = decoratedSharp; } }
紅色裝飾器實現這個抽象類便可:
public class RedSharpDecorator extends SharpDecorator { public RedSharpDecorator(Sharp decoratedSharp) { super(decoratedSharp); } private static void redIt() { System.out.println("[RED]"); } @Override protected void draw() { redIt(); this.decoratedSharp.draw(); redIt(); } }
主方法調用的時候只須要:
new RedSharpDecorator(new Circle()).draw();
UML圖以下:
裝飾器模式的應用
事件處理 每每和責任鏈模式搭配使用
Spring ApplicationEvent
組合模式中,最經常使用的一個用法就是目錄層級的遍歷,話很少說,直接上代碼,主方法中
BranchNode root=new BranchNode("root"); BranchNode branch1=new BranchNode("branch1"); BranchNode branch2=new BranchNode("branch2"); branch1.addNode(new LeafNode("leaf1")); root.addNode(branch1); root.addNode(branch2); tree(root,0);
其中,BranchNode爲分支節點,LeafNode是葉子節點 達到的效果就是打印以下的形式
root --branch1 ----leaf1 --branch2
其中BranchNode和LeafNode都實現了Node接口,Node接口(也能夠爲定義抽象類)僅提供了一個屬性(content:標識節點內容)和一個打印方法:
public abstract class Node { protected String content; protected abstract void print(); }
BranchNode下能夠包含多個Node,由於一個分支下面能夠有多個分支(這個分支能夠是任意的Node子類)
public class BranchNode extends Node { private List<Node> nodes = new ArrayList<>(); public BranchNode(String content) { this.content = content; } @Override public void print() { System.out.println(content); } // get..set方法略 }
組合模式的UML圖以下:
String 鏈接池管理
靜態代理
動態代理
Spring AOP
在結構不變的狀況下動態改變對於內部元素的動做 作編譯器的時候,生成AST的時候,進行類型檢查 根據抽象語法樹,生成中間代碼
XML文件解析
咱們在對一個實體類進行屬性的get/set的時候,能夠經過封裝一些經常使用的構造方法來簡化實體類的構造
好比:
public class Person { private String name; private int age; private String address; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } private Person() { } public static class PersonBuilder { private Person person = new Person(); public PersonBuilder basicInfo(String name, int age) { person.name = name; person.age = age; return this; } public PersonBuilder name(String name) { person.name = name; return this; } public PersonBuilder age(int age) { person.age = age; return this; } public PersonBuilder address(String address) { person.address = address; return this; } public Person build() { return person; } } public String getName() { return name; } public int getAge() { return age; } public String getAddress() { return address; } }
其中PersonBuilder就是一個內部類,用於構造Person的必要信息,外部調用Person的構造方法時候,能夠這樣使用:
Person person=new Person.PersonBuilder().basicInfo("zhangsan",10).address("xxx").build();
還有一種關於set構造器的編寫方式是每次返回this, 這樣能夠實現「鏈式構造」
public class Person { private String name; private int age; // 省略get方法 public Person name(String name) { this.name = name; return this; } public Person age(int age) { this.age = age; return this; } }
主方法在調用的時候能夠直接:
Person p=new Person(); p.age(10).name("zhangsan");
實際應用有很是多,不少組件都提供這樣的構造方式,好比OkHttpClient的構造方法:
public static OkHttpClient create(long connectTimeOut) { return new OkHttpClient().newBuilder() .connectionSpecs(Arrays.asList( ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS, ConnectionSpec.CLEARTEXT)) .connectTimeout(connectTimeOut, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .connectionPool(CONNECTION_POOL) .retryOnConnectionFailure(true) .followRedirects(true) .followSslRedirects(true) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }) .cookieJar(new CookieJar() { private List<Cookie> cookies; @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { this.cookies = cookies; } @Override public List<Cookie> loadForRequest(HttpUrl url) { if (cookies != null) { return cookies; } return Collections.emptyList(); } }) .build(); }
java.io jdbc-odbc bridge ASM transformer
抽象和具體的發展單獨分支,抽象中持有一個具體的引用 使用橋接模式: 分離抽象與具體實現,讓他們能夠獨自發展 Gift -> WarmGift ColdGift WildGiftGiftImpl -> Flower Ring Car
結合責任鏈模式實現屢次undo 結合組合模式實現宏命令 結合記憶模式實現transaction回滾
Object.clone()
記錄狀態,記錄快照,瞬時狀態,存盤 Tank的GameModel的load/save方法(實現序列化接口) 便於回滾
假設咱們要實現一個遊戲,這個遊戲有初始化,啓動,結束三個方法,咱們能夠定義一個遊戲的模板:
public abstract class Game { protected abstract void init(); protected abstract void start(); protected abstract void end(); protected final void play() { init(); start(); end(); } }
每種相似這樣結構(有初始化,啓動,結束)的遊戲均可以繼承這個類來實現這三個方法,好比BasketballGame
public class BasketballGame extends Game { @Override protected void init() { System.out.println("basketball init"); } @Override protected void start() { System.out.println("basketball start"); } @Override protected void end() { System.out.println("basketball end"); } }
FootballGame
public class FootballGame extends Game { @Override protected void init() { System.out.println("football init"); } @Override protected void start() { System.out.println("football start"); } @Override protected void end() { System.out.println("football end"); } }
主方法在調用的時候,直接:
Game basketballGame = new BasketballGame(); basketballGame.play(); Game footballGame = new FootballGame(); footballGame.play();
便可
模板方法的UML圖
實際應用場景
鉤子函數
RestTemplate /JDBCTemplate
狀態遷移