题 为什么Java有瞬态字段?


为什么Java有 短暂的 字段?


1202
2018-05-26 12:11


起源




答案:


transient Java中的关键字用于指示不应序列化字段。

来自 Java语言规范,Java SE 7版第8.3.1.3节。 transient 字段

可以标记变量 transient 至   表明他们不属于   对象的持久状态。

例如,您可能具有从其他字段派生的字段,并且只应以编程方式执行,而不是通过序列化保持状态。

这是一个 GalleryImage 包含图像的类和从图像派生的缩略图:

class GalleryImage implements Serializable
{
    private Image image;
    private transient Image thumbnailImage;

    private void generateThumbnail()
    {
        // Generate thumbnail.
    }

    private void readObject(ObjectInputStream inputStream)
            throws IOException, ClassNotFoundException
    {
        inputStream.defaultReadObject();
        generateThumbnail();
    }    
}

在这个例子中, thumbnailImage 是通过调用生成的缩略图图像 generateThumbnail 方法。

thumbnailImage 字段标记为 transient,所以只有原版 image 是序列化而不是保持原始图像和缩略图图像。这意味着保存序列化对象所需的存储空间更少。 (当然,根据系统的要求,这可能是也可能不合适 - 这只是一个例子。)

在反序列化时, readObject 调用方法以执行将对象的状态恢复回序列化发生状态所需的任何操作。这里需要生成缩略图,所以 readObject 重写方法,以便通过调用生成缩略图 generateThumbnail 方法。

有关其他信息,请 发现Java Serialization API的秘密 文章(最初在Sun Developer Network上提供)有一个部分讨论了使用和呈现的场景 transient keyword用于防止某些字段的序列化。


1414
2018-05-26 12:53



但为什么它是一个关键字,而不是一个注释 @DoNotSerialize? - Elazar Leibovich
我想,这是在Java中没有注释的时候拥有的。 - Peter Wippermann
我觉得奇怪的是serializable是Java内部的。它可以实现为需要用户覆盖读取和写入方法的接口或抽象类。 - caleb
@MJafar:readObject通常链接到反序列化机制,因此自动调用。此外,在许多情况下,您不需要覆盖它 - 默认实现可以解决问题。 - Mike Adler
@caleb可能是因为由于缺少无符号整数而在Java中处理二进制格式非常痛苦。 - rightfold


在了解之前 transient 关键字,必须要了解序列化的概念。如果读者知道序列化,请跳过第一点。

什么是序列化?

序列化是使对象的状态持久化的过程。这意味着对象的状态被转换为字节流并存储在文件中。以同样的方式,我们可以使用反序列化从字节中恢复对象的状态。这是Java编程中的重要概念之一,因为序列化主要用于网络编程。需要通过网络传输的对象必须转换为字节。为此,每个类或接口都必须实现 Serializable 接口。它是没有任何方法的标记界面。

现在是什么 transient 关键字及其目的?

默认情况下,所有对象的变量都转换为持久状态。在某些情况下,您可能希望避免持久化某些变量,因为您不需要保留这些变量。所以你可以将这些变量声明为 transient。如果变量声明为 transient,那就不会坚持下去了。那是主要目的 transient 关键词。

我想通过以下示例解释上述两点:

package javabeat.samples;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class NameStore implements Serializable{
    private String firstName;
    private transient String middleName;
    private String lastName;

    public NameStore (String fName, String mName, String lName){
        this.firstName = fName;
        this.middleName = mName;
        this.lastName = lName;
    }

    public String toString(){
        StringBuffer sb = new StringBuffer(40);
        sb.append("First Name : ");
        sb.append(this.firstName);
        sb.append("Middle Name : ");
        sb.append(this.middleName);
        sb.append("Last Name : ");
        sb.append(this.lastName);
        return sb.toString();
    }
}

public class TransientExample{
    public static void main(String args[]) throws Exception {
        NameStore nameStore = new NameStore("Steve", "Middle","Jobs");
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("nameStore"));
        // writing to object
        o.writeObject(nameStore);
        o.close();

        // reading from object
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("nameStore"));
        NameStore nameStore1 = (NameStore)in.readObject();
        System.out.println(nameStore1);
    }
}

输出将如下:

First Name : Steve
Middle Name : null
Last Name : Jobs

