设计模式之单例模式

目录

一、简介

单例模式是oop(面向对象编程)语言的一种概念,顾名思义,就是一个类只能有一个实例对象。

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

二、单例模式实现方式

单例设计模式分类两种:

饿汉式:类加载就会导致该单实例对象被创建。

懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。

1、饿汉式单例(推荐)

饿汉式-方式1(静态变量方式

这种方法通过将对象的实例设置为静态的方式,保证了该对象的实例,永远只有一份,且该对象的创建在类加载的时候就会立即创建在jvm内存中的方法区,在程序运行期间永久存在,所以当我们的对象太大的时候就会造成一种资源的浪费。(会产生大量类加载之后但是没有使用)

java 复制代码
/**
* 饿汉式
* 静态变量创建类的对象
*/
public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance = new Singleton();
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}

通过static关键字,在类加载时创建对象。

优化实现:

上述传统方式中,由于类加载时就实例化对象,因此当我们调用这个类的其它静态方法时,也会触发类加载,从而实例化单例独享,会导致空间的暂时浪费。

由于静态内部类中的对象不会默认加载,直到调用了获取该内部类属性的方法。因此可用静态内部类封装静态实例变量。

饿汉式-方式2(静态代码块方式)

对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样。

java 复制代码
/**
* 饿汉式-方法2
* 在静态代码块中创建该类对象
*/
public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance;
    static {
        instance = new Singleton();
    }
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        return instance;
    }
}
java 复制代码
class Singleton{
// 私有构造函数
private Singleton() {}

// 静态内部类
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
     return SingletonHolder.instance;
   }
}

2、懒汉式单例

懒汉式-方法1(线程不安全)

java 复制代码
/**
 * 懒汉式
 * 线程不安全
 */
public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式-方法2(线程安全)

由于可能有多个线程同时要使用对象,因此需要考虑线程安全问题,防止并发访问时生成多个实例。

通常需要加锁来解决并发冲突,是用时间换空间的方案。

java 复制代码
/**
 * 懒汉式
 * 线程安全
 */
public class Singleton {
    //私有构造方法
    private Singleton() {}
    //在成员位置创建该类的对象
    private static Singleton instance;
    //对外提供静态方法获取该对象,并且加锁
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式-方法3(双重锁结构)

双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,但是呢,JVM在实例化对象的时候会进行优化和指令重排序操作,在多线程的情况下,就可能会出现空指针问题

对于上述对象的创建主要分为三个部分:

  • 分配对象的内存空间。
  • 初始化对象。
  • 设置instance指针指向对象所在的内存空间。

为了提高性能在JVM在实例化对象的时候会进行优化和指令重排序操作,也就是说有可能将我们上述的第七行和第九行代码进行顺序的交换。

java 复制代码
/**
 * 双重检查方式
 */
public class Singleton {
    //私有构造方法
    private Singleton() {}
    //使用volatile修饰,禁止重排序
    private static volatile Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
        if(instance == null) {
            synchronized (Singleton.class) {
//抢到锁之后再次判断是否为空
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

三、单例破坏的方式

1、反射攻击

java 复制代码
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {
        // 私有构造
    }
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

// 反射攻击代码:
public class ReflectionAttack {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance();
        
        // 使用反射创建第二个实例
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);  // 突破私有访问限制
        Singleton instance2 = constructor.newInstance();
        
        System.out.println(instance1 == instance2);  // false!破坏了单例
        System.out.println("instance1: " + instance1);
        System.out.println("instance2: " + instance2);
    }
}

通过类信息进行反射创建。

2、序列化攻击

java 复制代码
import java.io.*;

public class Singleton implements Serializable {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

// 序列化攻击:
public class SerializationAttack {
    public static void main(String[] args) throws Exception {
        Singleton instance1 = Singleton.getInstance();
        
        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(instance1);
        
        // 反序列化(会创建新实例!)
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Singleton instance2 = (Singleton) ois.readObject();
        
        System.out.println(instance1 == instance2);  // false!
    }
}

3、克隆攻击

java 复制代码
public class CloneableSingleton implements Cloneable {
    private static final CloneableSingleton INSTANCE = new CloneableSingleton();
    
    private CloneableSingleton() {}
    
    public static CloneableSingleton getInstance() {
        return INSTANCE;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();  // 默认实现会创建新实例
    }
}

// 攻击:
CloneableSingleton instance1 = CloneableSingleton.getInstance();
CloneableSingleton instance2 = (CloneableSingleton) instance1.clone();
System.out.println(instance1 == instance2);  // false!

4、防御方式

java 复制代码
// 方法1:使用统一的类加载器(应用类加载器)
// 方法2:在getInstance()中检查类加载器
public class ClassLoaderSafeSingleton {
    private static final ClassLoaderSafeSingleton INSTANCE = new ClassLoaderSafeSingleton();
    
    private ClassLoaderSafeSingleton() {
        // 检查类加载器
        ClassLoader cl = this.getClass().getClassLoader();
        if (cl != ClassLoader.getSystemClassLoader()) {
            throw new RuntimeException("只能由系统类加载器加载");
        }
    }
    
    public static ClassLoaderSafeSingleton getInstance() {
        return INSTANCE;
    }
}
相关推荐
一条闲鱼_mytube2 小时前
智能体设计模式(四)模型上下文协议-目标设定与监控-异常处理与恢复
microsoft·设计模式
老蒋每日coding2 小时前
AI Agent 设计模式系列(十)——模型上下文协议 (MCP)
人工智能·设计模式
Yu_Lijing3 小时前
基于C++的《Head First设计模式》笔记——迭代器模式
笔记·设计模式
一条闲鱼_mytube16 小时前
智能体设计模式(三)多智能体协作-记忆管理-学习与适应
人工智能·学习·设计模式
小屁猪qAq21 小时前
设计模式总纲
开发语言·c++·设计模式
小简GoGo1 天前
前端常用设计模式快速入门
javascript·设计模式
会员果汁1 天前
17.设计模式-单例模式(Singleton)
单例模式·设计模式
派大鑫wink1 天前
【Day37】MVC 设计模式:原理与手动实现简易 MVC 框架
java·设计模式·mvc
会员果汁1 天前
18.设计模式-桥接模式
设计模式·桥接模式