在java中实现我自己的序列化

时间:2011-02-08 04:34:26

标签: java serialization

如何自行实现序列化。意思是我不希望我的类实现可序列化。但我想自己实现序列化。因此,如果不实现可序列化,我可以通过网络传输对象或将它们写入文件,然后在相同的状态下检索它们。我想这样做,因为我想学习和探索事物。

5 个答案:

答案 0 :(得分:6)

序列化是将对象结构转换为另一种格式的过程,该格式可以很容易地通过网络传输或者可以存储在文件中。 Java将对象序列化为二进制格式。如果带宽/磁盘空间不是问题,则不需要这样做。您可以简单地将对象编码为XML:

// Code is for illustration purpose only, I haven't compiled it!!!

public class Person {
    private String name;
    private int age;
    // ...

   public String serializeToXml() {
       StringBuilder xml = new StringBuilder();   
       xml.append("<person>");
       xml.append("<attribute name=\"age\" type=\"int\">").append(age);
       xml.append("</attribute>");
       xml.append("<attribute name=\"name\" type=\"string\">").append(name);
       xml.append("</attribute>"); 
       xml.append("</person>");
       return xml.toString(); 
}

现在,您可以获取对象的XML表示形式,并将其“序列化”为文件或网络连接。用任何语言编写的可以解析XML的程序都可以将该对象“反序列化”为自己的数据结构。

如果需要更紧凑的表示,可以考虑二进制编码:

  // A naive binary serializer. 
  public byte[] serializeToBytes() {
      ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 

      // Object name and number of attributes.
      // Write the 4 byte length of the string and the string itself to
      // the ByteArrayOutputStream.
      writeString("Person", bytes);
      bytes.write(2); // number of attributes;

      // Serialize age
      writeString("age", bytes);
      bytes.write(1); // type = 1 (i.e, int)
      writeString(Integer.toString(age), bytes);  

      // serialize name
      writeString("name", bytes);
      bytes.write(2); // type = 2 (i.e, string) 
      writeString(name, bytes);

      return bytes.toByteArray();
  }

  private static void writeString(String s, ByteArrayOutputStream bytes) {
      bytes.write(s.length());
      bytes.write(s.toBytes());
  }

要了解更紧凑的二进制序列化方案,请参阅Google Protocol Buffers的Java实现。

答案 1 :(得分:1)

您可以使用Externalizable并实现自己的序列化机制。序列化的一个难点是版本控制,因此这可能是一项具有挑战性的练习。您还可以将protobufAvro视为二进制序列化格式。

答案 2 :(得分:1)

你从反思开始。获取对象的类以及其类和所有超类的声明字段。然后获取每个字段的值并将其写入dump。

反序列化时,只需反转进程:从序列化表单中获取类名,实例化对象并根据转储设置其字段。

如果您只想学习,那就是简单的方法。如果你想“真实地”做这件事,可能会出现很多问题:

  • 版本。如果应用程序的一端运行新版本,但另一端有一个较旧的类定义,某些字段缺失或重命名该怎么办?
  • 覆盖默认行为。如果某个对象更复杂并且无法在逐个字段的基础上重新创建该怎么办?
  • 重新创建对象之间的依赖关系,包括循环对象。
  • ......可能还有更多。

答案 3 :(得分:0)

获取Java源代码并了解序列化的实现方式。我在一个月前做过这个,现在有一个序列化只使用16%的空间和20%的“正常”序列化时间,代价是假设编写序列化数据的类没有改变。我将它用于客户端 - 服务器序列化,我可以使用这个假设。

答案 4 :(得分:0)

作为@Konrad Garus回答的补充。有一个问题是完全重新实现Java序列化的显示阻止。

反序列化对象时,需要使用对象的类的构造函数之一来重新创建实例。但是你应该使用哪个构造函数?如果有一个no-args构造函数,你可以想象使用它。但是,除了创建它之外,no-args构造函数(或实际上任何构造函数)可能会对该对象执行某些操作。例如,它可能会向已创建新实例的其他内容发送通知...传递尚未完全反序列化的实例。

实际上,复制标准的Java反序列化代码确实很困难。它的作用是:

  1. 确定要创建的类。
  2. 创建类的实例,而不调用其任何构造函数
  3. 它使用反射来填充实例的字段,包括私有字段,以及从序列化重建的对象和值。
  4. 问题是第2步涉及一些普通Java类不允许做的“黑魔法”。

    (如果您想了解血腥细节,请阅读序列化规范并查看OpenJDK代码库中的实现。)