2025年Java中23种设计模式

Java中23种设计模式一 创建型模式 1 单例模式 Singleton Pattern 单例模式 Singleton Pattern 是 Java 中最简单的设计模式之一 这种模式涉及到一个单一的类 该类负责创建自己的对象 同时确保只有单个对象被创建 这个类提供了一种访问其唯一的对象的方式

大家好,我是讯享网,很高兴认识大家。

一、创建型模式

1.单例模式(Singleton Pattern)

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

1.1 饿汉式

特点:类加载时就初始化,线程安全

 // 构造方法私有化 private Singleton() { } // 饿汉式创建单例对象 private static Singleton singleton = new Singleton(); public static Singleton getInstance() { return singleton; }

讯享网

1.2 懒汉式

特点:第一次调用才初始化,避免内存浪费。

讯享网 /* * 懒汉式创建单例模式 由于懒汉式是非线程安全, 所以加上线程锁保证线程安全 */ private static Singleton singleton; public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; }

1.3 双重检验锁(double check lock)(DCL)

特点:安全且在多线程情况下能保持高性能

 private volatile static Singleton singleton; private Singleton (){} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; }

1.4 静态内部类

特点:效果类似DCL,只适用于静态域

讯享网 private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; }

1.5 枚举

特点:自动支持序列化机制,绝对防止多次实例化

public enum Singleton { INSTANCE; }

1.6 破坏单例的几种方式与解决方法

1.6.1 反序列化

讯享网 Singleton singleton = Singleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/test.txt")); oos.writeObject(singleton); oos.flush(); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/test.txt")); Singleton singleton1 = (Singleton)ois.readObject(); ois.close(); System.out.println(singleton);//com.ruoyi.base.mapper.Singleton@ System.out.println(singleton1);//com.ruoyi.base.mapper.Singleton@5ccd43c2

可以看到反序列化后,两个对象的地址不一样了,那么这就是违背了单例模式的原则了,解决方法只需要在单例类里加上一个readResolve()方法即可,原因就是在反序列化的过程中,会检测readResolve()方法是否存在,如果存在的话就会反射调用readResolve()这个方法。

private Object readResolve() { return singleton; } //com.ruoyi.base.mapper.Singleton@ //com.ruoyi.base.mapper.Singleton@

1.6.2 反射

讯享网 Singleton singleton = Singleton.getInstance(); Class<Singleton> singletonClass = Singleton.class; Constructor<Singleton> constructor = singletonClass.getDeclaredConstructor(); constructor.setAccessible(true); Singleton singleton1 = constructor.newInstance(); System.out.println(singleton);//com.ruoyi.base.mapper.Singleton@32a1bec0 System.out.println(singleton1);//com.ruoyi.base.mapper.Singleton@22927a81

同样可以看到,两个对象的地址不一样,这同样是违背了单例模式的原则,解决办法为使用一个布尔类型的标记变量标记一下即可,代码如下:

private static boolean singletonFlag = false; private Singleton() { if (singleton != null || singletonFlag) { throw new RuntimeException("试图用反射破坏异常"); } singletonFlag = true; }

但是这种方法假如使用了反编译,获得了这个标记变量,同样可以破坏单例,代码如下:

讯享网 Class<Singleton> singletonClass = Singleton.class; Constructor<Singleton> constructor = singletonClass.getDeclaredConstructor(); constructor.setAccessible(true); Singleton singleton = constructor.newInstance(); System.out.println(singleton); // com.ruoyi.base.mapper.Singleton@32a1bec0 Field singletonFlag = singletonClass.getDeclaredField("singletonFlag"); singletonFlag.setAccessible(true); singletonFlag.setBoolean(singleton, false); Singleton singleton1 = constructor.newInstance(); System.out.println(singleton1); // com.ruoyi.base.mapper.Singleton@5e8c92f4

如果想使单例不被破坏,那么应该使用枚举的方式去实现单例模式,枚举是不可以被反射破坏单例的。

1.7 容器式单例

当程序中的单例对象非常多的时候,则可以使用容器对所有单例对象进行管理,如下:

public class ContainerSingleton { private ContainerSingleton() {} private static Map<String, Object> singletonMap = new ConcurrentHashMap<>(); public static Object getInstance(Class clazz) throws Exception { String className = clazz.getName(); // 当容器中不存在目标对象时则先生成对象再返回该对象 if (!singletonMap.containsKey(className)) { Object instance = Class.forName(className).newInstance(); singletonMap.put(className, instance); return instance; } // 否则就直接返回容器里的对象 return singletonMap.get(className); } public static void main(String[] args) throws Exception { SafetyDangerLibrary instance1 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class); SafetyDangerLibrary instance2 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class); System.out.println(instance1 == instance2); // true } }

1.8 ThreadLocal单例

不保证整个应用全局唯一,但保证线程内部全局唯一,以空间换时间,且线程安全。

讯享网public class ThreadLocalSingleton { private ThreadLocalSingleton(){} private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = ThreadLocal.withInitial(() -> new ThreadLocalSingleton()); public static ThreadLocalSingleton getInstance(){ return threadLocalInstance.get(); } public static void main(String[] args) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); }).start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); }).start(); // Thread-0-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@53ac93b3 // Thread-1-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@7fe11afc // Thread-0-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@53ac93b3 // Thread-1-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@7fe11afc } } 

