当我们使用Singleton模式时,应该是期望某个类的实例应该是唯一的,但如果该类是可序列化的,那么发序列化后还会是单例的吗?下面我们通过如下示例一来验证一下:
示例一
User 类
User 类是单例模式,使用的饿汉模式,在类加载的时候就创建对象实例。
public class User implements Serializable {
private static final long serialVersionUID = 3380014540967816490L;
private String userName;
private String password;
private static User user = new User("zhangsan", "test");
private User(String userName, String password) {
this.userName = userName;
this.password = password;
}
public static User getInstance() {
return user;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
Test 类
测试类,把 User 的单例实例序列化后在反序列化
public class Test{
public static void main(String[] args) throws Exception {
File file = new File("d:\\a.user");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(User.getInstance());
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
User user = (User) ois.readObject();
System.out.println(user);
if(user==User.getInstance()){
System.out.println("同一个实例");
}else{
System.out.println("不同的实例");
}
}
}
执行结果如下:
输出的结果:
User [userName=zhangsan, password=123456]
不同的实例
通过结果可以看出,单例模式的饿汉模式也无法确保对象实例是单例的。
那么我们应该怎么解决这个问题呢?
readResolve() 方法
public class User implements Serializable {
private static final long serialVersionUID = 3380014540967816490L;
private String userName;
private String password;
private static User user = new User("zhangsan", "123456");
private User(String userName, String password) {
this.userName = userName;
this.password = password;
}
public static User getInstance() {
return user;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
public Object readResolve(){
return getInstance();
}
@Override
public String toString() {
return "User [userName=" + userName + ", password=" + password + "]";
}
}
我们在 User 类中添加了一个 readResolve() 方法,该方法直接返回单例中的示例。
然后在执行 Test.main() 方法
执行结果如下:
输出的结果:
User [userName=zhangsan, password=123456]
相同的实例
无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。
网友评论