Java 中的 transient
关键字用于指示字段不应成为序列化(这意味着像文件一样保存)过程的一部分。
从 Java Language Specification, Java SE 7 Edition、Section 8.3.1.3. transient
Fields:
变量可以被标记为瞬态的,以表明它们不是对象持久状态的一部分。
例如,您可能有从其他字段派生的字段,并且只能以编程方式完成,而不是通过序列化来保持状态。
这是一个 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
关键字用于防止某些字段的序列化的用法并提供了一个场景。
在理解 transient
关键字之前,必须先了解序列化的概念。如果读者了解序列化,请跳过第一点。
什么是序列化?
序列化是使对象的状态持久化的过程。这意味着对象的状态被转换为字节流以用于持久化(例如在文件中存储字节)或传输(例如通过网络发送字节)。同样,我们可以使用反序列化从字节中恢复对象的状态。这是 Java 编程中的重要概念之一,因为序列化主要用于网络编程。需要通过网络传输的对象必须转换为字节。为此,每个类或接口都必须实现 Serializable
接口。它是一个没有任何方法的标记接口。
现在transient关键字及其用途是什么?
默认情况下,对象的所有变量都会转换为持久状态。在某些情况下,您可能希望避免保留某些变量,因为您不需要保留这些变量。因此,您可以将这些变量声明为 transient
。如果变量被声明为 transient
,那么它将不会被持久化。这就是 transient
关键字的主要用途。
我想用下面的例子来解释以上两点(借用自this article):
包javabeat.samples;导入 java.io.FileInputStream;导入 java.io.FileOutputStream;导入 java.io.IOException;导入 java.io.ObjectInputStream;导入 java.io.ObjectOutputStream;导入 java.io.Serializable;类 NameStore 实现 Serializable{ private String firstName;私有瞬态字符串中间名;私人字符串姓氏; 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("名字:"); sb.append(this.firstName); sb.append("中间名:"); sb.append(this.middleName); sb.append("姓氏:"); sb.append(this.lastName);返回 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")); // 写入对象 o.writeObject(nameStore); o.close(); // 从对象中读取 ObjectInputStream in = new ObjectInputStream(new FileInputStream("nameStore")); NameStore nameStore1 = (NameStore)in.readObject(); System.out.println(nameStore1); } }
输出如下:
名字 : Steve 中间名 : null 姓氏 : Jobs
中间名 被声明为 transient
,因此它不会存储在持久存储中。
允许您定义不想序列化的变量。
在一个对象中,您可能拥有不想序列化/持久化的信息(可能是对父工厂对象的引用),或者序列化可能没有意义。将这些标记为“瞬态”意味着序列化机制将忽略这些字段。
为什么 Java 中需要瞬态字段?
transient
关键字使您可以对序列化过程进行一些控制,并允许您从该过程中排除一些对象属性。序列化过程用于持久化 Java 对象,主要是为了在传输或不活动时保留它们的状态。有时,不序列化对象的某些属性是有意义的。
您应该将哪些字段标记为瞬态?
既然我们知道了 transient
关键字和瞬态字段的用途,那么了解要标记瞬态的字段就很重要了。静态字段也没有序列化,因此相应的关键字也可以解决问题。但这可能会破坏您的课程设计;这就是 transient
关键字的用武之地。我尽量不允许序列化其值可以从其他字段派生的字段,因此我将它们标记为瞬态的。如果您有一个名为 interest
的字段,其值可以从其他字段(principal
、rate
和 time
)中计算出来,则无需对其进行序列化。
另一个很好的例子是文章字数。如果您要保存整篇文章,则实际上不需要保存字数,因为它可以在文章“反序列化”时计算。或者想想记录器; Logger
实例几乎不需要序列化,因此可以将它们设为瞬态。
transient
transient
是有意义的
transient
成为对感兴趣的类进行序列化的要求。此外,Logger
实例往往是静态的,因此首先不需要是 transient
transient
变量是在类被序列化时不包括在内的变量。
想到什么时候这可能有用的一个例子是,仅在特定对象实例的上下文中才有意义的变量,并且在您序列化和反序列化对象后变得无效。在这种情况下,将这些变量改为 null
很有用,这样您就可以在需要时使用有用的数据重新初始化它们。
本机 java 以外的序列化系统也可以使用此修饰符。例如,Hibernate 不会保留标记有 @Transient 或瞬态修饰符的字段。兵马俑也尊重这个修饰符。
我相信修饰符的比喻意义是“这个字段仅供内存使用。不要以任何方式将其保留或移动到此特定 VM 之外。它是不可移植的”。即你不能依赖它在另一个VM 内存空间中的值。很像 volatile 意味着您不能依赖某些内存和线程语义。
transient
就不会是关键字。他们可能会使用注释。
transient
用于表示类字段不需要序列化。最好的例子可能是 Thread
字段。通常没有理由序列化 Thread
,因为它的状态非常“特定于流”。
A
引用不可序列化类 B
(如您的示例中的 Thread
),则 A
必须将引用标记为 transient
XOR 必须覆盖默认序列化过程才能按顺序用 B
XOR 做一些合理的事情假设只有 B
的可序列化子类被实际引用(因此实际的子类必须注意它们的“坏”父 B
)XOR 接受序列化将失败。仅在一种情况下(标记为瞬态)B
会自动且静默地跳过。
在我回答这个问题之前,我需要解释一下序列化,因为如果你理解它在科学计算机中的序列化是什么意思,你就可以很容易地理解这个关键字。
当对象通过网络传输/保存在物理媒体(文件,...)上时,必须“序列化”对象。序列化转换字节状态对象系列。这些字节在网络上发送/保存,并从这些字节重新创建对象。
例子:
public class Foo implements Serializable
{
private String attr1;
private String attr2;
...
}
现在,如果您不想转移或保存此类中的某个字段,则可以使用 transient
关键字
private transient attr2;
这可以防止在序列化类时包含字段表单。
因为并非所有变量都具有可序列化的性质
当您不想共享一些与序列化相关的敏感数据时,就需要它。
Thread
(例如归功于 @AH),在这种情况下,您会将其标记为瞬态。但是,线程本身并不是敏感数据,序列化它只是没有逻辑意义(而且它是不可序列化的)。
根据谷歌短暂的意思==只持续很短的时间;暂时的。
现在,如果您想在 java 中使用瞬态关键字。
Q:在哪里使用瞬态?
A:一般在java中我们可以通过在变量中获取数据并将这些变量写入文件来将数据保存到文件中,这个过程称为序列化。现在,如果我们想避免将变量数据写入文件,我们将该变量设置为瞬态。
transient int result=10;
注意:瞬态变量不能是本地的。
瞬态关键字的简化示例代码。
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);
}
}
简单地说,transient java 关键字保护字段不被序列化作为它们的非瞬态字段对应部分。
在这段代码片段中,我们的抽象类 BaseJob 实现了 Serializable 接口,我们从 BaseJob 扩展,但我们不需要序列化远程和本地数据源;仅序列化组织名称和 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;
}
}
使用瞬态修饰符声明的字段将不参与序列化过程。当一个对象被序列化(以任何状态保存)时,其瞬态字段的值在序列表示中被忽略,而瞬态字段以外的字段将参与序列化过程。这就是瞬态关键字的主要目的。
因为并非所有变量都具有可序列化的性质。
序列化和反序列化是对称的过程,如果不是你不能期望结果是确定的,在大多数情况下,未确定的值是没有意义的;序列化和反序列化是幂等的,这意味着您可以根据需要进行多次序列化,并且结果是相同的。
所以如果Object可以存在于内存而不存在于磁盘上,那么Object就不能被序列化,因为反序列化时机器无法恢复内存映射。例如,您不能序列化 Stream
对象。
您不能序列化 Connection
对象,因为它的状态也依赖于远程站点。
不定期副业成功案例分享
@DoNotSerialize
?