Serialization: Understand 'readResolve'

What are the writeReplace() and readResolve() methods used for?

These methods are used to allow an object to provide an alternative representation for itself within an ObjectStream. Consider for instance the common means of implementing an enumerated type:

public class Gender implements Serializable {
  public final static Gender MALE   = new Gender("Male");
  public final static Gender FEMALE = new Gender("Female");

  private String name;

  private Gender(String name) {
    this.name = name;
  }
}

This works fine within one JVM; there will be at most two Gender objects created, no matter how often you use Gender.MALE and Gender.FEMALE in your code. However, consider what happens when an instance of this class is serialized across JVMs. The ObjectInputStream will create a new instance of Gender that has the same value as the original instance. So, if you have many thousands objects that have been de-serialized via RMI you might end up with many thousands of extra instances of Gender. The writeReplace() and readResolve() methods are the hook to solve this problem.

One way of eliminating the extra instances and some of the unnecessary heap allocation would be to do something like this:

public class Gender implements Serializable {
  public final static Gender MALE   = new Gender("Male");
  public final static Gender FEMALE = new Gender("Female");

  private String name;

  private Gender(String name) {
    this.name = name;
  }

  Object writeReplace() throws ObjectStreamException {
    if (this.equals(MALE)) {
      return SerializedForm.MALE_FORM;
    } else {
      return SerializedForm.FEMALE_FORM;
    }
  }

  private static class SerializedForm implements Serializable {

    final static SerializedForm MALE_FORM   = new SerializedForm(0);
    final static SerializedForm FEMALE_FORM = new SerializedForm(1);

    private int value;

    SerializedForm(int value) {
      this.value = value;
    }

    Object readResolve() throws ObjectStreamException {
      if (value == MALE_FORM.value) {
        return Gender.MALE;
      } else {
        return Gender.FEMALE;
      }
    }
  }
}

This also guarantees that in all cases where genderInstance.equals(MALE) is true,genderInstance == Gender.MALE is also true.

Serialization is a handy and powerful aspect of Java. Being able to persist objects onto disk and read them later is one of the most under-used features of Java I think. In the base cases, serialization can ‘just work‘. However, as more complicated object formats and design patterns are adopted, the likelihood that ‘transparent‘ object serialization will ‘just work‘ becomes less and less likely. One case where serialization needs a little help is when dealing with a controlled set of instances - such as singletons and enumerations.

Whenever a singleton is serializable, it‘s important to ensure that the singleton instance is used. This is done through thereadResolve method. For instance, a singleton may look like this:

public final class MySingleton {
 private MySingleton() { }
 private static final MySingleton INSTANCE = new MySingleton();
 public static MySingleton getInstance() { return INSTANCE; }
}

In the above example, there is only one way to get an instance of MySingleton - that is to use the getInstance() method. Unfortunately, this code becomes ‘broken‘ simply by adding one interface implementation:

public final class MySingleton implements Serializable {
//...

Now through the serializable tools, someone can write a singleton instance to disk, and then read it back up, effectively getting a new instance. Even though the constructor is private, the serializable tools have special access to create instances of a class regardless. Serialization has a special hook it uses - a private method on the class being instantiated called readResolve() - which is meant to supply a ‘hook‘ for a class developer to ensure that they have a say in what object is returned by serialization. Oddly enough, readResolve() is not static, but is instead invoked on the new instance just created by the serialization. We‘ll get into that in a minute - for now, here is how our readResolve() method works with our singleton:

public final class MySingleton {
 private MySingleton() { }
 private static final MySingleton INSTANCE = new MySingleton();
 public static MySingleton getInstance() { return INSTANCE; }
 private Object readResolve() throws ObjectStreamException {
  // instead of the object we‘re on,
  // return the class variable INSTANCE
  return INSTANCE;
 }
}

So far so good. Things get a little complicated when dealing with more than one instance however. To explain this, I‘ll show this using a type-safe enumeration. Keep in mind that Java 5‘s enum type automatically handles this readResolve case for you. Here is a nice little enumeration:

public final class Sides {
 private int value;
 private Sides(int newVal) { value = newVal; }
 private static final int LEFT_VALUE = 1;
 private static final int RIGHT_VALUE = 2;
 private static final int TOP_VALUE = 3;
 private static final int BOTTOM_VALUE = 4;

 public static final LEFT = new Sides(LEFT_VALUE);
 public static final RIGHT = new Sides(RIGHT_VALUE);
 public static final TOP = new Sides(TOP_VALUE);
 public static final BOTTOM = new Sides(BOTTOM_VALUE);

}

Now, implementing serialization, the key to determining which instance to return is in inspecting what value is set on the object itself:

public final class Sides implements Serializable {
 private int value;
 private Sides(int newVal) { value = newVal; }
 private static final int LEFT_VALUE = 1;
 private static final int RIGHT_VALUE = 2;
 private static final int TOP_VALUE = 3;
 private static final int BOTTOM_VALUE = 4;

