设计模式-单例模式

一、概念

单例模式(Singleton Pattern)是 Java 中较为简单的设计模式之一。该模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类需要提供访问唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

梳理单例模式的3个重点:
1、类所对应的实例只能有一个。
2、类的构造方法不能被外界调用。
3、需要为外界提供一个获取该类唯一实例的方法。

二、饿汉模式实现单例

/**
 * 饿汉式的特点:
 * 1.线程安全
 * 2.没有锁,效率高
 * 3.缺点是容易产生垃圾对象,因为该类被加载就会被实例化,就算没有调用getInstance()也会产生对象
 */
public class Singleton {
    //创建该类的唯一实例
    private static Singleton instance = new Singleton();  
    //私有化构造器,不让外界创建对象
    private Singleton (){}
    //为外界提供一个静态的公开方法,用于获取该类的实例
    public static Singleton getInstance() {  
        return instance;
    }  
}

叫饿汉模式的原因是:就算不调用getInstance()获取实例,也可能会创建类的实例。

三、懒汉模式实现单例

顾名思义:调用的时候才创建,不调用不创建。
1.懒汉式,线程不安全

//方式1:
public class Singleton {
    //为防止类加载时就创建实例,这里仅声明不赋值,在调用的时候再进行赋值
    private static Singleton instance;
    //私有化构造器防止被外界调用
    private Singleton (){}
    //为外界提供获取实例的方法
    public static Singleton getInstance() {
        instance = new Singleton(); //调用时再赋值
        return instance;
    }
}
//分析:很明显getInstance()方法是有问题的,因为每调用一次就会创建新的实例并返回,
//无法保证单例,所以需要对getInstance()进行修改。

//方式2:
public class Singleton {
    private static Singleton instance;
    private Singleton (){}
    
    public static Singleton getInstance() {
        //增加判断:instance等于null时创建对象返回,不等于null表示对象已经存在则直接返回
        if(instance==null){
            instance = new Singleton();
        }
        return instance;
    }
}
//分析:这个getInstance()就可以了吗?
//答案:不可以,因为在多线程下运行还是会有问题。所以我们来解决一下。

2.懒汉式,线程安全

public class Singleton {
    private static Singleton instance;
    private Singleton (){}
    //为方法加锁synchronized,这样就能保证该方法一个线程执行完毕,另一个才可以执行
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
//分析:虽然达到了线程安全的目的,但是锁范围有点大,效率较低。

再进行修改,优化一下效率:

/**
 * (双检锁/双重校验锁)实现懒汉单例,保证线程安全。
 *  核心在于:缩小锁的范围,同时增加两次判断,线程安全的同时保证了效率。
 */
public class Singleton {
    private static Singleton instance;
    private Singleton (){}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

四、静态内部类实现单例

/**
 * 这种方式线程安全的同时,还实现懒加载。
 * 这种方式是 Singleton 类被装载了,instance 不一定被初始化。
 * 因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,
 * 才会显式装载 SingletonHolder 类,从而实例化 instance。
 */
public class Singleton {
    //在内部类中创建该类的实例
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    //私有化构造器
    private Singleton (){}
    //提供获取实例的方法
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

到这里,我们介绍了几种用于实现单例的方案,感觉上还可以,但是要注意Java的反射机制会打破这一切,因为我们实现单例的思路是私有化构造器,不然外界调用,但是反射就可以调用到私有构造。

五、枚举实现单例

/**
 * 枚举实现单例
 * 1.枚举自带私有无参构造,这恰好满足我们的需求,防止外部调用
 * 2.枚举是不允许反射去调用构造器的,反射在通过newInstance创建对象时,
 * 会检查该类是否enum修饰,如果是则抛出异常,反射失败。
 */
public enum Singleton {
    INSTANCE;
}

点滴积累,只为天天向上

  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值