1.单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式
2.单例模式主要解决了什么问题?
3.单例模式的结构
4.单例设计模式分类两种
1.饿汉式-方式1(静态变量方式)
public class Singleton {
// 私有构造方法
public Singleton() {}
// 静态变量创建类的对象
private static Singleton instance = new Singleton();
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
// 静态变量创建类的对象
return instance;
}
}
说明:该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费
2.饿汉式-方式2(静态代码块方式)
public class Singleton {
// 私有构造方法
private Singleton() {}
// 在成员位置创建该类的对象
private static Singleton instance;
static {
instance = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}
说明:该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题
1.懒汉式-方式1(线程不安全)
public class Singleton {
// 私有构造方法
private Singleton() {}
// 在成员位置创建该类的对象
private static Singleton instance;
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
说明:从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题
2.懒汉式-方式2(线程安全)
public class Singleton {
// 私有构造方法
private Singleton() {}
// 在成员位置创建该类的对象
private static Singleton instance;
// 对外提供静态方法获取该对象
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
说明:该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了
3.懒汉式-方式3(双重检查锁)
public class Singleton {
// 私有构造方法
private Singleton() {}
private static volatile Singleton instance;
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
// 第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
if(instance == null) {
synchronized (Singleton.class) {
// 抢到锁之后再次判断是否为空
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
4.懒汉式-方式4(静态内部类方式)
public class Singleton {
// 私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
说明:第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性
小结:静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费
破坏单例模式:使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射
1.来一个单例模式
package com.andy.bug.demo01;
import java.io.Serializable;
public class Singleton implements Serializable {
// 私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private Object readResolve() {
return SingletonHolder.instance;
}
}
2.编写一个测试类
public class Test {
public static void main(String[] args) throws Exception {
// 往文件中写对象
// writeObject2File();
// 从文件中读取对象
Singleton s1 = readObjectFromFile();
Singleton s2 = readObjectFromFile();
// 判断两个反序列化后的对象是否是同一个对象
System.out.println(s1 == s2);
}
private static Singleton readObjectFromFile() throws Exception {
// 创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/andy/a.txt"));
// 第一个读取Singleton对象
Singleton instance = (Singleton) ois.readObject();
return instance;
}
public static void writeObject2File() throws Exception {
// 获取Singleton类的对象
Singleton instance = Singleton.getInstance();
// 创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/andy/a.txt"));
// 将instance对象写出到文件中
oos.writeObject(instance);
}
}
运行结果是false
,表明序列化和反序列化已经破坏了单例设计模式
3.解决方式
在Singleton类中添加
readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象
public class Singleton implements Serializable {
// 私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
1.来一个单例模式
public class Singleton {
// 私有构造方法
private Singleton() {}
private static volatile Singleton instance;
// 对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}
2.测试类
public class Test {
public static void main(String[] args) throws Exception {
// 获取Singleton类的字节码对象
Class clazz = Singleton.class;
// 获取Singleton类的私有无参构造方法对象
Constructor constructor = clazz.getDeclaredConstructor();
// 取消访问检查
constructor.setAccessible(true);
// 创建Singleton类的对象s1
Singleton s1 = (Singleton) constructor.newInstance();
// 创建Singleton类的对象s2
Singleton s2 = (Singleton) constructor.newInstance();
// 判断通过反射创建的两个Singleton对象是否是同一个对象
System.out.println(s1 == s2);
}
}
运行结果是false
,表明序列化和反序列化已经破坏了单例设计模式
3.解决方法
public class Singleton {
//私有构造方法
private Singleton() {
/*
反射破解单例模式需要添加的代码
*/
if(instance != null) {
throw new RuntimeException();
}
}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}