为什么Java有transient字段?
transient
关键字在Java中用于表示一个字段不应该参与序列化(比如保存到文件)的过程。
根据Java SE 7版语言规范的第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
方法来生成缩略图像。transient
关键字来防止序列化某些字段的情况。@DoNotSerialize
? - Elazar Leibovich在理解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
Middle Name 被声明为
transient
,因此它不会被存储在持久性存储中。
为什么在Java中需要使用瞬态字段?
transient
关键字可以控制序列化过程,允许你从这个过程中排除一些对象属性。序列化过程用于持久化Java对象,主要是为了在它们传输或不活动时保留它们的状态。有时,不对对象的某些属性进行序列化是有意义的。
哪些字段应该标记为瞬态字段?
既然我们知道了transient
关键字和瞬态字段的目的,就很重要知道应该标记哪些字段为瞬态字段。静态字段也不会被序列化,所以相应的关键字也可以起到作用。但是这可能会破坏你的类设计;这就是transient
关键字发挥作用的地方。我尽量不允许那些其值可以从其他字段派生的字段被序列化,因此我将它们标记为瞬态字段。如果你有一个叫做interest
的字段,它的值可以根据其他字段(principal
, rate
和time
)计算出来,那就没有必要对它进行序列化。
另一个很好的例子是文章单词计数。如果你正在保存一篇完整的文章,实际上没有必要保存它的单词数量,因为在反序列化文章时可以计算出来。或者考虑日志记录器;Logger
实例几乎不需要被序列化,所以它们可以被标记为瞬态字段。
transient
变量是一种在类被序列化时不会包含的变量。
其中一个例子是针对那些仅在特定对象实例上下文中才有意义且在对象序列化和反序列化后失效的变量。在这种情况下,将这些变量变为null
是有用的,这样当需要时可以重新初始化它们,并提供有用的数据。
transient
不会成为一个关键字,而会使用注解。 - Joachim Sauertransient
用于表示一个类的字段不需要被序列化。
可能最好的例子是Thread
字段。通常情况下,没有理由序列化一个Thread
,因为它的状态非常“流特定”。
public class Foo implements Serializable
{
private String attr1;
private String attr2;
...
}
现在,如果这个类中有一个字段你不想传输或保存,你可以使用transient
关键字。
private transient attr2;
由于并非所有变量都具有可序列化的性质
transient int result=10;
注意:瞬态变量不能是局部变量。