 public static final LEFT = new Sides(LEFT_VALUE);
 public static final RIGHT = new Sides(RIGHT_VALUE);
 public static final TOP = new Sides(TOP_VALUE);
 public static final BOTTOM = new Sides(BOTTOM_VALUE);

 private Object readResolve() throws ObjectStreamException {
  // Switch on this instance‘s value to figure out which class variable
  // this is meant to match
  switch(value) {
   case LEFT_VALUE: return LEFT;
   case RIGHT_VALUE: return RIGHT;
   case TOP_VALUE: return TOP;
   case BOTTOM_VALUE: return BOTTOM;
  }
  return null;
 }
}

Serialization: Understand 'readResolve'

时间: 2024-08-27 10:56:56

Serialization: Understand 'readResolve'的相关文章

understand的安装

1.win7 64位下安装 1)下载Understand.4.0.908.x64.rar. 2)解压之,直接运行里面的Understand-4.0.908-Windows-64bit.exe. 3)选择如下 之后,点击"Add Eval or SDL (RegCode)",如下图: 4)出现如下界面 进入破解程序生成code,如下 将生成的code填入即可,可以不用填写email.ok! 2.Linux下安装understand 为了能方便的看代码,想安装 Scientific Too

Lintcode7 Binary Tree Serialization solution 题解

[题目描述] Design an algorithm and write code to serialize and deserialize a binary tree. Writing the tree to a file is called 'serialization' and reading back from the file to reconstruct the exact same binary tree is 'deserialization'. 设计一个算法,并编写代码来序列化

Understand the Qt containers(有对应表)

Container classes are one of the cornerstones of object-oriented programming, invaluable tools that free us from having to permanently think about memory management. Qt comes with its own set of container classes, closely modeled after those in the S

leetcode 331. Verify Preorder Serialization of a Binary Tree

传送门 331. Verify Preorder Serialization of a Binary Tree My Submissions QuestionEditorial Solution Total Accepted: 10790 Total Submissions: 34071 Difficulty: Medium One way to serialize a binary tree is to use pre-order traversal. When we encounter a

【LeetCode】Verify Preorder Serialization of a Binary Tree(331)

1. Description One way to serialize a binary tree is to use pre-order traversal. When we encounter a non-null node, we record the node's value. If it is a null node, we record using a sentinel value such as #. _9_ / 3 2 / \ / 4 1 # 6 / \ / \ / # # #

Java序列化(Serialization)

关于Java的序列化的文章在网上已经够多了,在这里写关于Java序列化的文章是对自己关于这方面的的一种总结,结合以前的开发经验与网上的资料,写了这篇文章,对自己是有着巩固记忆的作用,也希望能够对大家有一定帮助. 一.什么是序列化(Serialization)? 序列化是Java提供的一种机制,将对象转化成字节序列,在字节序列中保存了对象的数据.对象的类型的信息与存储在对象中的数据的类型.序列化实际上就是将保存对象的"状态",可以方便以后的程序使用或者通过网络传输到另一台主机使用.一般来

Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptabl

在使用AFNetworking 2.0  的时候本来一切很顺畅,但是中途遇到几个比较坑的地方 这里分享一下爬坑经历,忘读者不能速爬坑! 在发送请求后,NSURLSessionDataTask一直报错 Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: text/html" AFURLResponseSerializat

微软自带的Serialization和Newtonsoft简单测试

刚刚对这两个进行了一下小小的测试 发现 当转换的内容少的时候  微软自带的比Newtonsoft速度要快一些,内容多的时候反之,当内容多到一定量的时候微软自带的就不能转换了,需要修改一下MaxJsonLength的默认值,此处我改为:999999999,这个时候可以转换了,不过时间很长. 添加50个对象: 添加500个对象: 添加5K对象: 添加5W对象: 添加50W对象: 50W,修改完默认值: 添加500W 对象: 添加700W对象: 微软自带的出不来了,报错了,这次我是把Newtonsof

Java序列化(Serialization)的理解

1.什么是序列化 Java是面向对象的编程语言,有时需要保存对象,并在下次使用时可以顺利还原该对象.由于这种需求很常见,所以Java API对此提供了支持,添加相关程序代码到标准类库中,并将保存和还原的过程称之为"对象序列化". Java SE7 文档中将与对象序列化的相关内容做了详细表述,将其称为: "Java对象序列化规范"  Java Object Serialization Specification,网址为: http://docs.oracle.com/