Java - 为什么瞬态成员变量在Java标准库中如此广泛使用?

时间:2015-11-11 09:29:25

标签: java transient

在查看了一些Java Collection类的源代码之后,我发现使用transient总是修改成员变量。

例如,LinkedList源代码:

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    private transient Entry<E> header = new Entry<E>(null, null, null);
    private transient int size = 0;

    public LinkedList() 
    {
        header.next = header.previous = header;
    }

    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

    // ...other stuff
}

当然,不仅LinkedList使用瞬态,几乎每个Java集合类都使用transient来修改其成员变量的至少一半。

所以,我的问题是:为什么transient在Java标准库中使用如此广泛

(当然每个人都知道transient的定义和用法,但这不是我的问题:)

2 个答案:

答案 0 :(得分:7)

从序列化的角度来看 序列化整个对象时,不会序列化瞬态变量。

如果您不希望某些变量被序列化,那么您可以将其变为瞬态

从你的例子中,LinkedList是序列化的。

如果仔细观察,所有变为瞬态的变量都会以编程方式进行维护。所以没有必要坚持下去。

例如size,当您回读任何序列化对象时,您只需阅读Node<E>并以编程方式维护大小。因此无需序列化size。请记住LinkedList的实际数据不是size。如果您拥有entries的实际数据,您可以随时计算其大小,并且这样更容易。

如需参考,请查看。

@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Read in any hidden serialization magic
    s.defaultReadObject();

    // Read in size
    int size = s.readInt();

    // Read in all elements in the proper order.
    for (int i = 0; i < size; i++)
        linkLast((E)s.readObject());
}

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

答案 1 :(得分:1)

当你编写一个非平凡的可序列化类(比POJO复杂得多的东西)时,通常最好将类代表的内容与实际实现分开。

执行此操作的一种方法是通过transient字段并提供readObject()/writeObject()方法来控制以序列化形式写入的值以及如何在反序列化时初始化transient字段。

更强大的解决方案是序列化代理,它们实际上使用readResolve()/writeReplace()来序列化完全不同的对象。 (可以在EnumSet中找到此模式的示例。)

这些技术的主要优点是它们允许您更改实现,而无需更改类的序列化形式。这是一件好事,因为如果您正在编写API,API对象的序列化形式是公共API的一部分,它代表了一个承诺,即使用以前版本序列化的对象将在以后的版本中反序列化。 (或者你的API最终会像Swing一样,所有类Javadoc都会在其中携带an ugly warning。)

它们还提供一些保护,防止恶意制作的序列化对象破坏您的类不变量。 (想象一下,如果链接列表按原样序列化,并且有人修改了结果,以便条目指向自身作为其后继,导致迭代在无限循环中运行。)