1.單例模式
1.1 概念
簡單點說,就是一個應(yīng)用程序中,某個類的實例對象只有一個,你沒有辦法去new,因為構(gòu)造器是被private修飾的,一般通過getInstance()的方法來獲取它們的實例。
getInstance()的返回值是一個對象的引用,并不是一個新的實例,所以不要錯誤的理解成多個對象。
步驟1.
創(chuàng)建一個單例對象:
public class SingleObject {
//創(chuàng)建 SingleObject 的一個對象
private static SingleObject instance = new SingleObject();
//讓構(gòu)造函數(shù)為 private,這樣該類就不會被實例化
private SingleObject(){}
//獲取唯一可用的對象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
步驟2.
從 singleton 類獲取唯一的對象。
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的構(gòu)造函數(shù)
//編譯時錯誤:構(gòu)造函數(shù) SingleObject() 是不可見的
//SingleObject object = new SingleObject();
//獲取唯一可用的對象
SingleObject object = SingleObject.getInstance();
//顯示消息
object.showMessage();
}
}

1.2 單例模式的幾種實現(xiàn)方式
1、懶漢式,線程不安全
描述:這種方式是最基本的實現(xiàn)方式,這種實現(xiàn)最大的問題就是不支持多線程。因為沒有加鎖 synchronized,所以嚴格意義上它并不算單例模式。
這種方式 lazy loading 很明顯,不要求線程安全,在多線程不能正常工作。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2、懶漢式,線程安全
優(yōu)點:第一次調(diào)用才初始化,避免內(nèi)存浪費。
缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。
getInstance() 的性能對應(yīng)用程序不是很關(guān)鍵(該方法使用不太頻繁)。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3、餓漢式
描述:這種方式比較常用,但容易產(chǎn)生垃圾對象。
優(yōu)點:沒有加鎖,執(zhí)行效率會提高。
缺點:類加載時就初始化,浪費內(nèi)存。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4、雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)
描述:這種方式采用雙鎖機制,安全且在多線程情況下能保持高性能。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5、登記式/靜態(tài)內(nèi)部類
描述:這種方式能達到雙檢鎖方式一樣的功效,但實現(xiàn)更簡單。對靜態(tài)域使用延遲初始化,應(yīng)使用這種方式而不是雙檢鎖方式。這種方式只適用于靜態(tài)域的情況,雙檢鎖方式可在實例域需要延遲初始化時使用。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6、枚舉
描述:這種實現(xiàn)方式還沒有被廣泛采用,但這是實現(xiàn)單例模式的最佳方法。它更簡潔,自動支持序列化機制,絕對防止多次實例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
2.觀察者模式
2.1 概念
對象間一對多的依賴關(guān)系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。

