设计模式:一文搞定单例模式(防止反射反序列化clone破坏单例)Singleton Pattern-Java版
Posted 冲冲冲冲冲冲!!!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式:一文搞定单例模式(防止反射反序列化clone破坏单例)Singleton Pattern-Java版相关的知识,希望对你有一定的参考价值。
一、定义
单例模式,顾名思义,就是一个类从始至终只产生一个对象。现实生活中的例子有很多,比如在太阳系考虑问题,那么太阳和地球都可称为单例,再比如工具类,有时候没有把所有方法用static修饰(这不是个好办法),就应该把它做成单例,因为它没有不变的状态。
二、五种单例模式:饿汉式、懒汉式、双重检查锁、静态内部类、枚举
(一) 饿汉式,加载类时马上创建对象
/**
* 饿汉式
* 优点:线程安全、效率高
* 缺点:不能做到“即用即创建”,有可能浪费内存资源
*/
public class Singleton {
private static Singleton single=new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return single;
}
}
(二) 懒汉式:用到时才创建对象,但线程不安全
/**
* 懒汉式
* 优点:即用即创建,用到时再创建
* 缺点:线程不安全
*
*/
class Singleton2 {
private static Singleton2 single=null;
private Singleton2() {}
public static Singleton2 getInstance() {
if(single==null) {
single=new Singleton2();
}
return single;
}
}
(三)双重检查锁:懒汉式的线程安全版
/**
* 懒汉式-双重检查锁
* 优点:即用即创建,用到时再创建
* 缺点:效率一般
*/
class Singleton3 {
private static Singleton3 single=null;
private Singleton3() {}
public static Singleton3 getInstance() {
if(single==null) {
synchronized(Singleton3.class) {
if(single==null)
single=new Singleton3();
}
}
return single;
}
}
(四)静态内部类:只有访问了getInstance方法,才会去加载静态内部类,达到“用到时才创建对象”的效果,线程安全,效率高
/**
* 静态内部类(推荐)
* 优点:线程安全,用到时才加载
*/
class Singleton4{
private Singleton4() {}
private static class SingletonHelper {
final static Singleton4 instance=new Singleton4();
}
public static Singleton4 getInstance() {
return SingletonHelper.instance;
}
}
(五)枚举单例:《Effective Java》说枚举单例是最好的单例,有效防止反序列化
/**
* 枚举(effective Java书中说枚举单例是最好的单例)(重磅推荐)
*优点:线程安全、用到时再加载、避免反序列化、避免反射、避免克隆
*/
enum Singleton5 {
INSTANCE
}
三、3个实验:使用反射、反序列化、clone来破坏单例
(一)反射破坏单例模式
public class Test {
public static void test11() throws Exception {
System.out.println("反射破坏饿汉式:");
Class<?> c1 = Class.forName("chapter5.singleton.Singleton");
Constructor<?> declaredConstructor = c1.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Singleton s1=Singleton.getInstance(),s2=Singleton.getInstance();
Singleton s3=(Singleton) declaredConstructor.newInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
public static void test12() throws Exception {
System.out.println("反射破坏懒汉式:");
Class<?> c1 = Class.forName("chapter5.singleton.Singleton2");
Constructor<?> declaredConstructor = c1.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Singleton2 s1=Singleton2.getInstance(),s2=Singleton2.getInstance();
Singleton2 s3=(Singleton2) declaredConstructor.newInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
public static void test13() throws Exception {
System.out.println("反射破坏双重检查锁:");
Class<?> c1 = Class.forName("chapter5.singleton.Singleton3");
Constructor<?> declaredConstructor = c1.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Singleton3 s1=Singleton3.getInstance(),s2=Singleton3.getInstance();
Singleton3 s3=(Singleton3) declaredConstructor.newInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
public static void test14() throws Exception {
System.out.println("反射破坏静态内部类:");
Class<?> c1 = Class.forName("chapter5.singleton.Singleton4");
Constructor<?> declaredConstructor = c1.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Singleton4 s1=Singleton4.getInstance(),s2=Singleton4.getInstance();
Singleton4 s3=(Singleton4) declaredConstructor.newInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
public static void test15() throws Exception {
System.out.println("反射破坏枚举类:");
Class<?> c1 = Class.forName("chapter5.singleton.Singleton5");
Constructor<?> declaredConstructor = c1.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Singleton5 s1=Singleton5.INSTANCE,s2=Singleton5.INSTANCE;
Singleton5 s3=(Singleton5) declaredConstructor.newInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
public static void main(String[] args) throws Exception {
test11();
test12();
test13();
test14();
test15();
}
}
输出结果:
反射破坏饿汉式:
chapter5.singleton.Singleton@15db9742
chapter5.singleton.Singleton@15db9742
chapter5.singleton.Singleton@6d06d69c
反射破坏懒汉式:
chapter5.singleton.Singleton2@7852e922
chapter5.singleton.Singleton2@7852e922
chapter5.singleton.Singleton2@4e25154f
反射破坏双重检查锁:
chapter5.singleton.Singleton3@70dea4e
chapter5.singleton.Singleton3@70dea4e
chapter5.singleton.Singleton3@5c647e05
反射破坏静态内部类:
chapter5.singleton.Singleton4@33909752
chapter5.singleton.Singleton4@33909752
chapter5.singleton.Singleton4@55f96302
反射破坏枚举类:
Exception in thread "main" java.lang.NoSuchMethodException: chapter5.singleton.Singleton5.<init>()
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getDeclaredConstructor(Unknown Source)
at chapter5.singleton.Singleton.test15(Singleton.java:70)
at chapter5.singleton.Singleton.main(Singleton.java:84)
可以看到,除了枚举,其它的单例模式都可以被反射破坏。
那么怎么防止反射呢?
方法也很简单,创建一个静态变量first,初始化为true,第一次创建时把它改为false,第二次以上再创建则抛出异常。以恶汉式为例(注意要把first放到single=new Singleton前面):
public class Singleton {
private static boolean first=true;//必须放到new Singleton()前面,因为编译器从上到下加载
private static Singleton single=new Singleton();
private Singleton() {
if(first) {
synchronized(Singleton.class) {
if(first) {
first=false;
}
}
} else {
throw new RuntimeException("单例不能创建两个");
}
}
public static Singleton getInstance() {
return single;
}
}
再次运行刚才的实验程序,运行结果:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.RuntimeException: 单例不能创建两个
at chapter5.singleton.Singleton.<init>(Singleton.java:22)
at chapter5.singleton.Singleton.<clinit>(Singleton.java:11)
(二)反序列化破坏单例模式
首先将所有的单例都实现Serializable 接口,生成一个serialVersionUID
public class Singleton implements Serializable {
private static final long serialVersionUID = -6693877351780017099L;
......
}
测试类:
public class TestSerializedRuin {
public static void test(Object s1) {
System.out.println("序列化前:"+s1);
ObjectOutputStream oos=null;
ObjectInput ois=null;
//序列化
try {
oos=new ObjectOutputStream(new FileOutputStream("test.txt"));
oos.writeObject(s1);
oos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//反序列化
try {
ois=new ObjectInputStream(new FileInputStream("test.txt"));
System.out.println("序列化后::"+ ois.readObject());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void test1() {
System.out.println("反序列化破坏饿汉式:");
Singleton s1=Singleton.getInstance();
test(s1);
}
public static void test2() {
System.out.println("反序列化破坏懒汉式:");
Singleton2 s1=Singleton2.getInstance();
test(s1);
}
public static void test3() {
System.out.println("反序列化破坏双重检查锁:");
Singleton3 s1=Singleton3.getInstance();
test(s1);
}
public static void test4() {
System.out.println("反序列化破坏静态内部类:");
Singleton4 s1=Singleton4.getInstance();
test(s1);
}
public static void test5() {
System.out.println("反序列化破坏枚举:");
Singleton5 s1=Singleton5.INSTANCE;
test(s1);
}
public static void main(String[] args) {
test1();
test2();
test3();
test4();
test5();
}
}
输出结果:
反序列化破坏饿汉式:
序列化前:chapter5.singleton.Singleton@15db9742
序列化后::chapter5.singleton.Singleton@3b07d329
反序列化破坏懒汉式:
序列化前:chapter5.singleton.Singleton2@41629346
序列化后::chapter5.singleton.Singleton2@3d075dc0
反序列化破坏双重检查锁:
序列化前:chapter5.singleton.Singleton3@214c265e
序列化后::chapter5.singleton.Singleton3@3b9a45b3
反序列化破坏静态内部类:
序列化前:chapter5.singleton.Singleton4@7699a589
序列化后::chapter5.singleton.Singleton4@568db2f2
反序列化破坏枚举:
序列化前:INSTANCE
序列化后::INSTANCE
可以看出,只有枚举可以防止反序列化。其他的单例模式该如何防止反序列化呢?只需要在单例类中加入readResolve方法即可:
private Object readResolve() {
return single;
}
这个readResolve方法只做了一个简单的事情,反序列化的时候,首先检查这个类有没有readResolve方法,若有,则返回readResolve指定的对象,若没有,才把反序列化的结果返回。
再次测试,查看结果:
反序列化破坏饿汉式:
序列化前:chapter5.singleton.Singleton@15db9742
序列化后::chapter5.singleton.Singleton@15db9742
反序列化破坏懒汉式:
序列化前:chapter5.singleton.Singleton2@3b07d329
序列化后::chapter5.singleton.Singleton2@3b07d329
反序列化破坏双重检查锁:
序列化前:chapter5.singleton.Singleton3@682a0b20
序列化后::chapter5.singleton.Singleton3@682a0b20
反序列化破坏静态内部类:
序列化前:chapter5.singleton.Singleton4@7cca494b
序列化后::单例模式防反射及性能