小工具知识网

当前位置: 首页 > 行业知识 > JAVA设计模式——创建型模式

行业知识

JAVA设计模式——创建型模式

2026-11-03 03:46:31 来源:互联网转载

JAVA设计模式——创建型模式

  • 一、创建型模式
    • 1.单例模式(Singleton Pattern)
      • 1.1 饿汉式
      • 1.2 懒汉式
      • 1.3 双重检验锁(double check lock)(DCL)
      • 1.4 静态内部类
      • 1.5 枚举
      • 1.6 破坏单例的几种方式与解决方法
        • 1.6.1 反序列化
        • 1.6.2 反射
        • 1.7 容器式单例
        • 1.8 ThreadLocal单例
        • 1.9 总结
        • 2.工厂方法模式(Factory Method)
          • 2.1 简单工厂模式
            • 2.1.1 基础版
            • 2.1.2 升级版
            • 2.1.3 总结
            • 2.2 工厂方法模式
              • 2.2.1 代码实现
              • 2.2.2 总结
              • 3.抽象工厂模式(Abstract Factory)
                • 3.1 代码实现
                • 3.2 总结
                • 4.原型模式(Prototype)
                  • 4.1 浅克隆
                  • 4.2 深克隆
                  • 4.3 克隆破坏单例与解决办法
                  • 4.4 总结
                  • 5.建造者模式(Builder)
                    • 5.1 常规写法
                    • 5.2 简化写法
                    • 5.3 链式写法
                    • 5.4 ���结

                      一、创建型模式

                      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;}

                      但是在多线程的情况下,可能出现空指针的问题——volatile

                      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@50134894System.out.println(singleton1);//com.ruoyi.base.mapper.Singleton@5ccd43c2

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

                      private Object readResolve() {        return singleton;    }//com.ruoyi.base.mapper.Singleton@50134894//com.ruoyi.base.mapper.Singleton@50134894
                      1.6.2 反射
                      Singleton singleton = Singleton.getInstance();Class singletonClass = Singleton.class;Constructor constructor = singletonClass.getDeclaredConstructor();constructor.setAccessible(true);Singleton singleton1 = constructor.newInstance();System.out.println(singleton);//com.ruoyi.base.mapper.Singleton@32a1bec0System.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 singletonClass = Singleton.class;Constructor constructor = singletonClass.getDeclaredConstructor();constructor.setAccessible(true);Singleton singleton = constructor.newInstance();System.out.println(singleton); // com.ruoyi.base.mapper.Singleton@32a1bec0Field 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 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 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

上一篇:微网页怎么做的?

下一篇:怎么远程访问服务器管理口?