设计模式之单例模式精讲

UML图:

  • 静态私有变量(即常量)保存单例对象,防止使用过程中重新赋值,破坏单例。
  • 私有化构造方法,防止外部创建新的对象,破坏单例。
  • 静态公共getInstance方法,作为唯一获取单例对象的入口。
java 复制代码
public class Demo1 {
    public static void main(String[] args) {
        Singleton singleton1 = new Singleton();
        Singleton singleton2 = new Singleton();
        System.out.println(singleton1);
        System.out.println(singleton2);
        System.out.println(singleton1 == singleton2);
    }
    static class Singleton {
        public Singleton() {
        }
    }
}
  1. 饿汉式--最简单有效,唯一的缺陷在于当对象占用空间较大时,可能浪费内存空间。
java 复制代码
public class Demo2 {
    public static void main(String[] args) {
        Demo2 instance = Demo2.getInstance();
        Demo2 instance2 = Demo2.getInstance();
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance == instance2);
    }
    private static final Demo2 singleton = new Demo2();
    
    private Demo2() {
        
    }
    public static Demo2 getInstance() {
        return singleton;
    }
}
  1. DCL(Double Check Lock)懒汉式
java 复制代码
public class Demo3 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Demo3 instance = Demo3.getInstance();
        Demo3 instance2 = Demo3.getInstance();
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance == instance2);
        System.out.println("**************************************");
        Constructor<Demo3> declaredConstructor = Demo3.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Demo3 demo3 = declaredConstructor.newInstance();
        System.out.println(demo3);
        System.out.println(demo3 == instance);
    }
    private static volatile Demo3 SINGLETON;
    private Demo3() {
    }
    public static Demo3 getInstance() {
        if (SINGLETON == null) {
            synchronized (Demo3.class) {
                if (SINGLETON == null) {
                    SINGLETON = new Demo3();
                }
            }
        }
        return SINGLETON;
    }
}
  1. 静态内部类
    1. 懒加载(Lazy Initialization):
      1. 实例仅在第一次调用 getInstance() 方法时创建,这意味着如果在整个程序运行过程中,单例并未被实际使用,则不会创建其实例,避免了不必要的内存消耗。
    2. 线程安全(Thread Safety):
      1. JVM确保了类的静态初始化只会发生一次,并且是线程安全的。静态内部类的实例化过程会被JVM自动处理并确保其原子性,无需程序员显式添加同步锁。
java 复制代码
public class Demo4 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Demo4 instance = Demo4.getInstance();
        Demo4 instance2 = Demo4.getInstance();
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance == instance2);
        System.out.println("**************************");
        Constructor<Demo4> declaredConstructor = Demo4.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Demo4 demo4 = declaredConstructor.newInstance();
        System.out.println(demo4);
        System.out.println(demo4 == instance);
    }
    private Demo4() {
    }
    public static class InnerClass {
        private static final Demo4 DEMO4 = new Demo4();
    }
    public static Demo4 getInstance() {
        return InnerClass.DEMO4;
    }
}
  1. 枚举类--最大的优势就是可以防止通过反射创建新对象。初始化时机: 枚举类型的实例是在类加载时由JVM统一初始化的,这个过程是由JVM的类加载机制保障线程安全的。当枚举类型被首次访问时,JVM会确保枚举类的所有实例都被正确地初始化,且这个初始化过程只执行一次,并在全局范围内保持可见。构造函数的私有化: 枚举类型隐式包含了构造函数,并且默认为私有,不允许外部直接实例化。因此,用户无法随意创建新的枚举实例,确保了在整个系统中只能存在预定义的一组实例。JVM的内存模型: 枚举实例一旦被创建,就会存储在JVM方法区的枚举类的常量池中,每个枚举值都是一个不可变的、唯一引用的对象,这就从根本上杜绝了多线程环境下不同线程创建多个实例的可能性。
java 复制代码
public enum Demo5 {
    INSTANCE;
    public Demo5 getInstance() {
        return INSTANCE;
    }
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Demo5 instance = Demo5.INSTANCE.getInstance();
        Demo5 instance2 = Demo5.INSTANCE.getInstance();
        System.out.println(instance);
        System.out.println(instance.hashCode());
        System.out.println(instance2);
        System.out.println(instance2.hashCode());
        System.out.println(instance == instance2);
        System.out.println("***************************************************");
        Constructor<?>[] declaredConstructors = Demo5.class.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            Object demo5 = declaredConstructor.newInstance();
            System.out.println(demo5);
            System.out.println(demo5.hashCode());
            System.out.println(demo5 == instance);
        }
    }
}
相关推荐
null or notnull10 分钟前
idea对jar包内容进行反编译
java·ide·intellij-idea·jar
angen20181 小时前
二十三种设计模式-享元模式
设计模式·享元模式
言午coding1 小时前
【性能优化专题系列】利用CompletableFuture优化多接口调用场景下的性能
java·性能优化
缘友一世2 小时前
JAVA设计模式:依赖倒转原则(DIP)在Spring框架中的实践体现
java·spring·依赖倒置原则
何中应2 小时前
从管道符到Java编程
java·spring boot·后端
SummerGao.2 小时前
springboot 调用 c++生成的so库文件
java·c++·.so
组合缺一3 小时前
Solon Cloud Gateway 开发:Route 的过滤器与定制
java·后端·gateway·reactor·solon
我是苏苏3 小时前
C#高级:常用的扩展方法大全
java·windows·c#
customer083 小时前
【开源免费】基于SpringBoot+Vue.JS贸易行业crm系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源
_GR4 小时前
Java程序基础⑪Java的异常体系和使用
java·开发语言