一、懒汉式单例模式,解决反射和反序列化漏洞
public static void main(String[] args) throws Exception {
SingletonDemo1 sc1 = SingletonDemo1.getInstance();
SingletonDemo1 sc2 = SingletonDemo1.getInstance();
System.out.println(sc1); // sc1,sc2是同一个对象
System.out.println(sc2);
// 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞)
// Class<SingletonDemo1> clazz = (Class<SingletonDemo1>) Class.forName("com.sankuai.ia.demo.web.service.SingletonDemo1");
// Constructor<SingletonDemo1> c = clazz.getDeclaredConstructor(null);
// c.setAccessible(true); // 跳过权限检查
// SingletonDemo1 sc3 = c.newInstance();
// SingletonDemo1 sc4 = c.newInstance();
// System.out.println(sc3); // sc3,sc4不是同一个对象
// System.out.println(sc4);
// 通过反序列化的方式构造多个对象(类需要实现Serializable接口)
// 1. 把对象sc1写入硬盘文件
FileOutputStream fos = new FileOutputStream("object.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(sc1);
oos.close();
fos.close();
// 2. 把硬盘文件上的对象读出来
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out"));
// 如果对象定义了readResolve()方法,readObject()会调用readResolve()方法。从而解决反序列化的漏洞
SingletonDemo1 sc5 = (SingletonDemo1) ois.readObject();
// 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。
System.out.println(sc5);
ois.close();
}
二、静态内部类式单例模式(解决反射和反序列化漏洞)
package com.sankuai.ia.demo.web.service;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
/**
*
*
* 静态内部类实现方式(也是一种懒加载方式)
* 这种方式:线程安全,调用效率高,并且实现了延迟加载
* 解决反射和反序列化漏洞
*
**/
public class SingletonDemo2 implements Serializable{
private static class SingletonClassInstance {
private static final SingletonDemo2 instance = new SingletonDemo2();
}
// 方法没有同步,调用效率高
public static SingletonDemo2 getInstance() {
return SingletonClassInstance.instance;
}
// 防止反射获取多个对象的漏洞
private SingletonDemo2() {
if (null != SingletonClassInstance.instance)
throw new RuntimeException();
}
// 防止反序列化获取多个对象的漏洞
private Object readResolve() throws ObjectStreamException {
return SingletonClassInstance.instance;
}
public static void main(String[] args) throws Exception {
SingletonDemo2 sc1 = SingletonDemo2.getInstance();
SingletonDemo2 sc2 = SingletonDemo2.getInstance();
System.out.println(sc1); // sc1,sc2是同一个对象
System.out.println(sc2);
// 通过反射的方式直接调用私有构造器(通过在构造器里抛出异常可以解决此漏洞)
Class<SingletonDemo2> clazz = (Class<SingletonDemo2>) Class.forName("com.sankuai.ia.demo.web.service.SingletonDemo2");
Constructor<SingletonDemo2> c = clazz.getDeclaredConstructor(null);
c.setAccessible(true); // 跳过权限检查
SingletonDemo2 sc3 = c.newInstance();
SingletonDemo2 sc4 = c.newInstance();
System.out.println("通过反射的方式获取的对象sc3:" + sc3); // sc3,sc4不是同一个对象
System.out.println("通过反射的方式获取的对象sc4:" + sc4);
// 通过反序列化的方式构造多个对象(类需要实现Serializable接口)
// 1. 把对象sc1写入硬盘文件
FileOutputStream fos = new FileOutputStream("object.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(sc1);
oos.close();
fos.close();
// 2. 把硬盘文件上的对象读出来
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.out"));
// 如果对象定义了readResolve()方法,readObject()会调用readResolve()方法。从而解决反序列化的漏洞
SingletonDemo2 sc5 = (SingletonDemo2) ois.readObject();
// 反序列化出来的对象,和原对象,不是同一个对象。如果对象定义了readResolve()方法,可以解决此问题。
System.out.println("对象定义了readResolve()方法,通过反序列化得到的对象:" + sc5);
ois.close();
}
}