舉個栗子:假設(shè)有三個人,小美(女,22),小王和小李。小美很漂亮,小王和小李是兩個程序猿,時刻關(guān)注著小美的一舉一動。有一天,小美說了一句:“誰來陪我打游戲啊。”這句話被小王和小李聽到了,結(jié)果樂壞了,蹭蹭蹭,沒一會兒,小王就沖到小美家門口了,在這里,小美是被觀察者,小王和小李是觀察者,被觀察者發(fā)出一條信息,然后觀察者們進行相應(yīng)的處理,看代碼:
public interface Person {
//小王和小李通過這個接口可以接收到小美發(fā)過來的消息
void getMessage(String s);
}
這個接口相當于小王和小李的電話號碼,小美發(fā)送通知的時候就會撥打getMessage這個電話,撥打電話就是調(diào)用接口,看不懂沒關(guān)系,先往下看
public class LaoWang implements Person {
private String name = "小王";
public LaoWang() {
}
@Override
public void getMessage(String s) {
System.out.println(name + "接到了小美打過來的電話,電話內(nèi)容是:" + s);
}
}
public class LaoLi implements Person {
private String name = "小李";
public LaoLi() {
}
@Override
public void getMessage(String s) {
System.out.println(name + "接到了小美打過來的電話,電話內(nèi)容是:->" + s);
}
}
代碼很簡單,我們再看看小美的代碼:
public class XiaoMei {
List<Person> list = new ArrayList<Person>();
public XiaoMei(){
}
public void addPerson(Person person){
list.add(person);
}
//遍歷list,把自己的通知發(fā)送給所有暗戀自己的人
public void notifyPerson() {
for(Person person:list){
person.getMessage("你們過來吧,誰先過來誰就能陪我一起玩兒游戲!");
}
}
}
我們寫一個測試類來看一下結(jié)果對不對
public class Test {
public static void main(String[] args) {
XiaoMei xiao_mei = new XiaoMei();
LaoWang lao_wang = new LaoWang();
LaoLi lao_li = new LaoLi();
//小王和小李在小美那里都注冊了一下
xiao_mei.addPerson(lao_wang);
xiao_mei.addPerson(lao_li);
//小美向小王和小李發(fā)送通知
xiao_mei.notifyPerson();
}
}
3 裝飾者模式
對已有的業(yè)務(wù)邏輯進一步的封裝,使其增加額外的功能,如Java中的IO流就使用了裝飾者模式,用戶在使用的時候,可以任意組裝,達到自己想要的效果。 舉個栗子,我想吃三明治,首先我需要一根大大的香腸,我喜歡吃奶油,在香腸上面加一點奶油,再放一點蔬菜,最后再用兩片面包夾一下,很豐盛的一頓午飯,營養(yǎng)又健康。(ps:不知道上海哪里有賣好吃的三明治的,求推薦~)那我們應(yīng)該怎么來寫代碼呢? 首先,我們需要寫一個Food類,讓其他所有食物都來繼承這個類,看代碼:
public class Food {
private String food_name;
public Food() {
}
public Food(String food_name) {
this.food_name = food_name;
}
public String make() {
return food_name;
};
}
然后我們寫幾個子類繼承它:
//面包類
public class Bread extends Food {
private Food basic_food;
public Bread(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+面包";
}
}
//奶油類
public class Cream extends Food {
private Food basic_food;
public Cream(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+奶油";
}
}
//蔬菜類
public class Vegetable extends Food {
private Food basic_food;
public Vegetable(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+蔬菜";
}
}
Test類:
public class Test {
public static void main(String[] args) {
Food food = new Bread(new Vegetable(new Cream(new Food("香腸"))));
System.out.println(food.make());
}
}
4 適配器模式
將兩種完全不同的事物聯(lián)系到一起,就像現(xiàn)實生活中的變壓器。假設(shè)一個手機充電器需要的電壓是20V,但是正常的電壓是220V,這時候就需要一個變壓器,將220V的電壓轉(zhuǎn)換成20V的電壓,這樣,變壓器就將20V的電壓和手機聯(lián)系起來了。
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
VoltageAdapter adapter = new VoltageAdapter();
phone.setAdapter(adapter);
phone.charge();
}
}
// 手機類
class Phone {
public static final int V = 220;// 正常電壓220v,是一個常量
private VoltageAdapter adapter;
// 充電
public void charge() {
adapter.changeVoltage();
}
public void setAdapter(VoltageAdapter adapter) {
this.adapter = adapter;
}
}
// 變壓器
class VoltageAdapter {
// 改變電壓的功能
public void changeVoltage() {
System.out.println("正在充電...");
System.out.println("原始電壓:" + Phone.V + "V");
System.out.println("經(jīng)過變壓器轉(zhuǎn)換之后的電壓:" + (Phone.V - 200) + "V");
}
}
5 工廠模式
5.1 概念
工廠模式(Factory Pattern)是 Java 中最常用的設(shè)計模式之一。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
在工廠模式中,我們在創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯,并且是通過使用一個共同的接口來指向新創(chuàng)建的對象。
5.2 介紹
意圖:定義一個創(chuàng)建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創(chuàng)建過程延遲到子類進行。
主要解決:主要解決接口選擇的問題。
何時使用:我們明確地計劃不同條件下創(chuàng)建不同實例時。
如何解決:讓其子類實現(xiàn)工廠接口,返回的也是一個抽象的產(chǎn)品。
關(guān)鍵代碼:創(chuàng)建過程在其子類執(zhí)行。
應(yīng)用實例: 1、您需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個汽車里面的具體實現(xiàn)。 2、Hibernate 換數(shù)據(jù)庫只需換方言和驅(qū)動就可以。
優(yōu)點: 1、一個調(diào)用者想創(chuàng)建一個對象,只要知道其名稱就可以了。 2、擴展性高,如果想增加一個產(chǎn)品,只要擴展一個工廠類就可以。 3、屏蔽產(chǎn)品的具體實現(xiàn),調(diào)用者只關(guān)心產(chǎn)品的接口。
缺點:每次增加一個產(chǎn)品時,都需要增加一個具體類和對象實現(xiàn)工廠,使得系統(tǒng)中類的個數(shù)成倍增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,同時也增加了系統(tǒng)具體類的依賴。這并不是什么好事。
使用場景: 1、日志記錄器:記錄可能記錄到本地硬盤、系統(tǒng)事件、遠程服務(wù)器等,用戶可以選擇記錄日志到什么地方。 2、數(shù)據(jù)庫訪問,當用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫,以及數(shù)據(jù)庫可能有變化時。 3、設(shè)計一個連接服務(wù)器的框架,需要三個協(xié)議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產(chǎn)品類,共同實現(xiàn)一個接口。

