【面试题Java】单例模式

单例模式的实现方法

  • 饿汉式:在类加载时就立即创建单例实例,线程安全,实现简单。代码示例如下:
java 复制代码
public class Singleton {
    // 私有静态实例,在类加载时就创建
    private static final Singleton instance = new Singleton();

    // 私有构造函数,防止外部实例化
    private Singleton() {}

    // 公共的静态方法,用于获取单例实例
    public static Singleton getInstance() {
        return instance;
    }
}
  • 懒汉式(线程不安全) :在第一次调用getInstance方法时才创建实例,实现较为简单,但在多线程环境下可能会创建多个实例。代码示例如下:
java 复制代码
public class Singleton {
    // 私有静态实例,初始化为null
    private static Singleton instance = null;

    // 私有构造函数,防止外部实例化
    private Singleton() {}

    // 公共的静态方法,用于获取单例实例
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 懒汉式(线程安全) :通过synchronized关键字保证在多线程环境下只有一个线程能创建实例,但性能可能会受影响。代码示例如下:
java 复制代码
public class Singleton {
    // 私有静态实例,初始化为null
    private static Singleton instance = null;

    // 私有构造函数,防止外部实例化
    private Singleton() {}

    // 公共的静态方法,用于获取单例实例,添加synchronized关键字保证线程安全
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 双重检查锁(DCL) :在getInstance方法中进行两次null检查,提高了性能,同时保证线程安全。代码示例如下:
java 复制代码
public class Singleton {
    // 私有静态实例,使用volatile关键字保证可见性和禁止指令重排序
    private static volatile Singleton instance = null;

    // 私有构造函数,防止外部实例化
    private Singleton() {}

    // 公共的静态方法,用于获取单例实例
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 静态内部类 :利用静态内部类的特性,在外部类加载时,静态内部类不会被加载,只有在调用getInstance方法时才会加载内部类并创建实例,线程安全且性能较好。代码示例如下:
java 复制代码
public class Singleton {
    // 私有构造函数,防止外部实例化
    private Singleton() {}

    // 静态内部类,用于持有单例实例
    private static class SingletonHolder {
        // 私有静态实例,在类加载时创建
        private static final Singleton instance = new Singleton();
    }

    // 公共的静态方法,用于获取单例实例
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
  • 枚举:通过枚举类型实现单例,简洁且天然支持序列化和线程安全。代码示例如下:
java 复制代码
public enum Singleton {
    // 唯一的实例
    INSTANCE;

    // 可以添加其他方法和属性
    public void doSomething() {
        // 具体实现
    }
}

破坏单例的方式

  • 反射:通过反射机制可以调用私有构造函数来创建新的实例,从而破坏单例。代码示例如下:
java 复制代码
import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws Exception {
        // 获取单例类的Class对象
        Class<Singleton> clazz = Singleton.class;
        // 获取私有构造函数
        Constructor<Singleton> constructor = clazz.getDeclaredConstructor();
        // 允许访问私有构造函数
        constructor.setAccessible(true);
        // 通过构造函数创建新的实例
        Singleton instance1 = constructor.newInstance();
        // 获取单例实例
        Singleton instance2 = Singleton.getInstance();
        // 比较两个实例是否相同
        System.out.println(instance1 == instance2); 
    }
}
  • 反序列化 :如果单例类实现了Serializable接口,在反序列化时会创建一个新的实例,从而破坏单例。代码示例如下:
java 复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Main {
    public static void main(String[] args) throws Exception {
        // 创建单例实例
        Singleton instance1 = Singleton.getInstance();
        // 将单例实例序列化到文件
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton.obj"))) {
            oos.writeObject(instance1);
        }
        // 从文件中反序列化出单例实例
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton.obj"))) {
            Singleton instance2 = (Singleton) ois.readObject();
            // 比较两个实例是否相同
            System.out.println(instance1 == instance2); 
        }
    }
}

class Singleton implements Serializable {
    // 私有静态实例
    private static final Singleton instance = new Singleton();

    // 私有构造函数
    private Singleton() {}

    // 公共的静态方法,用于获取单例实例
    public static Singleton getInstance() {
        return instance;
    }

    // 反序列化时,返回单例实例
    private Object readResolve() {
        return instance;
    }
}
  • 对象克隆 :如果单例类实现了Cloneable接口,并且没有正确处理克隆方法,通过克隆也可以创建新的实例,从而破坏单例。代码示例如下:
java 复制代码
public class Main {
    public static void main(String[] args) throws Exception {
        // 获取单例实例
        Singleton instance1 = Singleton.getInstance();
        // 克隆单例实例
        Singleton instance2 = (Singleton) instance1.clone();
        // 比较两个实例是否相同
        System.out.println(instance1 == instance2); 
    }
}

class Singleton implements Cloneable {
    // 私有静态实例
    private static final Singleton instance = new Singleton();

    // 私有构造函数
    private Singleton() {}

    // 公共的静态方法,用于获取单例实例
    public static Singleton getInstance() {
        return instance;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 如果不重写clone方法,默认会创建一个新的实例
        return super.clone();
    }
}

为了防止反射和反序列化破坏单例,可以在单例类的私有构造函数中添加逻辑,判断是否已经创建过实例,如果是则抛出异常。对于对象克隆,可以重写clone方法,返回单例实例而不是创建新的实例。对于枚举类型的单例,由于其本身的特性,天然防止了反射、反序列化和对象克隆的破坏。

相关推荐
咩咩啃树皮9 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
灯澜忆梦9 小时前
GO_并发编程---定时器
开发语言·后端·golang
鱟鲥鳚9 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
-银雾鸢尾-10 小时前
C#中的StringBuilder相关方法
开发语言·c#
-银雾鸢尾-10 小时前
C#中结构体与类的区别;抽象类与接口的区别
开发语言·c#
大模型码小白11 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司12 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地13 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415913 小时前
TCP超时重传机制是为了解决什么问题?
java
段一凡-华北理工大学13 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化