Java - 单例模式的几种实现, 以及反射和反序列化漏洞

单例模式的几种实现

package singleton;
/**
 * 单例模式 - 饿汉式
 * 特点: 立即加载,调用时效率高
 * 原理: 声明为static的变量在类加载时即初始化,jvm加载类不会发生并发问题
 * 缺点: 如果不需要用到该类,将造成资源浪费
 */
public class SingletonDemo1 {
	private static SingletonDemo1 instance = new SingletonDemo1();
	private SingletonDemo1() {}
	public static SingletonDemo1 getInstance() {
		return instance;
	}
}
package singleton;
/**
 * 单例模式 - 懒汉式
 * 特点: 延迟加载,调用时效率低
 * 原理: 变量不是在初始化时赋值,而是在第一次调用时赋值
 * 缺点: 每次方法的调用都需要同步,效率较低
 */
public class SingletonDemo2 {
	private static SingletonDemo2 instance;
	private SingletonDemo2(){}
	public static synchronized SingletonDemo2 getInstance() {
		if (instance == null) {
			instance = new SingletonDemo2();
		}
		return instance;
	}
}
package singleton;
/**
 * 单例模式 - 双重检测锁
 * 特点: 延迟加载,调用时效率一般
 * 原理: 与饿汉式相似,只不过由synchronized块结合volatile变量进行2次检查同步
 * 缺点: 不稳定,有时会出问题
 */
public class SingletonDemo3 {
	private volatile static SingletonDemo3 instance;

    private SingletonDemo3() {
    }

    public static SingletonDemo3 getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo3.class) {
                if (instance == null) {
                	instance = new SingletonDemo3();
                }
            }
        }
        return instance;
    }
}
package singleton;
/**
 * 单例模式 - 静态内部类
 * 特点: 延迟加载,调用时效率高
 * 原理: 静态内部类只有在调用时才初始化,变量声明为private static final保证不变性与唯一性
 * 缺点: 
 */
public class SingletonDemo4 {
	private static class SingletonInstance{
		private static SingletonDemo4 INSTANCE = new SingletonDemo4();
	}
	private SingletonDemo4(){}
	public static SingletonDemo4 getInstance() {
		return SingletonInstance.INSTANCE;
	}
}
package singleton;
/**
 * 单例模式 - 枚举类
 * 特点: 立即加载,调用时效率高,不会被反射或反序列化
 * 原理: 枚举类本身是单例的
 * 缺点: 不能延迟加载
 */
public enum SingletonDemo5 {
	INSTANCE;
}

反射和反序列化漏洞

package singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;

/**
 * 测试5个单例模式的实现方法
 * 反射与反序列化漏洞及解决
 *
 */
public class Test {
	public static void main(String[] args) {
		Test test = new Test();
		
		System.out.println("正常获取实例,判断与正常获得的是否同一个实例:");
		test.test1();
		
		System.out.println("反射获取实例,判断与正常获得的是否同一个实例:");
		test.test2();
		
		System.out.println("反序列化获取实例,判断与正常获得的是否同一个实例:");
		test.test3();
	}

	public void test1() {// 正常
		System.out.println("demo1--" + (SingletonDemo1.getInstance() == SingletonDemo1.getInstance()));
		System.out.println("demo2--" + (SingletonDemo2.getInstance() == SingletonDemo2.getInstance()));
		System.out.println("demo3--" + (SingletonDemo3.getInstance() == SingletonDemo3.getInstance()));
		System.out.println("demo4--" + (SingletonDemo4.getInstance() == SingletonDemo4.getInstance()));
		System.out.println("demo5--" + (SingletonDemo5.INSTANCE == SingletonDemo5.INSTANCE));
	}

	public void test2() {// 反射
		try {
			test2_2("singleton.SingletonDemo1");
			test2_2("singleton.SingletonDemo2");
			test2_2("singleton.SingletonDemo3");
			test2_2("singleton.SingletonDemo4");
			test2_2("singleton.SingletonDemo5");
		} catch (Exception e) {
			System.out.println(e.getMessage()+e.getClass());
		}
	}
	
	public void test2_2(String className) throws Exception {// 反射获取构造器,生成实例
		Class<SingletonDemo1> c = (Class<SingletonDemo1>) Class.forName(className);
		Constructor<SingletonDemo1> constructor = c.getDeclaredConstructor(null);// 空构造方法
		constructor.setAccessible(true);// 忽略访问权限(因构造方法被声明为private)
		System.out.println(className + "--" +(constructor.newInstance() == constructor.newInstance()));
	}
	
	public void test3() {// 反序列化
		SingletonDemo demo = (SingletonDemo) test3_3(SingletonDemo.getInstance());
		System.out.println("demo--" + (demo == SingletonDemo.getInstance()));
		SingletonDemo5 demo5 = (SingletonDemo5) test3_3(SingletonDemo5.INSTANCE);
		System.out.println("demo5--" + (demo5 == SingletonDemo5.INSTANCE));
	}
	
	public Object test3_3(Object obj) {// 序列化,反序列化
		Object resultObj = null;
		try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/test.txt"));
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/test.txt"));)
		{
			oos.writeObject(obj);
			resultObj = ois.readObject();
		}catch (Exception e) {
			e.printStackTrace();
		}
		return resultObj;
	}
	
	static class SingletonDemo implements Serializable{// 饿汉式
		private static SingletonDemo instance = new SingletonDemo();
		private SingletonDemo() {}
		public static SingletonDemo getInstance() {
			return instance;
		}
		/**
		 * 防止反序列化时生成新的实例(手动重写readResolve方法)
		 * @return 执行反序列化时得到的实例
		 */
//		private Object readResolve() {
//			return instance;
//		}
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值