五种单例模式的安全性问题

在之前两讲中,介绍了懒汉式、饿汉式双重锁、内部类、枚举5种单例模式,其实单例模式还有很多种设计,在此就不一一介绍了。

在这5种单例模式中,枚举最为特殊,由于是官方提供的一种模式,所以不能被破解,是十分安全的。

在剩余4种中,我们以懒汉式为例说说安全问题。

正常调用代码:

public class Client {
	public static void main(String[] args) {
		Husband01 s011 = Husband01.getWife();
		Husband01 s012 = Husband01.getWife();
		
		System.out.println(s011);
		System.out.println(s012);
		System.out.println(s011 == s012);
	}
}

输出结果
Husband01@c3c749
Husband01@c3c749
true

接下来,用反射破解单例模式:

import java.lang.reflect.Constructor;
public class Client {
	public static void main(String[] args) throws Exception {
		Husband01 s011 = Husband01.getWife();
		Husband01 s012 = Husband01.getWife();
		
		//利用反射创建新对象
		Class<Husband01> h1 = (Class<Husband01>) Class.forName("Husband01");//反射获取H1对象
		Constructor c = h1.getDeclaredConstructor(null);//获取无参构造函数
		c.setAccessible(true);//更改无参构造函数权限为公开
		Husband01 s013 = (Husband01) c.newInstance(null);//创建新对象
		
		System.out.println(s011);
		System.out.println(s012);
		System.out.println(s013);
		System.out.println(s011 == s012);
		System.out.println(s011 == s013);
	}
}
输出结果

Husband01@1bc4459
Husband01@1bc4459
Husband01@12b6651
true
false
另一种,利 用反序列化破解(需实现Serializable)单例模式:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Client {
	public static void main(String[] args) throws Exception {
		Husband01 s011 = Husband01.getWife();
		Husband01 s012 = Husband01.getWife();
		
		//利用反序列化创建新对象
		FileOutputStream fos = new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(s011);
		fos.close();
		oos.close();
		
		FileInputStream fis = new FileInputStream("d:/a.txt");
		ObjectInputStream ois = new ObjectInputStream(fis);
		Husband01 s013 = (Husband01) ois.readObject();
		fis.close();
		ois.close();
		
		System.out.println(s011);
		System.out.println(s012);
		System.out.println(s013);
		System.out.println(s011 == s012);
		System.out.println(s011 == s013);
	}
}
输出结果

Husband01@13bad12
Husband01@13bad12
Husband01@1a626f
true
false
我们现在知道了如果破解,那么如果避免呢?

代码应该这样写!

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * 懒汉式模式:在类加载时不初始化,当需要时再反馈(延迟加载)
 */
public class Husband01 implements Serializable{
	//这句话看起来很奇怪,就理解为一开始每个男人都是没有老婆的
	private static Husband01 wife = null;
	//私有化老公,让小三无法拥有
	private Husband01(){
		if(wife != null){//防止反射破解
			throw new RuntimeException();
		}
	}
	
	//当家里人找你要老婆的时候
	public static synchronized Husband01 getWife(){//添加synchronized防止小三插入问题
		if(wife == null){
			wife = new Husband01();//这句话理解成老公和老婆结婚啦!
		}
		return wife;
	}
	
	// 防止反序列化破解
    private Object readResolve() throws ObjectStreamException {    
        return wife;  
    } 
}
这样子,我们就能避免被破解啦?那么五种设计模式的效率如何呢?请看 5种单例模式的效率问题





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值