常见的实现单例的方法大致分为五种
1. 饿汉模式
2. 懒汉模式
#2.1
* 懒汉模式
* 线程不安全【延迟加载】
#2.2
* 懒汉模式
* 线程安全【同步,延迟加载】
3. 双重监测锁(懒汉模式)
* 线程安全【同步,延迟加载】
4. 内部类
* 线程安全【延迟加载】
5. 枚举
* 线程安全【饿汉】
* JVM底层支持
* 反射,序列化对其无效
以上单例的实现方式除枚举外都可以通过反射和反序列化的方式进行破解!
通过枚举方式实现的单例模式是JVM底层天然实现的单例,反射无法破解!
反射攻击单例模式
序列化攻击单例模式【前提单例类要实现Serializable接口】
##打印结果:false
>解决方法,单例类中添加readResolver方法
>该方法在ObjectReader调用readObject方法的时候触发【基于回调的】
>注意返回值是Object类型,可能会抛异常
- * 饿汉模式
- * 懒汉模式
- * 双重检查锁
- * 内部类
- * 枚举
1. 饿汉模式
public class Single1 {
private static Single1 single = new Single1();
private Single1() {
}
public static Single1 getInstance(){
return single;
}
}
2. 懒汉模式
#2.1
* 懒汉模式
* 线程不安全【延迟加载】
public class Single2_1 {
private static Single2_1 single;
private Single2_1() {
}
public static Single2_1 getInstance(){
if(single == null){
single = new Single2_1();
}
return single;
}
}
#2.2
* 懒汉模式
* 线程安全【同步,延迟加载】
public class Single2_2 {
private static Single2_2 single;
private Single2_2() {
}
public static synchronized Single2_2 getInstance(){
if(single == null){
single = new Single2_2();
}
return single;
}
}
3. 双重监测锁(懒汉模式)
* 线程安全【同步,延迟加载】
public class Single3 {
private static Single3 single;
private Single3() {
}
public static Single3 getInstance(){
if(single == null){
synchronized (Single3.class) {
if(single == null){
single = new Single3();
}
}
}
return single;
}
}
4. 内部类
* 线程安全【延迟加载】
public class Single4 {
private static class SingleHolder{
private static final Single4 single = new Single4();
}
private Single4() {
}
public static Single4 getInstance(){
return SingleHolder.single;
}
}
5. 枚举
* 线程安全【饿汉】
* JVM底层支持
* 反射,序列化对其无效
public enum Single5 {
INSTANCE;
public void operation(){
System.out.println("其它方法");
}
}
以上单例的实现方式除枚举外都可以通过反射和反序列化的方式进行破解!
通过枚举方式实现的单例模式是JVM底层天然实现的单例,反射无法破解!
反射攻击单例模式
public class ReflectAttack {
public static void main(String[] args) throws Exception {
//理想中唯一的单例
Single1 s1 = Single1.getInstance();
Class<Single1> clazz = (Class<Single1>) Class.forName("com.xy.single.Single1");
//反射出来的多例
Single1 s2 = clazz.newInstance();
System.out.println(s1==s2);
}
}
##打印结果:false
>解决方法,在构造函数中做文章!【如果想通过反射破坏单例则抛出运行时异常】 private Single1() {
if(single!=null)
throw new RuntimeException();
}
序列化攻击单例模式【前提单例类要实现Serializable接口】
public class SerializeAttack {
public static void main(String[] args) throws Exception {
//理想中的单例
Single1 single1 = Single1.getInstance();
//将对象序列化
FileOutputStream fos = new FileOutputStream("G:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(single1);
oos.close();
fos.close();
//反序列化对象完成破解单例
FileInputStream fis = new FileInputStream("G:/a.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Single1 single2 = (Single1) ois.readObject();
ois.close();
fis.close();
System.out.println(single1==single2);
}
}
##打印结果:false
>解决方法,单例类中添加readResolver方法
private Object readResolve() throws ObjectStreamException{
return single;
}
>该方法在ObjectReader调用readObject方法的时候触发【基于回调的】
>注意返回值是Object类型,可能会抛异常