`
onedaykoo
  • 浏览: 25377 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Externalizable的替代方法

    博客分类:
  • J2SE
 
阅读更多
Externalizable的替代方法
若不是特别在意要实现Externalizable接口,还有另一种方法可供选用。我们可以实现Serializable接口,并添加(注意是“添加”,而非“覆盖”或者“实现”)名为writeObject()和readObject()的方法。一旦对象被序列化或者重新装配,就会分别调用那两个方法。也就是说,只要提供了这两个方法,就会优先使用它们,而不考虑默认的序列化机制。
这些方法必须含有下列准确的签名:

private void
  writeObject(ObjectOutputStream stream)
    throws IOException;

private void
  readObject(ObjectInputStream stream)
    throws IOException, ClassNotFoundException

从设计的角度出发,情况变得有些扑朔迷离。首先,大家可能认为这些方法不属于基础类或者Serializable接口的一部分,它们应该在自己的接口中得到定义。但请注意它们被定义成“private”,这意味着它们只能由这个类的其他成员调用。然而,我们实际并不从这个类的其他成员中调用它们,而是由ObjectOutputStream和ObjectInputStream的writeObject()及readObject()方法来调用我们对象的writeObject()和readObject()方法(注意我在这里用了很大的抑制力来避免使用相同的方法名——因为怕混淆)。大家可能奇怪ObjectOutputStream和ObjectInputStream如何有权访问我们的类的private方法——只能认为这是序列化机制玩的一个把戏。
在任何情况下,接口中的定义的任何东西都会自动具有public属性,所以假若writeObject()和readObject()必须为private,那么它们不能成为接口(interface)的一部分。但由于我们准确地加上了签名,所以最终的效果实际与实现一个接口是相同的。
看起来似乎我们调用ObjectOutputStream.writeObject()的时候,我们传递给它的Serializable对象似乎会被检查是否实现了自己的writeObject()。若答案是肯定的是,便会跳过常规的序列化过程,并调用writeObject()。readObject()也会遇到同样的情况。
还存在另一个问题。在我们的writeObject()内部,可以调用defaultWriteObject(),从而决定采取默认的writeObject()行动。类似地,在readObject()内部,可以调用defaultReadObject()。下面这个简单的例子演示了如何对一个Serializable对象的存储与恢复进行控制:

//: SerialCtl.java
// Controlling serialization by adding your own
// writeObject() and readObject() methods.
import java.io.*;

public class SerialCtl implements Serializable {
  String a;
  transient String b;
  public SerialCtl(String aa, String bb) {
    a = "Not Transient: " + aa;
    b = "Transient: " + bb;
  }
  public String toString() {
    return a + "\n" + b;
  }
  private void
    writeObject(ObjectOutputStream stream)
      throws IOException {
    stream.defaultWriteObject();
    stream.writeObject(b);
  }
  private void
    readObject(ObjectInputStream stream)
      throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
    b = (String)stream.readObject();
  }
  public static void main(String[] args) {
    SerialCtl sc =
      new SerialCtl("Test1", "Test2");
    System.out.println("Before:\n" + sc);
    ByteArrayOutputStream buf =
      new ByteArrayOutputStream();
    try {
      ObjectOutputStream o =
        new ObjectOutputStream(buf);
      o.writeObject(sc);
      // Now get it back:
      ObjectInputStream in =
        new ObjectInputStream(
          new ByteArrayInputStream(
            buf.toByteArray()));
      SerialCtl sc2 = (SerialCtl)in.readObject();
      System.out.println("After:\n" + sc2);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~

在这个例子中,一个String保持原始状态,其他设为transient(临时),以便证明非临时字段会被defaultWriteObject()方法自动保存,而transient字段必须在程序中明确保存和恢复。字段是在构建器内部初始化的,而不是在定义的时候,这证明了它们不会在重新装配的时候被某些自动化机制初始化。
若准备通过默认机制写入对象的非transient部分,那么必须调用defaultWriteObject(),令其作为writeObject()中的第一个操作;并调用defaultReadObject(),令其作为readObject()的第一个操作。这些都是不常见的调用方法。举个例子来说,当我们为一个ObjectOutputStream调用defaultWriteObject()的时候,而且没有为其传递参数,就需要采取这种操作,使其知道对象的句柄以及如何写入所有非transient的部分。这种做法非常不便。
transient对象的存储与恢复采用了我们更熟悉的代码。现在考虑一下会发生一些什么事情。在main()中会创建一个SerialCtl对象,随后会序列化到一个ObjectOutputStream里(注意这种情况下使用的是一个缓冲区,而非文件——与ObjectOutputStream完全一致)。正式的序列化操作是在下面这行代码里发生的:
o.writeObject(sc);
其中,writeObject()方法必须核查sc,判断它是否有自己的writeObject()方法(不是检查它的接口——它根本就没有,也不是检查类的类型,而是利用反射方法实际搜索方法)。若答案是肯定的,就使用那个方法。类似的情况也会在readObject()上发生。或许这是解决问题唯一实际的方法,但确实显得有些古怪。
分享到:
评论

相关推荐

    程序员笔试题目(JAVA).doc

    - ConcurrentHashMap:线程安全的HashMap替代品,提供更高的并发性能。 【线程方法】 - sleep():使当前线程暂停指定时间,不会释放持有的锁。 - wait():使线程等待,释放锁,直到其他线程调用notify()或...

    对象流(序列化)

    - 使用`writeObject()`和`readObject()`方法替代`writeFields()`和`readFields()`,可以避免序列化整个对象,只序列化必要的字段。 - 对于大型或复杂对象,考虑使用` farkle.SerializationUtils`等第三方库进行更...

    sonar检查规则指南

    - **解决方案**:使用`Arrays.toString(args)`和`Arrays.hashCode(args)`来替代。 ##### 15. "instanceof" operators that always return "true" or "false" should be removed - **解释**:某些情况下,`...

    java序列化

    序列化可能会导致大量的I/O操作,因此在处理大量数据时,可以考虑使用`ObjectStreamClass`来优化性能,或者使用`Externalizable`接口,完全控制序列化过程。 7. **安全性**: 序列化可能引发安全问题,因为任何...

    Java存储与读取对象.7z

    - 使用`Externalizable`接口代替`Serializable`,可以自定义序列化和反序列化逻辑。 总之,Java对象的存储与读取是通过序列化和反序列化实现的,这涉及到对`Serializable`接口的理解,以及`ObjectOutputStream`和`...

    Java存储与读取对象源码

    此外,Java提供了`Externalizable`接口作为`Serializable`的替代,允许自定义序列化和反序列化的行为,但这需要程序员手动编写更多的代码。 另外,如果你的类中包含不希望序列化的字段,可以使用`transient`关键字...

    epam_task4_serialization

    - 可以使用`readResolve()`和`writeReplace()`方法来控制序列化和反序列化过程,例如返回替代的实例或处理特殊类型的对象。 **7. 知识扩展** - Java还提供了`Externalizable`接口,相比于`Serializable`,它允许...

Global site tag (gtag.js) - Google Analytics