可以看到上面线程0和1他们的对象是不一样的,但是线程内部,他们的对象是一样的,这就是线程内部保证唯一。

1.9 总结

适用场景:

  • 需要确保在任何情况下绝对只需要一个实例。如:ServletContext,ServletConfig,ApplicationContext,DBPool,ThreadPool等。

优点:


讯享网

  • 在内存中只有一个实例,减少了内存开销。
  • 可以避免对资源的多重占用。
  • 设置全局访问点,严格控制访问。

缺点:

  • 没有接口,扩展困难。
  • 如果要扩展单例对象,只有修改代码,没有其它途径。

2.工厂方法模式(Factory Method)

2.1 简单工厂模式

简单工厂模式不是23种设计模式之一,他可以理解为工厂模式的一种简单的特殊实现。

2.1.1 基础版

// 工厂类 public class CoffeeFactory { public Coffee create(String type) { if ("americano".equals(type)) { return new Americano(); } if ("mocha".equals(type)) { return new Mocha(); } if ("cappuccino".equals(type)) { return new Cappuccino(); } return null; } } 

讯享网// 产品基类 public interface Coffee { } // 产品具体类,实现产品基类接口 public class Cappuccino implements Coffee { }

基础版是最基本的简单工厂的写法,传一个参数过来,判断是什么类型的产品,就返回对应的产品类型。但是这里有一个问题,就是参数是字符串的形式,这就很容易会写错,比如少写一个字母,或者小写写成了大写,就会无法得到自己想要的产品类了,同时如果新加了产品,还得在工厂类的创建方法中继续加if,于是就有了升级版的写法。

2.1.2 升级版

 // 使用反射创建对象 // 加一个static变为静态工厂 public static Coffee create(Class<? extends Coffee> clazz) throws Exception { if (clazz != null) { return clazz.newInstance(); } return null; }

升级版就很好的解决基础版的问题,在创建的时候在传参的时候不仅会有代码提示,保证不会写错,同时在新增产品的时候只需要新增产品类即可,也不需要再在工厂类的方法里面新增代码了。

2.1.3 总结

适用场景:

  • 工厂类负责创建的对象较少。
  • 客户端只需要传入工厂类的参数,对于如何创建的对象的逻辑不需要关心。

优点:

  • 只需要传入一个正确的参数,就可以获取你所需要的对象,无须知道创建的细节。

缺点:

  • 工厂类的职责相对过重,增加新的产品类型的时需要修改工厂类的判断逻辑,违背了开闭原则。
  • 不易于扩展过于复杂的产品结构。

2.2 工厂方法模式

工厂方法模式是指定义一个创建对象的接口,让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

工厂方法模式主要有以下几种角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它和具体工厂之间一一对应。

2.2.1 代码实现

讯享网// 抽象工厂 public interface CoffeeFactory { Coffee create(); } // 具体工厂 public class CappuccinoFactory implements CoffeeFactory { @Override public Coffee create() { return new Cappuccino(); } } // 抽象产品 public interface Coffee { } // 具体产品 public class Cappuccino implements Coffee { }

2.2.2 总结

适用场景:

  • 创建对象需要大量的重复代码。
  • 客户端(应用层)不依赖于产品类实例如何被创建和实现等细节。
  • 一个类通过其子类来指定创建哪个对象。

优点:

  • 用户只需要关系所需产品对应的工厂,无须关心创建细节。
  • 加入新产品符合开闭原则,提高了系统的可扩展性。

缺点:

  • 类的数量容易过多,增加了代码结构的复杂度。
  • 增加了系统的抽象性和理解难度。

3.抽象工厂模式(Abstract Factory)

抽象工厂模式是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。

 工厂方法模式中考虑的是一类产品的生产,如电脑厂只生产电脑,电话厂只生产电话,这种工厂只生产同种类的产品,同种类产品称为同等级产品,也就是说,工厂方法模式只考虑生产同等级的产品,但是现实生活中许多工厂都是综合型工厂,能生产多等级(种类)的产品,如上面说的电脑和电话,本质上他们都属于电器,那么他们就能在电器厂里生产出来,而抽象工厂模式就将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,如上图所示纵轴是产品等级,也就是同一类产品;横轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。

抽象工厂模式的主要角色如下:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

3.1 代码实现

// 咖啡店 抽象工厂 public interface CoffeeShopFactory { // 咖啡类 Coffee createCoffee(); // 甜点类 Dessert createDessert(); } // 美式风格工厂 public class AmericanFactory implements CoffeeShopFactory { @Override public Coffee createCoffee() { return new Americano(); } @Override public Dessert createDessert() { return new Cheesecake(); } } // 意式风格工厂 public class ItalyFactory implements CoffeeShopFactory { @Override public Coffee createCoffee() { return new Cappuccino(); } @Override public Dessert createDessert() { return new Tiramisu(); } }

类图
小讯
上一篇 2025-01-28 23:03
下一篇 2025-03-04 08:00

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/71095.html