Java单例模式

1. 概念

单例模式,是一种常用的软件设计模式。单例对象的类必须保证只有一个实例存在。

很多时候我们只需要一个全局对象,可以避免多个对象占用内存资源,也可以方便我们管理一些配置信息。通常我们用getInstance来获取该对象单例实例

2. 单例模式的类型

对单例的实现,有两种构建方式:

  • 懒汉式:指全局的单例实例在第一次被使用时构建。

  • 饿汉式:指全局的单例实例在类装载时构建。

3. 懒汉式

先来看一下懒汉式单例的实现方式。

public class Single {
    private static volatile Single instance;
    private Single() {}
    public static Single getInstance() {
        if (instance == null) {
            synchronized (Single.class) {
                if (instance == null) {
                    instance = new Single();
                }
            }
        }
        return instance;
    }
}
Kotlin方式
public DoubleCheckSingleton private constructor {
    companion object {
        val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            DoubleCheckSingleton() 
        } 
    }
}

指令重排:计算机为了提高执行效率,会做的一些优化,在不影响最终结果的情况下,可能会对一些语句的执行顺序进行调整
volatile :禁止指令重排,在它的赋值完成之前,不会调用读操作
synchronized 同步锁:会强制除当前之外的所有线程等待,实际上会对程序的执行效率造成负面影响
private:构造器改为私有的,这样能够防止被外部的类调用

注意其中有两次if (instance == null)的判断,这个叫做『双重检查 Double-Check』:
* 第一个if (instance == null),其实是为了解决效率问题,只有instance为null的时候,才进入synchronized的代码段——大大减少了几率。
* 第二个if (instance == null),是为了防止可能出现多个实例的情况。

4. 饿汉式

饿汉式单例的实现方式。

public class Single {
    private static final Single instance= new Single();
    private Single() {}
    public static Single getInstance() {
        return instance;
    }
}
Kotlin方式
饿汉式在Kotlin中,只需要一个object修饰符就行了,这就是Kotlin非常厉害的地方。
object BaseSingleton {

}

对于饿汉式来说,能避免多线程情况,但是它的实例是在类加载的时候就创建了
缺点:
1.可能由于初始化的太早,造成资源的浪费
2.如果初始化本身依赖于一些其他数据,那么也就很难保证其他数据会在它初始化之前准备好。

5.其他方式

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE= new Singleton();
    }
    private Singleton (){}
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
Kotlin方式
public InnerStarticSingleton private constructor {
    companion object {
        fun getInstance = Holder.INSTANCE 
    }
    private object Holder {
        val INSTANCE = InnerStarticSingleton ()
    }
}
  • 对于内部类SingletonHolder,它是一个饿汉式的单例实现,在SingletonHolder初始化的时候会由ClassLoader来保证同步,使instance是一个单例。
  • 同时,由于SingletonHolder是一个内部类,只在外部类的Singleton的getInstance()中被使用,所以它被加载的时机也就是在getInstance()方法第一次被调用的时候。
    它利用了ClassLoader来保证了同步,同时又能让开发者控制类加载的时机。从内部看是一个饿汉式的单例,但是从外部看来,又的确是懒汉式的实现。

6.补充 类加载顺序

public class test {                         //1.第一步,准备加载类

    public static void main(String[] args) {
        new test();                         //4.第四步,new一个类,但在new之前要处理匿名代码块        
    }

    static int num = 4;                    //2.第二步,静态变量和静态代码块的加载顺序由编写先后决定 

    {
        num += 3;
        System.out.println("b");           //5.第五步,按照顺序加载匿名代码块,代码块中有打印
    }

    int a = 5;                             //6.第六步,按照顺序加载变量

    { // 成员变量第三个
        System.out.println("c");           //7.第七步,按照顺序打印c
    }

    test() { // 类的构造函数,第四个加载
        System.out.println("d");           //8.第八步,最后加载构造函数,完成对象的建立
    }

    static {                              // 3.第三步,静态块,然后执行静态代码块,因为有输出,故打印a
        System.out.println("a");
    }

    static void run()                    // 静态方法,调用的时候才加载// 注意看,e没有加载
    {
        System.out.println("e");
    }
}
  • 类加载 在第一次创建一个类的对象或者第一次调用一个类的静态属性和方法的时候发生
  • 类加载期间,如果发现有静态属性,就给对应的静态属性分配内存空间,并赋值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值