創建型模式
單例模式
一個類只允許創建一個對象,稱為單例。
單例體現:配置類、連接池、全局計數器、id生成器、日志對象。
懶漢式
(線程不安全) 單例:【不可用】
用到該單例對象的時候再創建。但存在很大問題,單線程下這段代碼沒有問題,但是在多線程下有很大問題。
public class LazyMan {private LazyMan(){};public static LazyMan lazyMan; public static LazyMan getInstance(){if (lazyMan==null){lazyMan = new LazyMan(); //用到該單例對象的時候再創建,這里沒加鎖,如果多個線程進入,會并發產生多個實例}return lazyMan;}
}
(線程安全)單例:【不推薦使用】
同步方法
缺點:效率低,每次getInstance時都要同步處理,存在性能問題,實際開發不推薦
public class LazyMan {private LazyMan(){}; //私有化構造函數,防止外部實例化public static LazyMan lazyMan; public static synchroized LazyMan getInstance(){ //加鎖if (lazyMan==null){lazyMan = new LazyMan(); //用到該單例對象的時候再創建}return lazyMan;}
}
雙檢鎖(線程安全):【推薦使用】
- 實例化代碼只用執行一次,后面再次訪問時,判斷
if (singleton == null)
,不為空則直接return實例化對象。 - 利用
volatile
關鍵字保證了可見性,利用雙重檢查機制減少了同步帶來的性能損耗。
public class Singleton {private static volatile Singleton instance;// 私有構造函數,防止外部實例化private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
餓漢式
(靜態常量)【可用】
類一旦加載就創建一個單例,保證在調用getInstance
方法之前單例已經存在,即沒有延遲加載,這種餓漢式單例會造成空間浪費。
public class Hungry {private Hungry(){}private final static Hungry HUNGRY = new Hungry(); //在類內部就創建了一個靜態對象,并且get方法返回該對象public static Hungry getInstance(){return HUNGRY;}
}
//或者 靜態代碼塊形式
public class Hungry {static{private final static Hungry HUNGRY = new Hungry(); } private Hungry(){}public static Hungry getInstance(){return HUNGRY;}
}Hungry in1=Hungry.getInstance()
Hungry in2=Hungry.getInstance() //in1==in2
靜態內部類
- (線程安全)單例:【推薦使用】
- 不僅線程安全,還實現了延遲加載
public class Inner {private Inner(){}//直到調用 getInstance() 方法之前,Inner 實例都不會被創建,實現了延遲初始化public static Inner getInstance(){return InnerClass.INNER;}private static class InnerClass{ private static final Inner INNER = new Inner(); //靜態字段只會在類加載時被初始化一次,線程安全}
}
枚舉
- (線程安全)【推薦使用】不僅線程安全,還能防止反序列化導致重新創建新的對象
class SingletonEnum{// 1、創建一個枚舉public enum CreateInstance{// 枚舉實例,底層變量定義是public static final,因此它在 JVM 中只會被初始化一次,并且是線程安全的INSTANCE;private SingletonEnum instance;// 保證不能在類外部通過new構造器來構造對象private CreateInstance() {instance = new SingletonEnum();System.out.println(Thread.currentThread().getName());}// 創建一個公共的方法,由實例調用返回單例類public SingletonEnum getInstance() {return instance;}}public static void main(string[] args){Singleton instance = CreateInstance.INSTANCE.getInstance();singleton instance2= CreateInstance.INSTANCE.getInstance();System.out.println(instance == instance2); //輸出 true}
}
原型模式
通過拷貝來創建新的對象,而不是通過實例化一個類,減少創建對象的成本,適用于創建對象成本高,需要大量相似對象的情況。
- Java 的 clone 僅是淺拷貝,默寫場景需要 使用深拷貝避免共享數據的導致錯亂
- 在 Spring 中,將 Bean 的作用范圍設置為 prototype,這樣每次從容器中獲取 Bean 時,都會返回一個新的實例。
一個簡單的原型模式實現例子如下
先創建一個原型類:
public class Prototype implements Cloneable { //一個原型類,只需要實現Cloneable接口,覆寫clone方法@Overridepublic Object clone() throws CloneNotSupportedException { Prototype proto = (Prototype) super.clone(); //因為此處的重點是super.clone()這句話return proto;}
}
Prototype pro=new Prototype();
Prototype pro1=(Prototype)pro.clone(); //克隆 二者地址不同
//不管深淺,拷貝出來的對象的地址是不一樣的,只是若對象里面有引用,那么深淺拷貝會不一樣
@Data
public class Prototype implements Cloneable, Serializable {private static final long serialVersionUID = 1L;private String string;private SerializableObject obj;/* 淺拷貝 */public Object clone() throws CloneNotSupportedException {Prototype proto = (Prototype) super.clone();return proto;}/* 深拷貝 */public Object deepClone() throws IOException, ClassNotFoundException {/* 寫入當前對象的二進制流 */ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);/* 讀出二進制流產生的新對象 */ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return ois.readObject();}}
class SerializableObject implements Serializable {private static final long serialVersionUID = 1L;
}
工廠模式
簡單工廠
簡單工廠模式通過傳入的不同的參數來控制創建哪個具體產品。它的實現較為簡單,但不夠靈活,違反了開放封閉原則。
- 抽象產品接口: 定義了產品的規范,描述了產品的主要特性和功能
- 具體產品實現類: 實現或者繼承抽象產品的子類
- 簡單工廠: 提供了創建產品的方法,調用者通過該方法來獲取產品。
// 產品接口
public interface Product {void use();
}
// 具體產品A
public class ConcreteProductA implements Product {@Overridepublic void use() {System.out.println("Using ConcreteProductA");}
}
// 具體產品B
public class ConcreteProductB implements Product {@Overridepublic void use() {System.out.println("Using ConcreteProductB");}
}// 簡單工廠類
public class SimpleFactory {public static Product createProduct(String type) {switch (type) {case "A":return new ConcreteProductA();case "B":return new ConcreteProductB();default:throw new IllegalArgumentException("Unknown product type");}}
}
// 客戶端代碼
public class Client {public static void main(String[] args) {Product productA = SimpleFactory.createProduct("A");productA.use(); // Output: Using ConcreteProductAProduct productB = SimpleFactory.createProduct("B");productB.use(); // Output: Using ConcreteProductB}
}
工廠方法
一個具體的工廠類負責生產一種產品
抽象工廠==>具體工廠
抽象產品==>具體產品(由具體工廠創建)
// 抽象產品
interface Product {void use();
}
// 具體產品A
class ConcreteProductA implements Product {public void use() {System.out.println("Using ConcreteProductA");}
}
// 具體產品B
class ConcreteProductB implements Product {public void use() {System.out.println("Using ConcreteProductB");}
}// 抽象工廠
interface Factory {Product createProduct();
}
// 具體工廠A
class ConcreteFactoryA implements Factory {public Product createProduct() {return new ConcreteProductA();}
}
// 具體工廠B
class ConcreteFactoryB implements Factory {public Product createProduct() {return new ConcreteProductB();}
}// 使用工廠方法創建產品
public class Client {public static void main(String[] args) {new ConcreteFactoryA().createProduct();new ConcreteFactoryB().createProduct() ;}
}
抽象工廠
- 是工廠方法的一種變體,創建一系列相關或相互依賴對象的接口,即生產一系列產品
- 給具體工廠類 添加一個工廠接口,使得工廠類可以多實現
// 抽象產品A
public interface ProductA {void use();
}
// 具體產品A1
public class ConcreteProductA1 implements ProductA {@Overridepublic void use() {System.out.println("Using ConcreteProductA1");}
}
// 具體產品A2
public class ConcreteProductA2 implements ProductA {@Overridepublic void use() {System.out.println("Using ConcreteProductA2");}
}// 抽象產品B
public interface ProductB {void eat();
}
// 具體產品B1
public class ConcreteProductB1 implements ProductB {@Overridepublic void eat() {System.out.println("Eating ConcreteProductB1");}
}
// 具體產品B2
public class ConcreteProductB2 implements ProductB {@Overridepublic void eat() {System.out.println("Eating ConcreteProductB2");}
}// 抽象工廠
public interface AbstractFactory {ProductA createProductA();ProductB createProductB();
}// 具體工廠1
public class ConcreteFactory1 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA1();}@Overridepublic ProductB createProductB() {return new ConcreteProductB1();}
}// 具體工廠2
public class ConcreteFactory2 implements AbstractFactory {@Overridepublic ProductA createProductA() {return new ConcreteProductA2();}@Overridepublic ProductB createProductB() {return new ConcreteProductB2();}
}// 使用抽象工廠創建產品
public class Client {public static void main(String[] args) {AbstractFactory factory1 = new ConcreteFactory1();ProductA productA1 = factory1.createProductA();ProductB productB1 = factory1.createProductB();productA1.use();productB1.eat();AbstractFactory factory2 = new ConcreteFactory2();ProductA productA2 = factory2.createProductA();ProductB productB2 = factory2.createProductB();productA2.use();productB2.eat();}
}
建造者模式
建造者模式使用相同的代碼基礎構建不同類型的對象,通過將對象構建過程分解為多個較小的步驟來實現此目的,如StringBuilder
兩個主要組成部分:建造者和產品 建造者是負責構建產品的類,產品則是最終構建的對象
例如 hutool 內的 ExecutorBuilder 就提供了建造者模式創建線程池的方法
public ExecutorService buildTaskPool() {
return ExecutorBuilder.create().setCorePoolSize(10).setMaxPoolSize(20).setWorkQueue(new LinkedBlockingQueue<>(100)).setKeepAliveTime(3L, TimeUnit.SECONDS).setThreadFactory(new ThreadFactoryBuilder().setNamePrefix("task-pool-").build()).build();
}
public class Product { //最終構建的對象,即產品private String partA;private String partB;private String partC;public void setPartA(String partA) {this.partA = partA;}public void setPartB(String partB) {this.partB = partB;} public void setPartC(String partC) {this.partC = partC;}// other product-related methods}public interface Builder { //Builder接口或抽象類,定義了構建過程的關鍵步驟void buildPartA();void buildPartB();void buildPartC();Product getResult();
}public class ConcreteBuilder implements Builder { //實現了Builder接口中定義的方法,以構建具體的Product對象private Product product = new Product();public void buildPartA() {product.setPartA("Part A");}public void buildPartB() {product.setPartB("Part B");}public void buildPartC() {product.setPartC("Part C");} public Product getResult() {return product;}
}public class Director { //指導類,它負責使用Builder對象來構建最終的Product對象private Builder builder;public Director(Builder builder) { //實現依賴倒置原則,依賴于抽象this.builder = builder; }public void construct() { //確保Product對象按照指定的順序創建builder.buildPartA();builder.buildPartB();builder.buildPartC();}
}Builder builder = new ConcreteBuilder(); //創建建造者
Director director = new Director(builder); //創建指導
director.construct();
Product product = builder.getResult();
//這將構建一個Product對象,并將其存儲在product變量中。