23种设计模式介绍:
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1.1 意图
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
1.2 模式定义
单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。
1.3 模式结构
1.4 时序图
1.5 代码demo
懒汉式,饿汉式,双重校验锁式,静态内部类式,枚举式
package DesignPattern23.Singleton;
/**
* Description:
* Author:
* Date: 2019/11/6
*/
public class SingletonDemo {
public static void main(String[] args) {
}
}
/**
* 懒汉:
* 1.构造器私有化
* 2.声明一个私有的静态变量
* 3.创建一个对外公共静态方法访问该变量,如果变量没有对象,则创建 (调用时创建)
*/
class LazyJvm{
//声明一个私有的静态变量
private static LazyJvm instance = null;
//构造器私有化
private LazyJvm() {}
//创建一个对外公共静态方法访问该变量,如果变量没有对象,则创建
public static LazyJvm getInstance() {
instance = new LazyJvm();
return instance;
}
}
/**
* 饿汉式:
* 1.构造器私有化
* 2.声明静态变量+同时创建对象(类加载时创建对象)
* 3.对外提供访问属性的方法
*
*/
class HunguryJvm{
private static HunguryJvm instance = new HunguryJvm();
private HunguryJvm() {
}
public static HunguryJvm getHunguryJvm () {
return instance;
}
}
/**
* 双重校验锁式
* 1.构造器私有化
* 2.声明一个私有的静态变量
* 3.创建一个对外公共静态方法访问该变量,如果变量没有对象,则创建 (d调用时创建)
*/
class Jvm{
//声明一个私有的静态变量
private static Jvm instance = null;
//构造器私有化
private Jvm() {}
//创建一个对外公共静态方法访问该变量,如果变量没有对象,则创建
//两个 (null == instance)p判断,double Checking提高已经存在对象的效率
public static Jvm getInstance() {
if (null == instance) {
synchronized (Jvm.class) {
if (null == instance) {
instance = new Jvm();
}
}
}
return instance;
}
}
/**
* 静态内部类:
* 1.创建静态内部类
* 2.构造器私有 *
* 3.创建一个对外公共静态方法访问该变量,如果变量没有对象,则创建 (调用时创建)
*/
class Jvm2{
private static class JVMholder{
private static Jvm2 instance = new Jvm2();
}
private Jvm2() {
}
public static Jvm2 getHunguryJvm () {
return JVMholder.instance;
}
}
/**
* 枚举
*/
enum enumJvm {
INSTANCE;
}
一般情况下,不建议使用第 1 种和第 2 种懒汉方式,
建议使用静态内部类,枚举。
静态内部类:
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:一般
描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。
枚举:
JDK 版本:JDK1.5 起
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
不能通过 reflection attack 来调用私有构造方法。
1.6 总结
- 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。
- 单例模式只包含一个单例角色:在单例类的内部实现只生成一个实例,同时它提供一个静态的工厂方法,让客户可以使用它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有。
- 单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法。该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
- 单例模式的主要优点在于提供了对唯一实例的受控访问并可以节约系统资源;其主要缺点在于因为缺少抽象层而难以扩展,且单例类职责过重。
- 单例模式适用情况包括:系统只需要一个实例对象;客户调用类的单个实例只允许使用一个公共访问点。