java设计模式之单例模式

本文介绍了Java设计模式中的单例模式,探讨了如何创建一个保证只有一个实例的类,包括通过私有构造方法、静态私有属性以及双重检查锁定的懒汉式实现。还讨论了枚举作为单例模式的实现方式以及 volatile 关键字在防止指令重排序中的作用,以确保线程安全。
摘要由CSDN通过智能技术生成

什么是单例模式?

一个类只能创建一个对象,堆内存中就只开辟一个空间,有效减少内存占用空间

如何设计单例模式?

  1. 创建一个对象是调用该类的构造方法,因此应该控制构造方法不能让别人随意调用构造方法创建对象,因此设计私有的构造方法。
  2. 由于私有的构造方法后,对象不能随意创建,因此我们需要在类的内部进行创建唯一一个对象,否则别人就拿不了对象啦。
  3. 需要考虑在类内部成员中(属性、块、构造方法、方法)哪个位置创建该类的对象合适,由于块是没有返回值,因此我们创建的对象不能传递给外面,因此不能使用;由于构造方法本身就是创建对象,如果在构造方法中创建对象会造成栈内存溢出,相当于一直调用构造方法创建对象,对象永远创建不完,堆内存中的空间比栈内存稍大,执行构造方法在栈内存中临时开辟空间执行,因此栈内存溢出,而且构造方法以及私有了;由于方法每次执行一次都在栈内存中开辟一个临时的执行空间执行创建对象,这样不能保证创建的对象唯一。
  4. 综上所述,块,构造方法,块这些成员变量都不满足单例的条件,因此可以考虑属性,属性中直接给引用赋值创建对象显然不行,还会造成栈内存溢出,一个对象不断的创建(调用构造方法,临时在栈内存开辟空间执行),永远都创建不完,因此需要加上static修饰属性,保证该属性只有一份,而且属性应该私有,不能让外部访问,安全。
  5. 设计好唯一一个对象后,由于外部无法使用创建的对象,我们需要设计一个方法返回给外部,public修饰、static静态修饰,为了外部能通过类名调用方法,而不用创建对象,将唯一一个单例对象传递出去使用。

单例类:

public class Singleton {

    //饿汉式(立即加载方式)
    //属性私有防止外部直接访问属性,不安全,静态保证该对象存在唯一一个
    private static Singleton singleton=new Singleton();

    //构造方法私有,保证该对象的创建不能外部创建
    private Singleton(){}

    //公有静态修饰是确保外部能够直接通过类名访问,不用通过创建对象
    public static Singleton getInstance(){
        return singleton;
    }

}

测试示例:

public class TestMain {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance==instance1);
    }
}

结果:
在这里插入图片描述
单例模式内存图:
在这里插入图片描述

以上单例设计,在类加载的时候,单例对象已经创建完毕,如果考虑到性能问题,整个系统在执行过程中不使用,则会一直占用内存,浪费内存空间

优化设计:

public class Singleton {

    //懒汉式(延迟加载方式)
    //属性私有防止外部直接访问属性,不安全,静态保证该对象存在唯一一个
    private static Singleton singleton;

    //构造方法私有,保证该对象的创建不能外部创建
    private Singleton(){}

    //公有静态修饰是确保外部能够直接通过类名访问,不用通过创建对象
    public static Singleton getInstance(){
        //如果方法没有调用则不进行创建,什么时候用再创建单例对象
        if (singleton==null){
            singleton=new Singleton();
        }
        return singleton;
    }
}

测试结果:
在这里插入图片描述
懒汉式的延迟加载创建单例对象,在多个线程并发执行,一个线程刚判断完是null,此时还没调用方法创建对象,则另外一个线程也进行判断是null,此时会造成一个空间进行两次创建对象赋值的过程。
在这里插入图片描述
设计优化:加入线程锁,双重判断

public class Singleton {

    //懒汉式(延迟加载方式)
    //属性私有防止外部直接访问属性,不安全,静态保证该对象存在唯一一个
    private static Singleton singleton;

    //构造方法私有,保证该对象的创建不能外部创建
    private Singleton(){}

    //公有静态修饰是确保外部能够直接通过类名访问,不用通过创建对象
    public static Singleton getInstance(){
        //先判断该对象是否被创建
        if (singleton==null){
            //如果没有创建,则用线程锁锁住当前类模板(防止创建对象),其他线程不能操作
           synchronized (Singleton.class){
               //再进行判断对象是否已经被赋值(双重判断)
               if (singleton==null){
                   //如果对象还未创建,则创建对象
                   singleton=new Singleton();
               }
           }
        }
        return singleton;
    }
}

测试结果:
在这里插入图片描述
上面设计解决了性能问题,但对象的开辟和赋值的过程中可能会产生指令重排序,JVM虚拟机可能会提高性能,对对象的加载和赋值产生顺序颠倒,导致先赋值后加载,导致赋值到的对象是空的,因此属性加上volatile,可以不被JVM指令重排序。

public class Singleton {

    //懒汉式(延迟加载方式)
    //属性私有防止外部直接访问属性,不安全,静态保证该对象存在唯一一个
    //volatile防止虚拟机指令重排序
    private volatile static Singleton singleton;

    //构造方法私有,保证该对象的创建不能外部创建
    private Singleton(){}

    //公有静态修饰是确保外部能够直接通过类名访问,不用通过创建对象
    public static Singleton getInstance(){
        //先判断该对象是否被创建
        if (singleton==null){
            //如果没有创建,则用线程锁锁住当前类模板(防止创建对象),其他线程不能操作
           synchronized (Singleton.class){
               //再进行判断对象是否已经被赋值(双重判断)
               if (singleton==null){
                   //如果对象还未创建,则创建对象
                   singleton=new Singleton();
               }
           }
        }
        return singleton;
    }
}

枚举Enum本质也是一个单例模式设计

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值