Java实现23中设计模式之单例模式(五种创建方式)
分为两种创建类型:
饿汉式:上来直接创建对象。(两种)
懒汉式:需要用时才创建。(三种)
1、饿汉式
/**
* 饿汉式
* 构造方法抛出异常是防止反射破坏单例
* readResolve() 是防止反序列化破坏单例
*
* 创建步骤:
* 1、构造器私有
* 2、私有的静态常量并初始化
* 3、提供外部访问静态方法
*/
public class Singleton01 implements Serializable {
private static final Singleton01 INSTANCE = new Singleton01();
private Singleton01() {
// 防止反射破坏序列化
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("调用了构造方法!!");
}
public static Singleton01 getInstance() {
return INSTANCE;
}
// 防止反序列化破坏单例
public Object readResolve() {
return INSTANCE;
}
}
描述: 构造方法抛出异常是防止反射破坏单例,
readResolve()
是防止反序列化破坏单例。
创建步骤:
- 构造器私有
- 私有的静态常量并初始化
- 提供外部访问静态方法
2、饿汉式(枚举)
/**
* 枚举饿汉式
* 好处:枚举饿汉式能天然防止反射、反序列化破坏单例
*
* 创建步骤:
* 1、创建常量
* 2、提供外部访问静态方法
*/
public enum Singleton02 {
INSTANCE;
Singleton02() {
System.out.println("调用了构造方法!!");
}
@Override
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public static Singleton02 getInstance() {
return INSTANCE;
}
}
描述:好处:枚举饿汉式能天然防止反射、反序列化破坏单例。
创建步骤:
- 创建常量
- 提供外部访问静态方法
3、懒汉式(加锁)
/**
* 懒汉式
* 其实只有首次创建单例对象时才需要同步,但该代码实际上每次调用都会同步
* 创建步骤:
* 1、构造器私有
* 2、私有的静态变量
* 3、提供外部访问静态方法(方法上加重量级锁,并且空则创建,非空直接返回)
*/
public class Singleton03 implements Serializable {
private static Singleton03 INSTANCE = null;
private Singleton03() {
System.out.println("调用了构造方法!!");
}
public static synchronized Singleton03 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton03();
}
return INSTANCE;
}
}
描述:每次调用
getInstance()
方法都会加锁。比较不推荐。
创建步骤:
- 构造器私有
- 私有的静态常量并初始化
- 提供外部访问静态方法(方法上加重量级锁,并且空则创建,非空直接返回)
4、懒汉式(双重检测锁).
/**
* 双检锁懒汉式
* 为什么必须加 volatile?
* INSTANCE = new Singleton4() 不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋
* 值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造
* 如果线程1 先执行了赋值,线程2 执行到第一个 INSTANCE == null 时发现 INSTANCE 已经不为
* null,此时就会返回一个未完全构造的对象
* 创建步骤:
* 1、构造器私有
* 2、私有的静态变量(volatile防止指令重排序)
* 3、提供外部访问静态方法(Class对象上锁,并且提供双重检测)
*/
public class Singleton04 implements Serializable {
private static volatile Singleton04 INSTANCE = null; // 可见性,有序性
private Singleton04() {
System.out.println("调用了构造方法!!");
}
public static Singleton04 getInstance() {
if (INSTANCE == null) {
synchronized (Singleton04.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton04();
}
}
}
return INSTANCE;
}
}
描述:设计较为繁琐。双重检测的原因是因为锁的是该类的
Class对象
,不做双重检测还是会有线程安全的问题。
创建步骤:
- 构造器私有
- 私有的静态变量(
volatile
防止指令重排序)- 提供外部访问静态方法(
Class
对象上锁,并且提供双重检测)
5、懒汉式(静态内部类)
/**
* 内部类懒汉式
* 避免了双检锁的缺点
*
* 创建步骤:
* 1、构造器私有
* 2、私有的静态内部类静态成员变量创建单例对象
* 3、提供外部访问静态方法
*/
public class Singleton05 implements Serializable {
private Singleton05() {
System.out.println("调用了构造方法!!");
}
private static class Holder {
static Singleton05 INSTANCE = new Singleton05();
}
public static Singleton05 getInstance() {
return Holder.INSTANCE;
}
}
描述:避免了双检锁的缺点。
创建步骤:
- 构造器私有
- 私有的静态内部类静态成员变量创建单例对象
- 提供外部访问静态方法