用私有构造器或枚举类型目的都是为了保证单例。但是如果要考虑反射和反序列的方式创建对象,则还需要额外的工作。
一.非枚举类型实现的单例
(1)防止实现Serializable的序列化,反序列化破坏单例:需要声明一个readResolve方法,ObjectInputStream.readObject() 返回的对象会是readResolve的返回对象。
ps:实际上是反序列化生成新对象后再调用了这个对象的readResolve方法。
public class Singleton_3_1 implements Serializable{
private static final long serialVersionUID = 1L;
private static final Singleton_3_1 INSTANCE = new Singleton_3_1();
private Singleton_3_1() {
}
private String param1;
public static Singleton_3_1 getInstance() {
return INSTANCE;
}
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}
//keep singleton for deserialization
private Object readResolve() {
System.err.println("readResolve :" + (this == INSTANCE));
return INSTANCE;
}
}
测试代码
public static void testSingleton() throws IOException, ClassNotFoundException {
Singleton_3_1 singleton1 = Singleton_3_1.getInstance();
Singleton_3_1 singleton2 = Singleton_3_1.getInstance();
System.err.println("get instatnce objs == result is " + (singleton1 == singleton2));
Singleton_3_1 singleton3 = Singleton_3_1.getInstance();
singleton3.setParam1("test");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(singleton3);
String objStr = byteArrayOutputStream.toString("ISO-8859-1");
objectOutputStream.close();
byteArrayOutputStream.close();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(objStr.getBytes("ISO-8859-1"));
ObjectInputStream objInputStream = new ObjectInputStream(byteArrayInputStream);
Singleton_3_1 singleton4 = (Singleton_3_1)objInputStream.readObject();
objInputStream.close();
byteArrayInputStream.close();
System.err.println("deserialization objs == result is " + (singleton3 == singleton4));
System.err.println(singleton4.getParam1());
}
结果:
get instatnce objs == result is true
readResolve :false
deserialization objs == result is true
test
readObject调用流程
1.ObjectInputStream.readObject();
2.ObjectInputStream.readObject0();
3.ObjectInputStream.readOrdinaryObject(unshared);
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) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
(2)防止反射破坏单例:修改构造器使它在创建额外实例的时候抛出异常。
private Singleton_3_1() {
if (null != INSTANCE) {
throw new RuntimeException();
}
}
Client:
//1.因为private构造器,本身就会抛错
// Singleton_3_1 singleton5 = (Singleton_3_1) c.newInstance();
//2.因为private构造器,本身就会抛错
// Constructor constructor1 = c.getDeclaredConstructor();
// Object obj1 = constructor1.newInstance();
//3.本身不会因为访问private构造器而抛错,现在抛出构造器中代码主动抛出的异常。
Constructor constructor2 = c.getDeclaredConstructor();
constructor2.setAccessible(true);
Object obj2 = constructor2.newInstance();
二.枚举类型实现的单例
不能利用 new、clone()、de-serialization、以及 Reflection API 来产生enum 的 instance。
public enum Singleton_3_2 {
INSTANCE;
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
三.对外公开单例的对象可以采用:
1.可以公开单例属性
public static final Singleton INSTANCE = nwe Singleton()
2.也可以公开获取单例属性的方法
private static final Singleton INSTANCE = nwe Singleton();
public static Singleton getInstance() {
return INSTANCE;
}
这种方式比前一种方式更为灵活,因为可以轻易修改这个方法的实现,而不影响调用者。
总结:用枚举类型实现单例:单元素的枚举类型是实现单例的最佳方法。