Singleton Pattern 单例模式自学笔记

文章背景
单例模式自学笔记, 自学java到了SHH框架阶段, 想看源码却看不懂, 恶补一下设计模式.

Singleton Pattern 单例模式

概念:

1.是一种用来创建对象的模式.
2.一个类仅有一个实例, 并且提供一个访问它的全局访问点.

使用范围

在创建对象时, 如果需要创建的对象非常复杂或者占用内存非常大, 一般采用此模式.

好处:
1.因为对象复杂可以简化创建细节, 使用者更方便的创建该类.
2.保证只有一个实例, 减少内存开销, 并且可以控制类的创建时刻.

实现方法

常见对象的创建方式

new
反射(reflect)
反序列化(deserialize)
克隆(clone)
公开的静态变量
公开的静态方法

实现类型

| 立即or延迟 | 线程安全 | 效率 | 实现难度 | 自动处理序列化问题
——— | ————- | ———— | ———— | ———- | —
饿汉式 | 立即 | 安全 | | 简单 |
懒汉式 | 延迟 | 不安全 | | 简单 |
加锁懒汉式 | 延迟 | 安全 | 低 | 中等 |
双重验证式懒汉式 | 延迟 | 安全 | | 复杂 |
类加载方式 | 延迟 | 安全 | | 简单 |
枚举实现 | 立即| 安全 | | 简单 | 能

饿汉式:

public class Dog {
//  创建唯一的实例,在类加载时即被创建不能做到延迟创建
    private static final Dog ONLYDOG = new Dog();
//  私有化构造器,防止被外部new
    private Dog() {}
//  创建全局访问点,中间可以添加其他代码比公开静态变量的方式要更灵活
    public static Dog getDog(){
        //此处可以添加控制代码
        return ONLYDOG;
    }
    
//可以写个测试类,结果为true
public class Test {
    
    public static void main(String[] args) {
        Dog a = Dog.getDog();
        Dog b = Dog.getDog();
        System.out.println(a == b);
    }

}

懒汉式&加锁懒汉式

public class Cat {
//  唯一实例变量
    private static Cat ONLYCAT;
    //  屏蔽外部new
    private Cat(){
        
    }
//  提供全局访问点
    public static Cat getCat(){    //未加锁,线程不安全
//  public synchronized static Dog getDog(){  //加锁,需要排队等待
        if (ONLYCAT != null) {
            ONLYCAT = new Cat();
        }
        return ONLYCAT;
    }
}

双重验证式

public class Cat {
//  创建唯一实例
    private static Cat ONLYCAT;
    //  屏蔽外部new
    private Cat(){
        
    }
//  提供全局访问点
    public static Cat getCat(){
    //...
        if (ONLYCAT != null) {
            synchronized (Cat.class) {//只对new的部分加锁,并且在此验证,线程安全并且效率较高.
                if (ONLYCAT != null) {
                    ONLYCAT = new Cat();                    
                }
            }
            
        }
        return ONLYCAT;
    }
}

内部类加载

涉及到序列化相关内容参考博客

import java.io.ObjectStreamException;
import java.io.Serializable;

public class Cat implements Serializable, Cloneable{

    
            //  屏蔽外部new
    private Cat(){
    }

            //  静态内部类,加载内部类的时候jvm会保证线程安全
    private static class InstanceHolder{
        static private Cat ONLYCAT = new Cat();
    }
            //  提供全局访问点
    public static Cat getCat(){
        return InstanceHolder.ONLYCAT;
    }
            //  如果必须实现序列化和克隆,需要如下代码防止反序列化和克隆来新建实例
            //      serializable通过readResolve方法解决,clone通过覆盖clone方法解决
    
    private Object readResolve() throws ObjectStreamException{
        return getCat();
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return getCat();
    }
    
}

枚举方式实现

相关博客

public enum Tiger {\\相当于 public class Tiger extends Enum{
    ONLYTIGER;\\单实例
    private String a = "I am ";\\枚举中也可以定义变量和方法
    private String b = "the ONLYTIGER!";
    public String whoAreYou(){
        return a+b;
    }
}

测试

public class Test {
    
    public static void main(String[] args) {
        System.out.println(Tiger.ONLYTIGER.whoAreYou());
        Tiger a = Tiger.ONLYTIGER; 
        Tiger b = Tiger.ONLYTIGER;
        System.out.println(a == b);
    }

}

测试结果
I am the ONLYTIGER!
true

总结

饿汉式:仅一个缺点,没有延迟加载的需求都可以使用
懒汉式&加锁懒汉式: 有双重验证和内部类两种更好的方式,基本用不到
双重验证: 实现的过程中控制比较多,有时能巧妙的实现特殊需求
内部类: 需要延迟加载的第一选择, 无法满足需求时可以考虑双重验证
枚举: 很多文章说是目前最好的单例模式实现. 对此有一些质疑, 枚举毕竟不能延迟加载,我认为最佳应用应该是饿汉式的替代, 比饿汉式更加优雅.

    原文作者:sunshujie1990
    原文地址: https://www.jianshu.com/p/c6380bc3c8ae
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