此篇总结五种单例创建方式,分别是:饿汉、懒汉、双重检查、静态内部类、枚举。及利用反射、反序列化方式破解单例,还有如何去防止反射、反序列化对单例的破坏。
直接上代码:
一、饿汉单例
package com.minant.singleton;
/**
* @ClassName HungrySingle
* @Description TODO 饿汉单例模式(即时加载,效率高、线程安全、适用频繁调用的单例)
* @Author MinAnt
* @Date 2020/5/18
* @Version V1.0
*/
public class HungrySingle {
private static HungrySingle single = new HungrySingle();
private HungrySingle() {
}
public static HungrySingle getHungry() {
return single;
}
}
二、懒汉单例
package com.minant.singleton;
/**
* @ClassName LazySingle
* @Description TODO 懒汉单例模式(延时加载,适用创建代价较大的单例,调用性能不佳)
* @Author MinAnt
* @Date 2020/5/18
* @Version V1.0
*/
public class LazySingle {
private static LazySingle lazySingle = null;
private LazySingle() {
}
public static synchronized LazySingle getLazySingle() {
if(lazySingle == null) {
lazySingle = new LazySingle();
}
return lazySingle;
}
}
三、静态内部类
package com.minant.singleton;
/**
* @ClassName StatInnerSingle
* @Description TODO 静态内部类单例模式(延时加载,线程安全,调用效率高)
* @Author MinAnt
* @Date 2020/5/18
* @Version V1.0
*/
public class StatInnerSingle {
private StatInnerSingle() {
}
private static class MyInnerSingle {
private static StatInnerSingle single = new StatInnerSingle();
}
public static StatInnerSingle getStatiSingle() {
return MyInnerSingle.single;
}
}
四、双重检查
package com.minant.singleton;
/**
* @ClassName DoubleCheckSingle
* @Description TODO 双重检查单例模式(延时加载,比饿汉式稍轻量的同步块)
* @Author MinAnt
* @Date 2020/5/18
* @Version V1.0
*/
public class DoubleCheckSingle {
/**
* 由于内存模型问题,并发性况下可能发生指令重排,导致第一次检测到的是还未完成构建的对象,从面返回一个有问题的对象
* 加上volatile解决(可见性、有序性)
* 1、分配内存空间;2、指定内存地址;3、初始化对象
* 在创建对象过程中,不是原子性的,23步骤可能因性能原因编译器或处理器会对其顺序进行调整
* */
private static volatile DoubleCheckSingle single = null;
private DoubleCheckSingle() {
}
public static DoubleCheckSingle getDoubleSingle() {
// 不必每次都走同步块,提高调用性能
if(single == null) {
// 不让重复创建
synchronized (DoubleCheckSingle.class) {
// 不让重复创建,前面检查为null,这里在查一次,避免再次创建
if(single == null) {
single = new DoubleCheckSingle();
}
}
}
return single;
}
}
五、枚举单例
package com.minant.singleton;
/**
* @ClassName EnuSingle
* @Description TODO 枚举单例模式(即时加载,线程安全,调用效率高,可以避免反射攻击)
* @Author MinAnt
* @Date 2020/5/18
* @Version V1.0
*/
public enum EnuSingle {
MYENU;
public void myjob() {
System.out.println("吧啦吧啦。。。。");
}
}
反射方式破坏(懒汉型单例为例):
System.out.println("=============反射破解测试===========");
Class<LazySingle> lazySingle = (Class<LazySingle>)Class.forName("com.minant.singleton.LazySingle");
Constructor<LazySingle> lc = lazySingle.getDeclaredConstructor(null);
// 跳过访问限制
lc.setAccessible(true);
LazySingle ls1 = lc.newInstance();
LazySingle ls2 = lc.newInstance();
LazySingle ls3 = LazySingle.getLazySingle();
System.out.println(ls1);
System.out.println(ls2);
System.out.println(ls3);
System.out.println(ls1 == ls2);
防止反射:
package com.minant.singleton;
/**
* @ClassName LazySingle
* @Description TODO 懒汉单例模式(延时加载,适用创建代价较大的单例,调用性能不佳)
* @Author MinAnt
* @Date 2020/5/18
* @Version V1.0
*/
public class LazySingle {
private static LazySingle lazySingle = null;
private LazySingle() {
// 防止反射破解(只有先用getLazySingle()创建后才行)
if(lazySingle != null) {
throw new RuntimeException();
}
}
public static synchronized LazySingle getLazySingle() {
if(lazySingle == null) {
lazySingle = new LazySingle();
}
return lazySingle;
}
}
序列化反序列化破坏(前提单例类实现了Serializable接口):
System.out.println("=============反序列化破解测试===========");
LazySingle ls3 = LazySingle.getLazySingle();
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(out);
oout.writeObject(ls3);
oout.close();
out.close();
byte[] rs = out.toByteArray();
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(rs));
LazySingle ls4 = (LazySingle)in.readObject();
System.out.println(ls3);
System.out.println(ls4);
防止反序列化:
package com.minant.singleton;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* @ClassName LazySingle
* @Description TODO 懒汉单例模式(延时加载,适用创建代价较大的单例,调用性能不佳)
* @Author MinAnt
* @Date 2020/5/18
* @Version V1.0
*/
public class LazySingle implements Serializable {
private static LazySingle lazySingle = null;
private LazySingle() {
// 防止反射破解(只有先用getLazySingle()创建后才行)
if(lazySingle != null) {
throw new RuntimeException();
}
}
// 防止反序列化创建
private Object readResolve() throws ObjectStreamException {
return lazySingle;
}
public static synchronized LazySingle getLazySingle() {
if(lazySingle == null) {
lazySingle = new LazySingle();
}
return lazySingle;
}
}