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

Posted

技术标签:

【中文标题】Java - 为啥瞬态成员变量在 Java 标准库中使用如此广泛?【英文标题】:Java - why transient member variables used so widely in Java standard library?Java - 为什么瞬态成员变量在 Java 标准库中使用如此广泛? 【发布时间】:2016-02-12 08:54:50 【问题描述】:

查看了一些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使用transient,几乎每个Java集合类都使用transient来修改至少一半的成员变量。

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

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

【问题讨论】:

这可能是一个更好的副本:***.com/questions/9848129/… 或者这个:***.com/questions/25926012/… 【参考方案1】:

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

当你不希望某个变量被序列化时,你可以将其设为瞬态

从您的示例 LinkedList 是可序列化的。如果您仔细查看,所有瞬态变量都是以编程方式维护的。所以没有必要坚持他们。

例如size,当您读取任何序列化对象时,您只是读取Node&lt;E&gt; 并以编程方式保持大小。所以不需要序列化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++;

【讨论】:

因此,例如,由于当前头指向的节点对于刚刚解散的LinkedList 并不重要,所以我们不必在序列化 while 对象时保留这些信息?跨度> 是的,关于size,我可以这么说,如果你看到linkLast()的方法你就会明白。但是我这里的源代码没有header。您实际阅读的是哪个版本?【参考方案2】:

当您编写一个重要的可序列化类(比 POJO 复杂得多的东西)时,将类表示的内容与实际实现分离通常是一个好主意。

一种方法是通过transient 字段并提供readObject()/writeObject() 方法来控制在序列化表单中写入哪些值以及如何在反序列化时初始化transient 字段。

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

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

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

【讨论】:

以上是关于Java - 为啥瞬态成员变量在 Java 标准库中使用如此广泛?的主要内容,如果未能解决你的问题,请参考以下文章

非瞬态类成员的 Java PMD 警告

为啥Java有瞬态字段?

为啥杰克逊还要序列化瞬态成员?

Java中静态最终瞬态的作用是啥?

java中瞬态变量的任何实时示例

JAVA接口中成员变量必须是final类型的,为啥