设计模式之单例模式基础掌握

本文深入解析单例设计模式,涵盖饿汉式、懒汉式、双重检索懒汉式及静态内部类实现方式,探讨volatile关键字作用,适用于希望提升代码质量与效率的开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本文章为自己复习做的基础记录,总结,新手可以看看,我会在代码中详细介绍代码含义,以及简单结论,一起学习
饿汉式
懒汉式
双重检索懒汉式
volatile 解释
静态内部类方式(了解即可)
跳转:反射破坏单例及扩展

设计模式概述

设计模式(Design Pattern)是对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解。

单例设计模式定义和特点:

  1. 单例设计模式保证一个类只有一个实例,要提供一个访问该类对 象实例的全局访问点(方法)。(静态方法)
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
注意:记住第一条就可以,下面的只是帮助理解

饿汉式

饿汉式(静态常量)
步骤:
构造器私有化(防止new)
类的内部创建对象
向外暴露一个静态的公共方法 getinstance

//饿汉式   静态常量
public class demo1 {
    public static void main(String[] args) {
        //测试
        Singleton singleton=Singleton.getInstance();//拿到实例
        Singleton singleton2=Singleton.getInstance();
        System.out.println(singleton==singleton2);//测试判断是否相等,结果应为true
        System.out.println(singleton.hashCode()==singleton2.hashCode());//测试判断hashcode地址是否相等  结果应为true

    }
}
class Singleton{
    //构造器私有化,外部不能new
    private Singleton() { }
    //本类内部创建对象实例
    private static final Singleton instance=new Singleton();

    //对外提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance(){
        return instance;
    }

}

分析优缺点:
优点:写法简单,在类装载时完成实例化,避免线程同步
缺点:因为在类装载时完成实例化,没有Lazy Loading(懒加载)效果,如果一直没有使用该实例,会造成内存浪费,一上来就全部加载

  1. 饿汉式第二种方式
    饿汉式(静态代码块)
//饿汉式   静态代码块
public class demo2 {
    public static void main(String[] args) {
        //测试
        Singleton2 singleton3=Singleton2.getInstance2();//拿到实例
        Singleton2 singleton4=Singleton2.getInstance2();
        System.out.println(singleton3==singleton4);//测试判断是否相等,结果应为true
        System.out.println(singleton3.hashCode()==singleton4.hashCode());//测试判断hashcode地址是否相等  结果应为true

    }
}
class Singleton2{
    //构造器私有化,外部不能new
    private Singleton2() { }
    static {//在静态代码块中,创建单例对象
        instance2=new Singleton2();
    }
    //本类内部创建对象实例
    private static  Singleton2 instance2 ;

    //对外提供一个公有的静态方法,返回实例对象
    public static Singleton2 getInstance2(){
        return instance2;
    }

}

懒汉式

调用的时候才加载
懒汉式(线程不安全)
步骤是一样的,注意看注释

//懒汉式,线程不安全方式
public class Layzsingleton {
    public static void main(String[] args) {//测试
        Singletonlazy singletonlazy=Singletonlazy.getInstance();
        Singletonlazy singletonlazy2=Singletonlazy.getInstance();
        System.out.println(singletonlazy==singletonlazy2);
        System.out.println(singletonlazy.hashCode()==singletonlazy2.hashCode());

    }
}
class Singletonlazy{
    private Singletonlazy(){}
    private static volatile Singletonlazy instance ;
    //提供一个静态的公有方法,当时用该方法时,才创建instance;
    public static synchronized Singletonlazy getInstance(){
        if (instance==null) {
            instance=new Singletonlazy();
        }
        return instance;
    }
}

优缺点说明:
起到了Lazy Loading的效果,但只能在单线程使用
如果在多线程下,一个线程进入if(instance==null)的时候,还未来得及执行new对象,另一个线程也通过这个判断语句,就会产生多个实例

第二种懒汉式(线程安全,双重检索)重点

//双重检查
public class lazySungletonave {
    public static void main(String[] args) {
        SingletonSave s=SingletonSave.getInstancesave();
        SingletonSave s2=SingletonSave.getInstancesave();
        System.out.println(s==s2);
        System.out.println(s.hashCode()==s2.hashCode());

    }
}
class SingletonSave {
    private SingletonSave(){

    }
    private static volatile SingletonSave instancesave;//关键字volatile,先初始化完成后再赋值给实例(记住这句话就好了)
    //提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,解决懒加载
    public static  SingletonSave getInstancesave(){
        if (instancesave==null) {//在第一次判断instancesave是不是已经有了,没有就创建,有就直接返回下面return 的方法
            synchronized (SingletonSave.class){
                if (instancesave==null) {
                    instancesave=new SingletonSave();
                }
            }
        }
        return instancesave;//理解的意思指已经创建过一次实例后,第二次判断,直接返回已经创建好了的方法,
        // 第一个已经创建的实例依旧在内存中,直接返回调用
    }
}

结论:双重检索double-check概念是多线程开发经常使用的,进行两次if语句的判断,就可以保证线程的安全

到此我们总结两点:

1.饿汉式这种方式加载类对象,我们称作:预先加载方式。
2.懒汉式这种方式加载类对象,我们称作:延迟加载方式。

volatile解释,代码注释DLC懒汉式2:

//懒汉式单例
public class Lazyman {

    private Lazyman(){
        System.out.println(Thread.currentThread().getName()+"ok");

    }
    private volatile static Lazyman lazyman;

    public static Lazyman getInstance(){
        if (lazyman==null) {
            synchronized (Lazyman.class){
                if (lazyman==null) {
                    lazyman=new Lazyman();//不是一个原子性操作
                    /***
                     * 1,分配内存空间
                     * 2,执行构造方法,初始化对象
                     * 3,把这个对象指向这个空间
                     *
                     * 123
                     * 132 A
                     * 线程B:会认为lazyman!=null,会直接return lazyman;此时,A还没有完成构造,
                     * 所以lazyman要避免指令重排的要加上volatile
                     */
                }
            }
        }

        return lazyman;
    }

    //多线程并发
    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                Lazyman.getInstance();
            }).start();

        }
    }
}

静态内部类方式:

//静态内部类方式
public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static class InnerClass{
        private static final Holder HOLDER=new Holder();

    }
}

单例会被破坏,反射与枚举文章开头跳转
到这里结束,觉得用心,点个关注呗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值