中间名字 被宣布为 transient,因此它不会存储在持久存储中。

资源


355
2018-05-26 12:13



此示例取自此代码,您可以在此处阅读:javabeat.net/2009/02/what-is-transient-keyword-in-java - Krishna
这部分让我觉得奇怪而且可能令人困惑:“这意味着对象的状态被转换为字节流和 存储在一个文件中“在我看来,大多数时候序列化都不涉及写入文件(例如:后面的网络示例) - Garcia Hurtado
这个例子很糟糕,因为中间名字很明显 不 短暂的财产。 - Raphael


允许您定义不想序列化的变量。

在一个对象中,您可能拥有不想序列化/持久化的信息(可能是对父工厂对象的引用),或者序列化可能没有意义。将这些标记为“瞬态”意味着序列化机制将忽略这些字段。


80
2018-03-18 22:04





我的小贡献:

什么是Java中的瞬态变量?
在简单的句子中,任何使用transient关键字修改的变量都会成为java中的瞬态变量。

为什么我们需要java中的瞬态变量?
Transient关键字为您提供了对序列化过程的一些控制,并使您可以灵活地从序列化过程中排除某些对象属性。有些时候,不管序列化对象的某些属性是否合理,我们将看到哪些变量不应该被序列化,并且应该在下一节中变为瞬态。

你应该在哪个变量标记瞬态?
由于我们知道临时关键字或具有瞬态变量的目的,因此考虑哪个变量应该标记为瞬态是有意义的。我的规则是,任何其值都可以从其他变量计算的变量不需要保存。例如,如果您有一个名为“interest”的字段,其值可以从其他字段派生,例如原则,速率,时间等,然后没有必要序列化它。
另一个例子是单词计数,如果您正在保存文章,则无需保存单词计数,因为它可以在文章被反序列化时创建。 transient关键字的另一个很好的例子是“Logger”,因为大部分时间你都有用于登录Java的logger实例,但你当然不希望它序列化正确吗?


27
2018-05-26 12:15



你的“简单句子”只是一个重言式。它什么都没解释。没有它你会好起来的。 - user207421


一个 transient 变量是可能未序列化的变量。

想到这可能有用的一个例子是,变量只在特定对象实例的上下文中有意义,并且在序列化和反序列化对象后变为无效。在这种情况下,将这些变量变为有用 null 相反,以便您可以在需要时使用有用的数据重新初始化它们。


21
2018-03-26 14:16





transient 用于表示不需要序列化类字段。 可能最好的例子是 Thread 领域。通常没有理由序列化 Thread,因为它的状态非常“特定于流量”。


13
2018-05-26 12:15



如果我错了,请纠正我,但Thread不可序列化,所以它会被跳过吗? - TFennis
@TFennis:如果是可序列化的类 A 引用不可序列化的类 B (喜欢 Thread 在你的例子中),然后 A 必须将引用标记为 transient XOR必须覆盖默认的序列化过程才能执行合理的操作 B XOR假设只有可序列化的子类 B 实际引用(因此实际的子类必须照顾他们的“坏”父级 B)XOR接受序列化将失败。只有一种情况(标记为瞬态) B 自动并静默跳过。 - A.H.
@TFennis不,它会引起异常。 - user207421
@ A.H。:为什么选择XOR?我认为执行这些事情的任何组合的代码都可以工作,并且某些组合可能是有用的(例如,即使仅引用B的可序列化子类,覆盖默认序列化过程也可能是有用的,反之亦然)。 - supercat


因为并非所有变量都具有可序列化的性质


10
2018-05-28 02:20



当您给出答案时,请提供更多信息。 - Gynnad


除本机java之外的序列化系统也可以使用此修饰符。例如,Hibernate不会持久化标记为的字段 @短暂的 或者 短暂的修改。兵马俑也尊重这个修饰符。

我相信修饰符的比喻意思是“此字段仅供内存使用。不要以任何方式持久存在或将其移出此特定VM。它不可移植”。即你不能在另一个VM内存空间中依赖它的值。很像 挥发物 意味着你不能依赖某些内存和线程语义。


10
2017-12-22 08:20



我觉得 transient 如果它是在此时设计的,它将不是关键字。他们可能会使用注释。 - Joachim Sauer