5.3 代碼
步驟 1
創(chuàng)建一個接口:
public interface Shape {
void draw();
}
步驟 2
創(chuàng)建實現(xiàn)接口的實體類。
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步驟 3
創(chuàng)一個工廠,生成基于給定信息的實體類的對象。
public class ShapeFactory {
//使用 getShape 方法獲取形狀類型的對象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
步驟 4
使用該工廠,通過傳遞類型信息來獲取實體類的對象。
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//獲取 Circle 的對象,并調(diào)用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//調(diào)用 Circle 的 draw 方法
shape1.draw();
//獲取 Rectangle 的對象,并調(diào)用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//調(diào)用 Rectangle 的 draw 方法
shape2.draw();
//獲取 Square 的對象,并調(diào)用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//調(diào)用 Square 的 draw 方法
shape3.draw();
}
}
6 代理模式(proxy)
http://www.itdecent.cn/p/9bcac608c714
7 抽象工廠模式
7.1 概念
抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創(chuàng)建其他工廠。該超級工廠又稱為其他工廠的工廠。這種類型的設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。
在抽象工廠模式中,接口是負責(zé)創(chuàng)建一個相關(guān)對象的工廠,不需要顯式指定它們的類。每個生成的工廠都能按照工廠模式提供對象。
7.2 介紹
意圖:提供一個創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無需指定它們具體的類。
主要解決:主要解決接口選擇的問題。
何時使用:系統(tǒng)的產(chǎn)品有多于一個的產(chǎn)品族,而系統(tǒng)只消費其中某一族的產(chǎn)品。
如何解決:在一個產(chǎn)品族里面,定義多個產(chǎn)品。
關(guān)鍵代碼:在一個工廠里聚合多個同類產(chǎn)品。
應(yīng)用實例:工作了,為了參加一些聚會,肯定有兩套或多套衣服吧,比如說有商務(wù)裝(成套,一系列具體產(chǎn)品)、時尚裝(成套,一系列具體產(chǎn)品),甚至對于一個家庭來說,可能有商務(wù)女裝、商務(wù)男裝、時尚女裝、時尚男裝,這些也都是成套的,即一系列具體產(chǎn)品。假設(shè)一種情況(現(xiàn)實中是不存在的,要不然,沒法進入共產(chǎn)主義了,但有利于說明抽象工廠模式),在您的家中,某一個衣柜(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產(chǎn)品),每次拿這種成套的衣服時也自然要從這個衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具體工廠)都是衣柜類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體產(chǎn)品),褲子(某一具體產(chǎn)品),這些具體的上衣其實也都是上衣(抽象產(chǎn)品),具體的褲子也都是褲子(另一個抽象產(chǎn)品)。
優(yōu)點:當一個產(chǎn)品族中的多個對象被設(shè)計成一起工作時,它能保證客戶端始終只使用同一個產(chǎn)品族中的對象。
缺點:產(chǎn)品族擴展非常困難,要增加一個系列的某一產(chǎn)品,既要在抽象的 Creator 里加代碼,又要在具體的里面加代碼。
使用場景: 1、QQ 換皮膚,一整套一起換。 2、生成不同操作系統(tǒng)的程序。
注意事項:產(chǎn)品族難擴展,產(chǎn)品等級易擴展。

7.3 代碼
步驟 1
為形狀創(chuàng)建一個接口。
public interface Shape {
void draw();
}
步驟 2
創(chuàng)建實現(xiàn)接口的實體類
class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步驟 3
為顏色創(chuàng)建一個接口。
public interface Color {
void fill();
}
步驟4
創(chuàng)建實現(xiàn)接口的實體類。
class Red implements Color {
@Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
class Green implements Color {
@Override
public void fill() {
System.out.println("Inside Green::fill() method.");
}
}
class Blue implements Color {
@Override
public void fill() {
System.out.println("Inside Blue::fill() method.");
}
}
步驟 5
為 Color 和 Shape 對象創(chuàng)建抽象類來獲取工廠。
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape) ;
}
步驟 6
創(chuàng)建擴展了 AbstractFactory 的工廠類,基于給定的信息生成實體類的對象。
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
@Override
public Color getColor(String color) {
return null;
}
}
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
return null;
}
@Override
public Color getColor(String color) {
if(color == null){
return null;
}
if(color.equalsIgnoreCase("RED")){
return new Red();
} else if(color.equalsIgnoreCase("GREEN")){
return new Green();
} else if(color.equalsIgnoreCase("BLUE")){
return new Blue();
}
return null;
}
}
步驟 7
創(chuàng)建一個工廠創(chuàng)造器/生成器類,通過傳遞形狀或顏色信息來獲取工廠。
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
步驟 8
使用 FactoryProducer 來獲取 AbstractFactory,通過傳遞類型信息來獲取實體類的對象。
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
步驟 8
使用 FactoryProducer 來獲取 AbstractFactory,通過傳遞類型信息來獲取實體類的對象。
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//獲取形狀工廠
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
//獲取形狀為 Circle 的對象
Shape shape1 = shapeFactory.getShape("CIRCLE");
//調(diào)用 Circle 的 draw 方法
shape1.draw();
//獲取形狀為 Rectangle 的對象
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//調(diào)用 Rectangle 的 draw 方法
shape2.draw();
//獲取形狀為 Square 的對象
Shape shape3 = shapeFactory.getShape("SQUARE");
//調(diào)用 Square 的 draw 方法
shape3.draw();
//獲取顏色工廠
AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
//獲取顏色為 Red 的對象
Color color1 = colorFactory.getColor("RED");
//調(diào)用 Red 的 fill 方法
color1.fill();
//獲取顏色為 Green 的對象
Color color2 = colorFactory.getColor("Green");
//調(diào)用 Green 的 fill 方法
color2.fill();
//獲取顏色為 Blue 的對象
Color color3 = colorFactory.getColor("BLUE");
//調(diào)用 Blue 的 fill 方法
color3.fill();
}
}