单例模式之预防反射攻击

单例模式很简单,基本有点开发经验或者学习经验的人都可以写出来,通过私有构造,返回INSTANCE,代码如下:

public class Singleton {

	// 私有,静态的类自身实例
	private static Singleton INSTANCE = new Singleton();

	// 私有的构造子(构造器,构造函数,构造方法)
	private Singleton() {}

	// 公开,静态的工厂方法
	public static Singleton getInstance() {
		return INSTANCE;
	}
	
	public void sayHello(){
		System.out.println("Hello,我是单例的方法哦!!");
	}
}


没错,很简单,通过公开静态方法获取实例,构造方法定义为私有了,也无法实例化,代码如下

public static void main(String[] args) {
	// 通过公开静态方法getInstance()获取
	System.out.println(Singleton.getInstance());
	Singleton.getInstance().sayHello();

}


运行结果:

com.mengli.bean.Singleton@5fb7a531
Hello,我是单例的方法哦!!

重点来了,Java反射是可以绕过Java机制调用私有构造的,所以我这样写:

public static void main(String[] args) {
	// 通过公开静态方法getInstance()获取
	System.out.println(Singleton.getInstance());
	Singleton.getInstance().sayHello();
	try {
		// 通过反射操作
		Class claxx = Class.forName("com.mengli.bean.Singleton");
		Constructor<Singleton> constructor = claxx.getDeclaredConstructor();
		constructor.setAccessible(true);
		Object object = constructor.newInstance();
		Singleton singleton = (Singleton)object;
		System.out.println(singleton);
		singleton.sayHello();
	} catch (Exception e) {
		e.printStackTrace();
	}
}


运行结果:

com.mengli.bean.Singleton@5fb7a531
Hello,我是单例的方法哦!!
com.mengli.bean.Singleton@11be650f
Hello,我是单例的方法哦!!

很明显,这个时候内存中有了两个实例了,这就完全不符合单例模式了,如何修改呢?在此之前呢,先实现一个自定义异常:

public class StudyException extends RuntimeException {
	private static final long serialVersionUID = 1L;

	public StudyException(String message){
		super(message);
	}
}

重点修改的地方来了:

public class Singleton {
	// 定义全局静态变量count
	private static int count = 0;
	// 私有,静态的类自身实例
	private static Singleton INSTANCE = new Singleton();

        // 私有的构造子(构造器,构造函数,构造方法)
	private Singleton() {
		if(count > 0){
			throw new StudyException("该类只能有一个实例对象!");
		}
		count ++ ;
	}

	// 公开,静态的工厂方法
	public static Singleton getInstance() {
		return INSTANCE;
	}
	
	public void sayHello(){
		System.out.println("Hello,我是单例的方法哦!!");
	}
}

如上代码所示,我加入了一个全局的count,当该对象实例化的时候,对count进行自增操作,也就是在count大于等于1的时候呢,说明有代码在视图创建第二个实例,这个时候就可以果断抛出异常,捕获处理...

我们再次执行程序:

com.mengli.bean.Singleton@7a4014a0
Hello,我是单例的方法哦!!
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
	at java.lang.reflect.Constructor.newInstance(Unknown Source)
	at com.mengli.main.StudyMain.main(StudyMain.java:20)
Caused by: com.mengli.exception.StudyException: 该类只能有一个实例对象!
	at com.mengli.bean.Singleton.<init>(Singleton.java:14)
	... 5 more
从结果开来,防御成功了是吧!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值