单例模式 概念: 1.一个类只有一个实例,并提供一个全局访问点 2.提供静态的getInstance()工厂方法用来访问它的唯一实例 3.为了防止其它外部对他实例化,所以构造函数私有
作用: 1.控制实例数目节省系统资源 2.可以在不建立直接关联的条件下, 让多个不相关的两个线程或者进程之间实现通信(作为通信媒介)
应用场景: 一个全局使用的类频繁的创建与销毁,eg: 1.网站计数器 2.windows系统的任务管理器和回收站 3.一般池的设计都是单例模式,例如数据库连接池和多线程连接池,可以降低资源的消耗
代码示例: 饿汉式单例: 饿汉式单例在类加载时就创建了单例实例,不管之后是否会使用这个实例。由于实例在类加载时就已经创建好,所以它是线程安全的,因为类加载过程是由 JVM 保证线程安全的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class EagerSingleton { // 在类加载时就创建单例实例 private static final EagerSingleton INSTANCE = new EagerSingleton(); // 私有构造函数,防止外部实例化 private EagerSingleton() {} // 提供公共的静态方法来获取单例实例 public static EagerSingleton getInstance() { return INSTANCE; } public void doSomething() { System.out.println("EagerSingleton is doing something."); } public static void main(String[] args) { EagerSingleton singleton = EagerSingleton.getInstance(); singleton.doSomething(); } }
适用于实例创建成本较低且需要保证线程安全的场景
懒汉式单例: 懒汉式单例在第一次使用时才创建实例,也就是在调用获取实例的方法时才进行实例化 但如果不进行适当的同步处理,懒汉式单例在多线程环境下可能会出现线程安全问题
非线程安全: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class LazySingleton { private static LazySingleton INSTANCE; private LazySingleton() {} // 非线程安全的获取实例方法 public static LazySingleton getInstance() { if (INSTANCE == null) { INSTANCE = new LazySingleton(); } return INSTANCE; } public void doSomething() { System.out.println("LazySingleton is doing something."); } public static void main(String[] args) { LazySingleton singleton = LazySingleton.getInstance(); singleton.doSomething(); } }
在 getInstance() 方法中,首先检查实例是否为 null,如果为 null 则创建实例。但在多线程环境下,可能会有多个线程同时进入 if (INSTANCE == null) 语句块,从而创建多个实例
线程安全: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 // 线程安全的懒汉式单例(双重检查锁定) public class ThreadSafeLazySingleton { // 使用 volatile 关键字确保可见性 private static volatile ThreadSafeLazySingleton INSTANCE; private ThreadSafeLazySingleton() {} // 线程安全的获取实例方法 public static ThreadSafeLazySingleton getInstance() { if (INSTANCE == null) { synchronized (ThreadSafeLazySingleton.class) { if (INSTANCE == null) { INSTANCE = new ThreadSafeLazySingleton(); } } } return INSTANCE; } public void doSomething() { System.out.println("ThreadSafeLazySingleton is doing something."); } public static void main(String[] args) { ThreadSafeLazySingleton singleton = ThreadSafeLazySingleton.getInstance(); singleton.doSomething(); } }
使用 volatile 关键字确保 INSTANCE 变量的可见性,在 getInstance() 方法中进行双重检查。首先检查实例是否为 null,如果为 null 则进入同步块,在同步块中再次检查实例是否为 null,如果仍然为 null 则创建实例。这样可以避免在多线程环境下创建多个实例
工厂模式 概念: 实例化对象不使用new,而是使用工厂方法代替,提供了一种创建对象的最佳方式 并且工厂模式中,创建对象的逻辑是不会向客户端暴露的,且通过使用一个共同的接口来指向创建的对象
代码示例: 简单工厂模式 也叫做静态工厂模式,是工厂模式的基础,它定义了一个工厂类,负责根据不同的条件创建不同类型的产品对象。简单工厂模式通常包含一个工厂类、一个抽象产品类和多个具体产品类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 // 抽象产品类 interface Product { void use(); } // 具体产品类 A class ConcreteProductA implements Product { @Override public void use() { System.out.println("Using ConcreteProductA"); } } // 具体产品类 B class ConcreteProductB implements Product { @Override public void use() { System.out.println("Using ConcreteProductB"); } } // 简单工厂类 class SimpleFactory { public static Product createProduct(String type) { if ("A".equals(type)) { return new ConcreteProductA(); } else if ("B".equals(type)) { return new ConcreteProductB(); } return null; } } // 客户端代码 public class SimpleFactoryClient { public static void main(String[] args) { Product productA = SimpleFactory.createProduct("A"); if (productA != null) { productA.use(); } Product productB = SimpleFactory.createProduct("B"); if (productB != null) { productB.use(); } } }
可以理解为不再使用new创建实例,而是使用函数代替,其实这也间接体现的java的封装特性 代码解释: ● Product 是抽象产品类,定义了产品的通用方法 use() ● ConcreteProductA 和 ConcreteProductB 是具体产品类,实现了 Product 接口 ● SimpleFactory 是简单工厂类,根据传入的参数 type 来创建不同类型的产品对象 ● 客户端代码通过调用 SimpleFactory 的 createProduct() 方法来获取产品对象
工厂方法模式: 工厂方法模式将对象的创建延迟到子类中进行,定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式包含一个抽象工厂类、多个具体工厂类、一个抽象产品类和多个具体产品类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 // 抽象产品类 interface Product { void use(); } // 具体产品类 A class ConcreteProductA implements Product { @Override public void use() { System.out.println("Using ConcreteProductA"); } } // 具体产品类 B class ConcreteProductB implements Product { @Override public void use() { System.out.println("Using ConcreteProductB"); } } // 抽象工厂类 abstract class Factory { public abstract Product createProduct(); } // 具体工厂类 A,负责创建产品 A class ConcreteFactoryA extends Factory { @Override public Product createProduct() { return new ConcreteProductA(); } } // 具体工厂类 B,负责创建产品 B class ConcreteFactoryB extends Factory { @Override public Product createProduct() { return new ConcreteProductB(); } } // 客户端代码 public class FactoryMethodClient { public static void main(String[] args) { Factory factoryA = new ConcreteFactoryA(); Product productA = factoryA.createProduct(); productA.use(); Factory factoryB = new ConcreteFactoryB(); Product productB = factoryB.createProduct(); productB.use(); } }
和简单工厂对比就是多添加了一步实现抽象工厂类的过程,让子类决定实例化哪一个类, 工厂方法把实例化的权利交给了子类 代码解释: ● Product 是抽象产品类,定义了产品的通用方法 use() ● ConcreteProductA 和 ConcreteProductB 是具体产品类,实现了 Product 接口 ● Factory 是抽象工厂类,定义了一个抽象方法 createProduct() 用于创建产品对象 ● ConcreteFactoryA 和 ConcreteFactoryB 是具体工厂类,分别实现了 createProduct() 方法,负责创建具体的产品对象 ● 客户端代码通过创建具体的工厂类对象,调用其 createProduct() 方法来获取产品对象
抽象工厂模式 上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品, 如果要生成另一类产品那应该如何表示? 这时候我们就需要用到抽象工厂模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 // 抽象产品类 A interface ProductA { void use(); } // 具体产品类 A1 class ConcreteProductA1 implements ProductA { @Override public void use() { System.out.println("Using ConcreteProductA1"); } } // 具体产品类 A2 class ConcreteProductA2 implements ProductA { @Override public void use() { System.out.println("Using ConcreteProductA2"); } } // 抽象产品类 B interface ProductB { void work(); } // 具体产品类 B1 class ConcreteProductB1 implements ProductB { @Override public void work() { System.out.println("ConcreteProductB1 is working"); } } // 具体产品类 B2 class ConcreteProductB2 implements ProductB { @Override public void work() { System.out.println("ConcreteProductB2 is working"); } } // 抽象工厂类 interface AbstractFactory { ProductA createProductA(); ProductB createProductB(); } // 具体工厂类 1,负责创建产品 A1 和产品 B1 class ConcreteFactory1 implements AbstractFactory { @Override public ProductA createProductA() { return new ConcreteProductA1(); } @Override public ProductB createProductB() { return new ConcreteProductB1(); } } // 具体工厂类 2,负责创建产品 A2 和产品 B2 class ConcreteFactory2 implements AbstractFactory { @Override public ProductA createProductA() { return new ConcreteProductA2(); } @Override public ProductB createProductB() { return new ConcreteProductB2(); } } // 客户端代码 public class AbstractFactoryClient { public static void main(String[] args) { AbstractFactory factory1 = new ConcreteFactory1(); ProductA productA1 = factory1.createProductA(); productA1.use(); ProductB productB1 = factory1.createProductB(); productB1.work(); AbstractFactory factory2 = new ConcreteFactory2(); ProductA productA2 = factory2.createProductA(); productA2.use(); ProductB productB2 = factory2.createProductB(); productB2.work(); } }
在上述代码中,为了便于理解,可以将抽象产品类a看成手机,抽象产品类b看成平板,而工厂1可以看作小米,工厂2看作apple
客户端的意思就是实现抽象工厂类类来创建具体工厂1小米,然后调用具体工厂的createProductA用来造小米手机,createProductB用来造小米平板,下面同理
总结: ● 简单工厂模式:实现简单,但不符合开闭原则,新增产品时需要修改工厂类代码。 ● 工厂方法模式:符合开闭原则,新增产品时只需新增具体产品类和具体工厂类,无需修改现有代码。 ● 抽象工厂模式:适用于创建一系列相关或相互依赖的对象,提供了更高层次的抽象和封装。