单例模式的学习

单例模式

 
​  对某些类只允许存在一个对象实例,并且提供一个取得其对象实例的方法【静态方法】

单例模式结构

在这里插入图片描述
​  单例(Singleton)类 声明一个名为 getInstance获取实例的静态方法来返回所属类的一个相同实例。
​  并且其构造方法需要对外隐藏,即getInstance()是唯一能获取该实例的唯一方法
 

Java中的体现:

​  1.在类中添加一个私有静态成员变量用于保存单例实例。

​  2.声明一个公有静态构建方法用于获取单例实例。

​  3.在静态方法中实现"延迟初始化"。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例。

​  4.将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。

​  5.检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用
 

 

分类

 

饿汉式(静态常量)

​  饿汉式是直接在内存new该实例

class Singleton{
    //2.本类内部创建对象实例
    private final static Singleton instance = new Singleton();

    //1.构造器私有化
    private Singleton(){

    }

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

public class SingletonTest {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = instance1;

        System.out.println(instance1 == instance2);//true
        System.out.print(instance1.hashCode()+"|");
        System.out.println(instance2.hashCode());
        //1325547227|1325547227
    }
}

 
优点:

​ ​  在类装载时完成实例化,避免了线程同步问题

缺点:

​​  未达到Lazy Loading的效果

​​  若未使用过这个实例,则会造成内存的浪费
 

饿汉式(静态代码块)

class Singleton{
    private static Singleton instance;


    private Singleton(){
    }

    //在静态代码块中,创建单例对象
    static {
        instance = new Singleton();
    }


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

 

懒汉式(线程不安全)

​  懒汉式是只有get该实例时才在内存中new该对象。

class Singleton{
    private static Singleton instance;


    private Singleton(){
    }

    //提供一个静态的公有方法
    //当使用该方法时,采取创建instance
    public static Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

​ ​  拥有Lazy Loading的效果。但只能在单线程下使用

​​  在多线程时,一个进程进入if判断,尚未往下执行,其他线程也通过了这个判断语句,则会产生多个实例

在实际开发中,不使用这种方式

 

懒汉式(线程安全,同步方法)

class Singleton{
    private static Singleton instance;


    private Singleton(){
    }

    //加入同步处理代码,解决线程不安全的问题
    public static synchronized Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

​ ​  解决了线程不安全的问题

​ ​  但效率太低了。当每个线程都想获取该实例,进入getInstance方法都需要进行同步,而该方法仅需要执行一次便足够了,若后面需要获取该实例们直接return即可。

 

懒汉式(线程不安全,同步代码块)

class Singleton{
    private static Singleton instance;


    private Singleton(){
    }


    public static Singleton getInstance(){
        if (instance == null){
            //加入同步处理代码,解决线程不安全的问题
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}

​  本意是对上一种的改进

​ ​  但并不能起到线程同步的作用。因为当进入if判断时,容易产生多个实例。
 

双重检查

class Singleton{
    private static volatile Singleton instance;

    private Singleton(){
    }

    public static Singleton getInstance(){
        if (instance == null){
            synchronized (Singleton.class) {
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

​ 优缺点说明:

​ ​  双重检查时多线程开发中常使用的。进行两次if判断,则保证了线程安全。

​ ​  实例化代码只进行一次。效率提高。

 

静态内部类

​  静态内部类的特点:

​ ​  ​  进行类装载时,不会被装载。保证懒加载。

​ ​  ​  当去调用时,则会去装载该类【而且只有一次】。保证线程安全

class Singleton{

    private Singleton(){
    }

    //静态内部类,有一个静态属性
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }

    //返回静态内部类的静态属性
    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}

优缺点:

​ ​  采用了类装载机制来保证初始化只有一个线程

​​  静态内部类在外部类装载时并不会立即实例化,而仅有当需要实例化,调用getInstance()时,才会装载SingletonInstance类,从而完成实例化

 

枚举

enum Singleton{
    INSTANCE;

    public void sayOK(){
        System.out.println("This is OK.");
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;

        System.out.println(instance1 == instance2);//true
        System.out.print(instance1.hashCode()+"|");
        System.out.println(instance2.hashCode());
        //1325547227|1325547227

        instance1.sayOK();//This is OK.
    }
}

优缺点:

​ ​  借助枚举实现,不仅避免多线程同步问题,而且还能防止反序列化重现创建新的对象。

​ ​  👍也是Effective Java作者Josh Bloch 提倡的方式
 

单例模式的应用场景

​  如果程序中的某个类对于所有客户端只有一个可用的实例,可以使用单例模式。

​  如果你需要更加严格地控制全局变量,可以使用单例模式。
 

优缺点

​  优点:
​  ​  ✔️你可以保证一个类只有一个实例
​  ​  ✔️你获得了一个指向该实例地全局访问节点
​​    ✔️仅在首次请求单例对象时对其初始化

​  缺点:
​  ​  ❌单例模式可能会掩盖不良设计,例如程序各组件之间相互了解过多等。
​  ​  ❌该模式在多线程环境下需要进行处理,因此需要避免多个线程多次创建单例对象。
​  ​  ❌单例的客户端代码单元测试可能会比较困难, 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的, 而且绝大部分语言无法重写静态方法, 所以你需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码, 或者不使用单例模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值