单例模式应用场景_单例模式的7种实现方式及反射,序列化破坏单例模式怎样防止?...

概述

e229ff161da4dfd3a194cb0359a03689.png

本文主要记录单例模式各种实现方式。主要包含以下内容

  • 单例模式含义
  • 单例特点
  • 单例缺点
  • 应用场景
  • 单例实现方式
  • 饿汉模式 (静态方法,静态块,枚举)
  • 懒汉模式(单线程,线程安全,双重验证,静态内部类)
  • 反射破环单例
  • 单例序列化

单例模式含义

97df0aa2f0c2516d5c38f15047fc776a.png

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象

特点

  • 单例模式限制了类的实例化,单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。
  • 避免对共享资源的多重占用

劣势

  • 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
  • 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”

应用场景

  • 要求生产唯一序列号。
  • WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。


单例实现方式

为了实现Singleton模式,我们有不同的方法,但是所有方法都具有以下共同概念。

  • 私有构造函数,用于限制该类从其他类的实例化。
  • 同一类的私有静态变量,是该类的唯一实例。
  • 返回类实例的公共静态方法,这是外部世界获取单例类实例的全局访问点。
bb96f8f7d870d2e4037bde3771d85799.png

饿汉模式

静态方法初始化单例类实现

在类加载时,就进行构建,急切初始化。

  • 优点

简单。创建单例模式最简单的一种方式。

线程安全。

速度快。由于类加载时已经进行初始化,调用速度快。

  • 缺点

浪费资源。没有调用对象,也会创建。

没有异常处理。

public class Singleton {  private static final Singleton instance = new  Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; }}

静态块实现

实现方式与急切创建类似,在急切创建基础上添加了异常处理。

42e0bbb03bd0e657a0c03d243f2c0e4b.png

枚举方式

  • 优点

创建枚举默认就是线程安全的。

防止反序列化导致重新创建新的对象。保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量)。

effective java 中推荐。

  • 不足

灵活性不足

public enum Singleton { INSTANCE;}

懒汉模式(惰性模式)

实现单例模式的惰性初始化方法在全局访问方法中创建实例

  • 优点

在单线程中可以实现按需加载。

  • 不足

不适用于多线程。 多线程时,可能存在破环单例模式情况。

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

线程安全单例实现

创建线程安全的单例类的更简单方法是使全局访问方法时加锁,实现同步,以便一次只能有一个线程执行此方法

  • 优点

保证多线程下正常运行。

  • 不足

性能低下。每次调用时都会进行synchronized ,引起阻塞,导致性能低下。

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

双重校验

为了在多线程环境下,不影响程序的性能,不让线程每次调用方法时都加锁,而只是在实例未被创建时再加锁,在加锁处理里面还需要判断一次实例是否已存在。

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

静态内部类实现 (推荐)

  • 优点
    资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法 。目前使用率最高的方法。

不需要考虑多线程同步问题。

  • 不足
    第一次加载时反应不够快 。

由于存在静态方法,在类加载时就会创建类。而且会一直存在。

public class Singleton { private Singleton() { } public static Singleton getInstance() { return SingletonHelper.INSTANCE; } private static class SingletonHelper { private static final Singleton INSTANCE = new Singleton(); }}

反射破坏单例

以上的单例模式,除枚举外,在反射时会被破坏。以下代码输出的hashcode值是不一致的。

public class SingletonTest { public static void main(String[] args) { Singleton instanceOne = Singleton.getInstance(); Singleton instanceTwo = null; try { Constructor[] constructors = Singleton.class.getDeclaredConstructors(); for (Constructor constructor : constructors) { constructor.setAccessible(true); instanceTwo = (Singleton) constructor.newInstance(); break; } } catch (Exception e) { } System.out.println(instanceOne.hashCode()); System.out.println(instanceTwo.hashCode()); }}

单例模式序列化

序列化单例类的问题在于,每当我们反序列化它时,它将创建该类的新实例。为防止破环单例模式,加入readResolve()函数。

public class SerializedSingleton implements Serializable { private SerializedSingleton() { } public static SerializedSingleton getInstance() { return SingletonHelper.INSTANCE; } protected Object readResolve() { return getInstance(); } private static class SingletonHelper { private static final SerializedSingleton INSTANCE =  new SerializedSingleton(); }}

总结

本文主要总结了单例模式实现方式。两大类,7种实现。已经反射,序列化对单例的破坏。

静态内部类或者是枚举方式是最常见与官方推荐的实现方式。如果文章对您有些,帮助请关注下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值