为什么Java有瞬态字段?

时间:2009-05-26 12:11:37

标签: java field transient

为什么Java有瞬态字段?

15 个答案:

答案 0 :(得分:1553)

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

来自Java Language Specification, Java SE 7 EditionSection 8.3.1.3. transient Fields

  

变量可能会被标记为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方法生成缩略图。

有关其他信息,Discover the secrets of the Java Serialization API文章(最初在Sun Developer Network上提供)的部分讨论了使用transient关键字以防止序列化的情况并提供了一个方案。某些领域。

答案 1 :(得分:396)

在理解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,因此它不会存储在持久存储中。

Source

答案 2 :(得分:81)

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

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

答案 3 :(得分:33)

我的小贡献:

什么是瞬态场?
基本上,使用transient关键字修改的任何字段都是一个瞬态字段。

为什么Java中需要瞬态字段?
transient关键字使您可以控制序列化过程,并允许您从此过程中排除某些对象属性。序列化过程用于持久化Java对象,主要是为了在传输或非活动状态时保留它们的状态。有时候,不要序列化某个对象的某些属性是有道理的。

您应该将哪些字段标记为瞬态?
现在我们知道transient关键字和瞬态字段的用途,了解哪些字段标记为瞬态非常重要。静态字段也没有序列化,因此相应的关键字也可以实现。但这可能会破坏你的课堂设计;这是transient关键字拯救的地方。我尽量不允许其值可以从其他字段派生的字段被序列化,所以我将它们标记为瞬态。如果您有一个名为interest的字段,其值可以从其他字段(principalratetime)计算得出,则无需对其进行序列化。 />
另一个很好的例子是文章字数。如果要保存整篇文章,则实际上不需要保存单词计数,因为可以在文章被“反序列化”时计算。或者想想伐木工; Logger实例几乎从不需要序列化,因此可以使它们变为瞬态。

答案 4 :(得分:23)

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

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

答案 5 :(得分:14)

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

答案 6 :(得分:13)

本机java之外的序列化系统也可以使用此修饰符。例如,Hibernate不会保留标有 @Transient 瞬态修饰符的字段。兵马俑也尊重这个修饰符。

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

答案 7 :(得分:8)

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

答案 8 :(得分:6)

在回答这个问题之前,我必须向您解释 SERIALIZATION ,因为如果您了解科学计算机中序列化的含义,您就可以轻松理解这个关键字。

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

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

现在,如果您不想转换 / 已保存此对象 SO 字段,则可以使用关键字 transient

private transient attr2;

Example

答案 9 :(得分:3)

当您不想共享序列化的敏感数据时,需要这样做。

答案 10 :(得分:0)

根据谷歌 暂时意义==只持续很短的时间;无常。

现在,如果你想在java中做一些短暂的瞬态关键字。

问:在哪里使用瞬态?

A:通常在java中,我们可以通过在变量中获取数据并将这些变量写入文件来将数据保存到文件中,此过程称为序列化。现在,如果我们想要避免将可变数据写入文件,我们会将该变量设置为瞬态。

transient int result=10;

注意:瞬态变量不能是本地的。

答案 11 :(得分:0)

简单地说,瞬态java关键字保护来自Serialize的字段作为非瞬态字段计数器部分。

在这段代码片段中,我们的抽象类BaseJob实现了Serializable接口,我们从BaseJob扩展,但我们不需要序列化远程和本地数据源;仅序列化organizationName和isSynced字段。

public abstract class BaseJob implements Serializable{
   public void ShouldRetryRun(){}
}

public class SyncOrganizationJob extends BaseJob {

   public String organizationName;
   public Boolean isSynced

   @Inject transient RemoteDataSource remoteDataSource;
   @Inject transient LocalDaoSource localDataSource;

   public SyncOrganizationJob(String organizationName) {
     super(new 
         Params(BACKGROUND).groupBy(GROUP).requireNetwork().persist());

      this.organizationName = organizationName;
      this.isSynced=isSynced;

   }
}

答案 12 :(得分:0)

瞬态关键字的简化示例代码。

import java.io.*;

class NameStore implements Serializable {
    private String firstName, lastName;
    private transient String fullName;

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

    private void buildFullName() {
        // assume building fullName is compuational/memory intensive!
        this.fullName = this.firstName + " " + this.lastName;
    }

    public String toString(){
        return "First Name : " + this.firstName
            + "\nLast Name : " + this.lastName
            + "\nFull Name : " + this.fullName;
    }

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

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

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("ns"));
        NameStore ns = (NameStore)in.readObject();
        System.out.println(ns);
    }
}

答案 13 :(得分:0)

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

答案 14 :(得分:0)

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

  1. 序列化和反序列化是对称过程,如果不能,您不能期望确定结果,在大多数情况下,不确定的值是没有意义的;
  2. 串行化和反序列化是幂等的,这意味着您可以根据需要进行多次序列化,结果是相同的。

因此,如果对象可以存在于内存中但不能存在于磁盘上,则该对象不能被序列化,因为在反序列化时计算机无法恢复内存映射。 例如,您无法序列化Stream对象。

您无法序列化Connection对象,因为它的状态也取决于远程站点。