为什么Java有瞬时字段?


当前回答

Java中的transient关键字用于指示字段不应该是序列化(这意味着保存,就像保存到文件)过程的一部分。

摘自Java语言规范,Java SE 7版,第8.3.1.3节。瞬态场:

变量可以标记为瞬态表明它们不是对象的持久状态。

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

这里有一个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字段被标记为瞬态,因此只序列化原始图像,而不是同时保存原始图像和缩略图图像。这意味着需要更少的存储空间来保存序列化对象。(当然,这可能是可取的,也可能不是可取的,这取决于系统的要求——这只是一个示例。)

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

有关更多信息,《发现Java序列化API的秘密》一文(最初在Sun开发者网络上提供)中有一节讨论了transient关键字的使用,并介绍了使用transient关键字阻止某些字段序列化的场景。

其他回答

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

在我回答这个问题之前,我需要解释序列化,因为如果你理解了它在科学计算机中的含义,你可以很容易地理解这个关键词。

当对象通过网络传输/保存在物理介质(文件,…)上时,该对象必须“序列化”。序列化转换字节状态对象序列。这些字节在网络上发送/保存,并根据这些字节重新创建对象。

例子:

public class Foo implements Serializable 
{
 private String attr1;
 private String attr2;
 ...
}

现在,如果这个类中有一个字段不想传输或保存,可以使用transient关键字

private transient attr2;

这防止在序列化类时包含字段表单。

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

什么是序列化?

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

现在什么是瞬时关键字及其用途?

默认情况下,对象的所有变量都转换为持久状态。在某些情况下,您可能希望避免持久化某些变量,因为您不需要持久化这些变量。因此,您可以将这些变量声明为瞬时变量。如果变量被声明为瞬时变量,那么它将不会被持久化。这是transient关键字的主要目的。

我想用以下示例(借用本文)解释以上两点:

打包javabeat.samples;导入java.io.FileInputStream;导入java.io.FileOutputStream;导入java.io.IOException;导入java.io.ObjectInputStream;导入java.io.ObjectOutputStream;import java.io.Serializable;类NameStore实现了Serializable{private字符串firstName;private transient String middleName;private String lastName;public NameStore(字符串fName、字符串mName、字符串lName){this.firstName=fName;this.middleName=mName;this.lastName=lName;}公共字符串到字符串(){StringBuffer sb=新StringBuffer(40);sb.append(“名字:”);某人附加(这个名字);sb.append(“中间名:”);某人附加(这个中间名);sb.append(“姓氏:”);sb.append(this.lastName);return sb.toString();}}公共类TransientExample{公共静态void main(Stringargs[])引发异常{NameStore NameStore=新的NameStore(“Steve”、“Middle”、“Jobs”);ObjectOutputStream o=新的ObjectOutputStream(新的FileOutputStream(“nameStore”));//写入对象o.writeObject(nameStore);o.关闭();//从对象读取ObjectInputStream in=新ObjectInputStream(新FileInputStream(“nameStore”));NameStore nameStore1=(NameStore)在.readObject()中;System.out.println(nameStore1);}}

输出如下:

名字:Steve中间名:空姓氏:Jobs

Middle Name声明为transient,因此不会存储在持久存储中。

本机java系统以外的序列化系统也可以使用此修饰符。例如,Hibernate不会持久化标记为@Transient或Transient修饰符的字段。Terracotta也尊重这个修饰语。

我认为修饰语的比喻意义是“此字段仅供内存使用。不要以任何方式将其持久化或移出此特定VM。它不可移植”。也就是说,你不能依赖它在另一个VM内存空间中的值。与volatile非常类似,意味着您不能依赖特定的内存和线程语义。

使用瞬时修饰符声明的字段将不会参与序列化过程。当对象被序列化(以任何状态保存)时,其瞬态字段的值在串行表示中被忽略,而瞬态字段以外的字段将参与序列化过程。这是transient关键字的主要目的。