单例是我们程序运行过程中只存在唯一的一个实例,对于唯一性的保证如何做到。
1.防反射
首先我们看一个单例的例子
public class Instance {
private static Instance INSTANCE;
private Instance(){}
public static Instance getInstance()
{
if( null == INSTANCE )
{
synchronized (Instance.class)
{
if( null == INSTANCE )
{
INSTANCE = new Instance();
}
}
}
return INSTANCE;
}
}
这样一个我们平时使用最多的单例就是能够用反射轻而易举的破解的
Instance instance = Instance.getInstance();
Constructor<Instance> con = Instance.class.getDeclaredConstructor();
con.setAccessible(true);
Instance instance1 = con.newInstance();
Log.e(TAG, "onCreate: instance == instance1 ? " + (instance == instance1));
输出结果是:
onCreate: instance == instance1 ? false
也就是说instance和instance1并不是同一个对象,所以通过反射拿到该类的Constructor(构造函数结构表示),我们就可以创建无数个Instance对象,单例的规则被破坏,不够严谨。
如何防止该问题呢?其实在构造函数里面加一个判断就好了
package com.example.randerc.myapplication;
/**
* Created by Rander.C on 2017/4/23.
*/
public class Instance {
private static Instance INSTANCE;
private Instance()
{
if( null != INSTANCE)
{
throw new RuntimeException("instance can not creat any more ,is already exists");
}
}
public static Instance getInstance()
{
if( null == INSTANCE )
{
synchronized (Instance.class)
{
if( null == INSTANCE )
{
INSTANCE = new Instance();
}
}
}
return INSTANCE;
}
}
2.防序列化
看下面单例例子
public class Instance implements Serializable{
private static Instance INSTANCE;
public static Instance getInstance()
{
if( null == INSTANCE )
{
synchronized (Instance.class)
{
if( null == INSTANCE )
{
INSTANCE = new Instance();
}
}
}
return INSTANCE;
}
}
我们用序列化的方法来构造Instance对象.
Instance instance = Instance.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
oos.writeObject(Instance.getInstance());
//Read Obj from file
File file = new File("tempFile");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Instance newInstance = (Instance) ois.readObject();
//判断是否是同一个对象
System.out.println(newInstance == Instance.getInstance());
输出结果为false,通过对Instance的序列化与反序列化得到的对象是一个新的对象,这就破坏了Instance的单例性。我们看一下readObject是如何恢复对象的。readObject的调用过程如下:
readObject--->readObject0--->readOrdinaryObject--->checkResolve
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
如何防止反序列化,具体看readOrdinaryObject源码:
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//此处省略部分代码
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
//此处省略部分代码
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
其中
obj = desc.isInstantiable() ? desc.newInstance() : null;
desc.isInstantiable()调用无参数构造方法创建了一个obj对象,这里创建的这个obj对象,就是本方法要返回的对象,也可以暂时理解为是ObjectInputStream的readObject返回的对象。这里得到的obj对象并非是该函数最终的返回结果,我们看一下下面的代码片段
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
关键语句为desc.hasReadResolveMethod(),如果有ReadResolve方法,就会调用该方法返回一个对象
Object rep = desc.invokeReadResolve(obj);该rep对象又会重新赋值给obj,然后返回。所以只要我们单例类实现这个方法,并返回我们这个唯一的单例实例就能保证反序列化创建多个对象了。所以下面重写一下该单例类。
package com.example.randerc.myapplication;
import java.io.Serializable;
/**
* Created by Rander.C on 2017/4/23.
*/
public class Instance implements Serializable{
private static Instance INSTANCE;
public static Instance getInstance()
{
if( null == INSTANCE )
{
synchronized (Instance.class)
{
if( null == INSTANCE )
{
INSTANCE = new Instance();
}
}
}
return INSTANCE;
}
private Object readResolve()
{
return INSTANCE;
}
}
3.防clone
如果单例某些情况实现了Cloneable接口(比如父类实现了,单例集成父类)就可以通过调用clone()方法复制一个对象。它是直接通过内存拷贝复制出的一个对象,并不是调用构造函数。解决办法就是clone方法抛出异常。下面给出更改前后的代码.
改动前:
public class Instance implements Cloneable{
private static Instance INSTANCE;
public static Instance getInstance()
{
if( null == INSTANCE )
{
synchronized (Instance.class)
{
if( null == INSTANCE )
{
INSTANCE = new Instance();
}
}
}
return INSTANCE;
}
}
改动后:
public class Instance implements Cloneable{
private static Instance INSTANCE;
public static Instance getInstance()
{
if( null == INSTANCE )
{
synchronized (Instance.class)
{
if( null == INSTANCE )
{
INSTANCE = new Instance();
}
}
}
return INSTANCE;
}
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("single instance not support clone");
}
}