我应该如何在java中读写包含List的对象?

时间:2015-09-16 16:10:36

标签: java list serialization

我有一个对象

public class Foo implements Serializable {
    private List<Bar> barList;
    // getters and setters
}

Bar也实现了Serializable。)

FindBugs违反了SE_BAD_FIELD。这是公平的,因为List不是transientSerializable。但是,我想序列化这个列表!所以我需要自己写writeObjectreadObject

我查看了JDK中ArrayList writeObject的实现,这非常简单,但也比我想象的要复杂一些。这是:

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

我曾预料到它会在上面做一个名为“按正确顺序写出所有元素”的部分。但它似乎比那复杂一点。我是否需要将这些内容复制到writeObjectFoo的实现中?

  • 如果我要生成一个新的ArrayList来写对象,我是否需要调用default write?列表本身的私有字段可能不需要保留。
  • 当大小写入默认写入时,单独写入大小有什么意义?在相应的readObject实现中忽略该大小。
  • 由于我无法访问受保护的modCount字段,有没有办法复制modCount行为?我应该将writeObject放在synchronized区块内吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

如果barList是私有的并且不需要逐字设置,则快速解决方案是将其声明为private ArrayList<Bar> barList之类的可序列化类型,并使用以下内容对其进行修改:

void addBar(Bar bar) { barList.add(bar); }
void setBarList(List<Bar> bars) { barList = new ArrayList(bars); }

但是如果由于某种原因必须自己编写序列化,则需要编写大小和每个对象(使用Externalizable,它比Serializable更快)。基于Oracle的example

class Foo implements Externalizable {
  List<Bar> barList;

  public void writeExternal(ObjectOutput out) throws IOException {
    if (barList == null) {
      out.writeInt(-1);
      return;
    }
    out.writeInt(barList.size());
    for (Bar bar : barList) {
      out.writeObject(bar);
    }
  }

  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    int size = in.readInt();
    if (size >= 0) {
      barList = new ArrayList<>();
      for (int i = 0; i < size; i++) {
        barList.add((Bar) in.readObject());
      }
    }
  }
}

请注意,readExternal()仍然使用ArrayList。如果要保留列表的类,还可以在writeExternal()中编写类名,并使用readExternal()中的反射对其进行实例化。这需要在该类中使用no-args构造函数。

答案 1 :(得分:0)

FindBugs警告你,因为实际的具体List 可能不是Serializable。这并不意味着它不是。如果您确定实际列表确实在运行时可序列化(如ArrayList,LinkedList和许多其他List实现),那么您不需要做任何事情。

确保这一点的一个好方法是避免从外部接受任何List作为参数:

// ArrayList is serializable
private List<Bar> barList = new ArrayList<>();

public void setBarList(List<Bar> barList) { 
    // barList stays an ArrayList, still serializable
    this.barList.clear();
    this.barList.addAll(barList);
}