设计模式:单例模式

发布于:2025-08-01 ⋅ 阅读:(23) ⋅ 点赞:(0)

一、单例模式介绍

1、定义

单例模式是Java中最简单的设计模式之一,此模式保证 某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类

2、使用单例模式要做的两件事

  • 保证一个类只有一个实例
  • 为该实例提供一个全局访问点

二、饿汉式

1、特点

        在类加载期间初始化私有的静态实例,保证instance实例创建过程式线程安全的。

// 饿汉式
public class Singleton01 {

    // 私有化构造方法:禁止外部通过new的方式创建对象
    private Singleton01(){}

    // 通过static 在类加载期间创建私有化静态实例
    // 静态实例属于类,在类加载时会创建出来,并且只创建一次。这样可以保证线程安全,因为拿到的始终时同一个对象
    private static Singleton01 instance = new Singleton01();
    
    // 提供全局访问点
    public static Singleton01 getInstance(){
        return instance;
    }
}

二、懒汉式(线程不安全)

1、特点

        支持懒加载(延迟加载),只有调用getInstance方法时,才会创建对象

2、问题 

在多线程下,会有线程安全问题。

        线程A 进入if判断后,还没new操作。线程B也通过if判断了。这样线程A、B 都会进行new操作。获取到两个不同的对象

// 懒汉式
public class Singleton02 {
    // 私有化构造方法:防止外部通过new的方式创建对象
    private Singleton02(){}

    // 不再这里创建对象了,调用getInstance方法时才创建
    private static Singleton02 instance;

    // 在调用getInstance方法中,判断没创建对象就新new一个对象返回
    public static Singleton02 getInstance(){
        if (instance == null){
            return new Singleton02();
        }
        return instance;
    }
}

      

三、懒汉式(线程安全)

1、特点

通过使用synchronized 锁,锁住单例模式对象的方法,防止多个线程同时调用问题。

2、缺点

对方法加了synchronized锁,并发就很低。

class Singleton03{
    private Singleton03(){}

    private static Singleton03 instance;

    //通过添加 synchronized 保证多线程模式下,单例模式对象的唯一性。
    public static synchronized Singleton03 getInstance(){
        if (instance == null){
            return new Singleton03();
        }
        return instance;
    }
}

四、懒汉式-双重校验

// 懒汉式 双重校验
public class Singleton04{

    private Singleton04(){}
    // 加volatile保证变量的可见性、屏蔽指令重排序
    private volatile static Singleton04 instance;

    // 通过添加 synchronized 保证多线程模式下,单例模式对象的唯一性。
    public static Singleton04 getInstance(){
        // 第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
        if (instance == null){
            // 第二次判断要等到抢到锁之后。
            synchronized (Singleton04.class) {
                if (instance == null) {
                    instance =  new Singleton04();
                   /**
                    * 上面的创建对象的代码,在JVM 中被分为三步:
                    *    1、分配内存空间
                    *    2、初始化对象
                    *    3、将instance指向分配好的内存空间
                    */
                }
            }
        }
        return instance;
    }
}

五、静态内部类(懒加载)

静态内部类的特性:在静态内部类中创建单例,在转载内部类的时候,才会创建单例对象。

就是只有在调用Singleton4Static 的时候才会去new 对象。

/**
 * 单例模式- 静态内部类 (懒加载)
 *  根据静态内部类的特性,同时解决了 延时加载 程序安全的问题,并且代码更加简洁
 */
class Singleton05{
    private Singleton05(){}
    // 创建静态内部类
    private static class Singleton4Static{
        private static Singleton05 instance = new Singleton05();
    }
    public static Singleton05 getInstance(){
        return Singleton4Static.instance;
    }
}

六、反射对单例对象的破坏

反射破坏的原理

  1. 突破封装setAccessible(true) 覆盖了Java的访问控制检查

  2. 绕过初始化逻辑:直接调用构造方法,跳过了静态内部类的初始化控制

  3. 类加载机制失效:静态内部类方案依赖类加载保证单例,但反射在运行时创建新实例

class Reflect{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<Singleton05> clazz = Singleton05.class;
        Constructor<Singleton05> constructor = clazz.getDeclaredConstructor();
        // 设置为true后就可以对 类中的私有成员进行操作了。
        constructor.setAccessible(true);
        Singleton05 instance01 = constructor.newInstance();
        Singleton05 instance02 = constructor.newInstance();
        System.out.println(instance01 == instance02);
    }
}

七、防止反射破坏(以Singleton05代码举例)

在私有无参构造方法中加一个判断。

class Singleton05{
    private Singleton05(){
        if (Singleton4Static.instance != null){
            throw new RuntimeException("不允许非法访问");
        }
    }
    // 创建静态内部类
    private static class Singleton4Static{
        private static Singleton05 instance = new Singleton05();
    }
    public static Singleton05 getInstance(){
        return Singleton4Static.instance;
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到