设计模式之单例

单例模式确保一个类只有一个实例,并提供全局访问点。它有饿汉式(类加载时创建实例)、懒汉式(线程不安全和安全的实现)、双重检查、静态内部类和枚举等实现方式。饿汉式和静态内部类实现是线程安全的,而枚举是推荐的实现方式,因为它既安全又防止反射攻击。
摘要由CSDN通过智能技术生成

单例模式


1. 定义

单例模式(Singleton Pattern)保证一个类仅有一个范例,并提供一个访问它的全局访问点。

单例模式是最简单、最常用的设计模式之一,属于创建型模式。

单例模式只涉及到一个类,该类负责创建自己的对象,而且确保只有单个对象被创建。另外,单例类提供了一种访问其唯一对象的方式,客户端可以直接使用,无需范例化该类的对象。

三个特点

  • 1、单例类只能有一个范例。
  • 2、单例类必须自行创建自己的范例。
  • 3、单例类必须向系统的其它对象提供这一范例。

2. 单例模式的要点

1)目的

单例模式中的单例类负责创建自己的对象,而且确保只有单个对象被创建,并提供全局访问其对象的方法。

单例模式可以达到三个目的:

  • 1、控制资源的使用,通过线程同步控制资源的并发访问。
  • 2、控制范例产生的数量,达到节省资源的目的,同时提高运行效率。
  • 3、单例对象可以作为通信媒介,实现数据共享的,在多个线程或进程之间进行通信。

2)优点

  • 1、单例模式中的单例类只存在一个对象,可以节约系统资源。
  • 2、单例模式在需要频繁创建和销毁的对象的场景中,可以提高系统的性能。
  • 3、单例模式可以避免对共享资源的多重占用。

3)缺点

  • 1、单例模式中不适用于变化的对象,不能保存多个不同的状态。
  • 2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
  • 3、单利模式中没有抽象层,因此单例类的扩展比较困难。

4)使用场景

  • 1、资源控制。
  • 每台计算机可连接多个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。
  • 常见的数据库连接池,整个系统通常只需要一个对象,无需范例化多份数据。
  • 2、数据同步
  • 网站的计数器,一般采用单例模式实现数据同步,否则计数可能会被不断覆盖。

3.实现

1.)饿汉
静态变量式
public class singleton {
    public static void main(String[] args) {
        //测试是否只创建了一个实例
        Sing sing1 = Sing.getSing();
        Sing sing2 = Sing.getSing();
        System.out.println(sing2==sing1);
        // true
    }
}
class Sing{
    //1.构造器私有,外部无法new
    private Sing(){}
    //2.创建实例
    private final static Sing sing = new Sing();
    //3.对外暴露获取实例方法
    public static Sing getSing(){
        return sing;
    }
}

说明

  • 优点,写法简单,在类的装载时就发生实例化,避免了线程同步问题!
  • 缺点,类被装载就会被实例化,所以若没有使用过该实例,会造成资源浪费
  • 这种静态变量式可用,但可能造成资源浪费
静态代码块式
public class singleton {
    public static void main(String[] args) {
        //测试是否只创建了一个实例
        Sing sing1 = Sing.getSing();
        Sing sing2 = Sing.getSing();
        System.out.println(sing2==sing1);
        // true
    }
}
class Sing{
    //1.构造器私有,外部无法new
    private Sing(){}
    //2.在本类中创建实例
    private  static Sing sing ;
    static {
        sing = new Sing();
    }
    //3.对外暴露获取实例方法
    public static Sing getSing(){
        return sing;
    }
}

说明

  • 和静态变量式类似,实例化放在了静态代码块中,也在类装载时被实例化,同样可能浪费资源
2.)懒汉()安全的,不安全的
线程不安全式(不推荐)
public class singleton {
    public static void main(String[] args) {
        //测试是否只创建了一个实例
        Sing sing1 = Sing.getSing();
        Sing sing2 = Sing.getSing();
        System.out.println(sing2==sing1);
        System.out.println(sing2.hashCode());
        System.out.println(sing1.hashCode());
        // true
    }
}
class Sing{
    //1.构造器私有,外部无法new
    private Sing(){}
    //2.在本类中创建实例
    private  static Sing sing ;

    //3.对外暴露获取实例方法
    public static Sing getSing(){
        if(sing==null){
            sing = new Sing();
        }
        return sing;
    }
}

说明:

在多线程操作中,会有多个线程进入if语句,存在线程安全问题,不推荐使用该方法

线程安全式(不推荐)

加入同步处理代码可以解决线程安全问题,但是效率很低,同样不推荐使用

public static synchronized Sing getSing(){
    if(sing==null){
        sing = new Sing();
    }
    return sing;
}
同步代码块式
class Sing{
    //1.构造器私有,外部无法new
    private Sing(){}
    //2.在本类中创建实例
    private  static Sing sing ;

    //3.对外暴露获取实例方法
    public static synchronized Sing getSing(){
        if (sing==null){
            synchronized (Sing.class){
                sing = new Sing();
            }
        }
        return sing;
    }
}

说明:同样不推荐,多线程时,会造成多个实例的创建

3.)双重检查
class Sing{
    //1.构造器私有,外部无法new
    private Sing(){}
    //2.在本类中创建实例
    private  static Sing sing ;

    //3.对外暴露获取实例方法
    public static synchronized Sing getSing(){
        if (sing==null){
            synchronized (Sing.class){
                if(sing==null){
                    sing = new Sing();
                }
            }
        }
        return sing;
    }
}

说明:

  • 推荐使用,两次检查,可以保证线程安全,效率较高
3.)静态内部类 (推荐)
class Sing{
    //1.构造器私有,外部无法new
    private Sing(){}
    //2.在本类中创建实例
    private static class SingInstance {
        private  static final Sing SING=new Sing() ;
    }

    //3.对外暴露获取实例方法
    public static synchronized Sing getSing(){

        return SingInstance.SING;
    }
}

说明:

  • 当调用geiSing方法时,导致静态内部类被装载 ,随之创造实例
  • 而且在类Sing被装载时,不会创建实例,只有在调用getSing方法才会完成实例化
  • 线程安全的,类的初始化jvm保证线程安全
  • 推荐使用!!
4.)枚举(推荐)
public class singleton {
    public static void main(String[] args) {
        //测试是否只创建了一个实例
        Sing2 sing1 = Sing2.INSTANCE;
        Sing2 sing2 = Sing2.INSTANCE;
        System.out.println(sing2==sing1);
    }
}
enum Sing2{
    INSTANCE;
}

说明:

  • 最推荐使用!!!

4.jdk源码中的Runtime

是使用的是单例模式中的饿汉式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值