单例模式-创建型

单例分为懒汉式和饿汉式

简单代码块

懒汉式

个人解释懒加载生成对象,即用到的时候才去实例化
单线程

public class Lazy {
    private static Lazy l;

    private Lazy() {

    }

    public static Lazy getH(){
        if(l==null){
            l=new Lazy();
        }
        return l;
    }
}
//懒汉式 双检锁 多线程
public class SingleTest {
    private static boolean key = false;
    
    //volatile指令实现有序性,防止指令重排序,否则在双检锁中获取到null
    //eg:第一个线程进入singleTest=new SingleTest(); 但是顺序是先引用赋值未初始化
    //第二个线程进入发现第一个判断singleTest!=null 返回未初始化的singleTest
    private static volatile SingleTest singleTest;

    private SingleTest(){
        synchronized (SingleTest.class){
            if (key==false){
                key=true;
            }
            else{
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
        System.out.println(Thread.currentThread().getName()+" ok");
    }
    public static SingleTest instance(){
        if(singleTest==null){
            synchronized (SingleTest.class){
                if(singleTest==null){
                    singleTest=new SingleTest();
                }
            }
        }
        return singleTest;
    }

    public static void main(String[] args) {
        try {
            //Java中有反射
//        LazyMan instance = LazyMan.getInstance();
            Field key = SingleTest.class.getDeclaredField("key");
            key.setAccessible(true);
            Constructor<SingleTest> declaredConstructor = 		SingleTest.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true); //无视了私有的构造器
            SingleTest instance1 = SingleTest.instance();
            key.set(instance1,false);
            SingleTest lazyMan1 = declaredConstructor.newInstance();


            SingleTest instance = declaredConstructor.newInstance();

            System.out.println(instance);
            System.out.println(lazyMan1);
            System.out.println(instance == lazyMan1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

饿汉式

普通饿汉式

public class Hungry {
    private static final Hungry h=new Hungry();

    private Hungry() {
        
    }
  
    public static Hungry getH(){
        return h;
    }
}

静态内部类 天生线程安全

注意使用ObejectOutputStream oos序列化前后导致对象不是同一地址,可以重写readResolve()方法解决

//静态内部类
public class SingleStatic {

    private static class Inner{
        private static final SingleStatic singleStatic=new SingleStatic();
    }
    private SingleStatic(){

    }
    public static SingleStatic instance(){
        return Inner.singleStatic;
    }
}

Enum枚举类型 天生线程安全 序列化也能包装同一对象

public enum  EnumSingleTest {
    INSTANCE;
    int a;
    public EnumSingleTest instance() {
        return INSTANCE;
    }

    public static void main(String[] args) {
        try {

            Constructor<EnumSingleTest> declaredConstructor = EnumSingleTest.class.getDeclaredConstructor(String.class,int.class);
            declaredConstructor.setAccessible(true);
            EnumSingleTest enumSingleTest = declaredConstructor.newInstance();
            System.out.println(enumSingleTest);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
posted @   8023渡劫  阅读(40)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示