再学单例模式:著名的延迟加载实现方式

       单例模式的实现,那几种常见实现大部分人都滚瓜烂熟了,其中就:synchronize + 双重检测 + volatile  这种延迟加载(懒加载)方式有点不好理解,涉及到的东西有点深,但是网上也有非常多的文章进行过详细的讲解了,这里就不赘述啦,如果还不够熟悉的话可以找下相关的文章,这篇文章就讲比较少见的、跟类初始化有关的延迟加载实现方式,这种方式其实在我之前多次学单例模式的时候都有见过,但是都没有从根本上明白,马马虎虎的,这次是因为自己相关学习的积累,再次看到的时候突然就想明白了,我想有很多人应该也还是半知半解吧?下面我们来看下

一、先得知道类初始化相关知识

       关于类初始化相关的详细内容可以看下我很久之前写的:Java类的生命周期和加载机制,不想看引文也可以直接往下看,我这里就说几点关键的点:

  1. 一个类被加载到堆内存(方法区)后,并不一定会立即进行初始化操作
  2. 触发类初始化的其中一个条件:访问某个类的静态方法时,如果类还没被初始化就会进行初始化(详情请看引文
  3. java项目启动期间,一个类只会被初始化一次
  4. 类初始化是线程安全的
  5. 简单说下类初始化做的事情:为类的静态变量赋值指定的初始值(还有就是:执行类中静态代码块的类容,详情请看引文

二、著名的延迟加载实现方式

       我反手就是直接把代码贴上:

public class Singleton {

    // 声明禁止new,这里容易忽略,别漏啦!!!
    private Singleton(){}

    public static Singleton getInstance(){
        return LazyHolder.INSTANCE;
    }

    public static class LazyHolder{ // 这个子类不是static也可以
        static final Singleton INSTANCE = new Singleton();
    }
}

       根据前面提到知识,是不是恍然大悟,明白这种实现方式的原理了?先好好思考一下,如果还不明白,评论区见!

       ps:如果你哪次面试的时候,面试官问到了这种实现方式,你是不是可以起飞了?:)

三、油然而生的疑惑

       通过上面的吸收理解之后,我们明白了和类的初始化密切相关,但是我发现我们非常常见的非延迟加载(饿汉式)难道不也是延迟加载吗?我来看下饿汉式的实现代码:

public class Singleton_2 {
    private static final Singleton_2 instance = new Singleton_2();

    private Singleton_2() {}

    public static Singleton_2 getInstance() {
        return instance;
    }
}

对比上面的延迟加载实现方式,如果这个Singleton_2类被加载到堆内存,但是如果我们一直不调用getInstance不也是没有初始化,也就没有创建这个Singleton_2 类实例 不是吗?那还要这么一层封装干嘛?所以Singleton_2 难道不也是非延迟加载的实现方式嘛?难道网上说的一直是错的?

好好想想,是不是想的没错?我一开始也是疑惑,这难道不是一样嘛!但是确实不一样,我们来看下

四、解惑!

       我把两个单例的实现类稍微修改一下,都只添加一个额外的类方法(静态方法),看下面代码:

// 延迟加载
public class Singleton {
    private Singleton(){
        System.out.println("Singleton 进行了实例化");
    }

    public static Singleton getInstance(){
        return LazyHolder.INSTANCE;
    }

    public static class LazyHolder{ // 这个子类不是static也可以
        static final Singleton INSTANCE = new Singleton();
    }

    public static void someStaticMethod(){
        System.out.println("执行了其中的一个静态方法");
    }
}


// 饿汉式
public class Singleton_2 {
    private static final Singleton_2 instance = new Singleton_2();

    private Singleton_2() {
        System.out.println("Singleton_2 进行了实例化....");
    }

    public static Singleton_2 getInstance() {
        return instance;
    }

    public static void someStaticMethod(){
        System.out.println("执行了其中的一个静态方法");
    }
}

如果你前面对我提出的异或有仔细全面的了解,其实你看到这两个改造之后的代码应该可以很快解惑了!我们继续往下看,我贴出一个客户端调用的代码:

public class client {

    public static void main(String[] args) {
        Singleton.someStaticMethod();
        System.out.println("---------------------------");
        Singleton_2.someStaticMethod();
    }
}

对应的输出结果:

执行了其中的一个静态方法

--------------------------------------------

Singleton_2 进行了实例化....

执行了其中的一个静态方法

到这里想必应该明白上面提到的疑惑其实是错的!

我再总结一下:我们说的饿汉/懒汉式,指的是这个单例类的实例对象什么时候被实例化,真正等需要这个类的实例对象时才构造实例 这种就是懒汉式,没有真正使用到但却构建了 这种属于饿汉式。就比如上面的Singleton 和 Singleton_2,其实我们调用他们的someStaticMethod类方法时(或者访问他们的静态变量时),确实都会触发这两个类进行初始化,但是不同的是,Singleton_2的实现方式,在初始化的时候会构造类实例对象,而我们此时并没有真正需要它的实例对象,而只是用到了他的静态方法/类方法 而已。所以Singleton_2就是饿汉式,Singleton通过内部类的一次封装,实现了延迟加载!

       到这里本篇文章就结束啦,希望对你有所帮助!感谢阅读~

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值