为什么Java有瞬态字段?
允许您定义不想序列化的变量。
在对象中,您可能有不想序列化/持久化的信息(可能是对父工厂对象的引用),或者序列化没有意义。将这些标记为“瞬态”意味着序列化机制将忽略这些字段。
因为不是所有的变量都是可序列化的
transient变量是在序列化类时不包括的变量。
transient
想到的一个有用的例子是,只有在特定对象实例的上下文中才有意义的变量,并且在序列化和反序列化对象后变得无效。在这种情况下,将这些变量变为null是有用的,这样您就可以在需要时使用有用的数据重新初始化它们。
null
当您不想共享一些与序列化相关的敏感数据时,需要使用它。
Java中的transient关键字用于指示字段不应成为序列化过程的一部分(这意味着保存,如文件)。
从Java语言规范,JavaSE 7版,部分8.3.1.3#0字段:
变量可以标记为transient到表明他们不属于对象的持久状态。
例如,您可能有派生自其他字段的字段,并且应该仅以编程方式完成,而不是通过序列化持久化状态。
这是一个GalleryImage类,它包含一个图像和一个从图像派生的缩略图:
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
generateThumbnail
thumbnailImage字段被标记为transient,因此只有原始的image被序列化,而不是同时保留原始图像和缩略图图像。这意味着保存序列化对象需要更少的存储空间。(当然,这可能是可取的,也可能不是可取的,取决于系统的要求--这只是一个例子。)
image
在反序列化时,调用#0方法执行任何必要的操作,将对象的状态恢复到发生序列化的状态。这里需要生成缩略图,因此readObject方法被覆盖,以便通过调用generateThumbnail方法生成缩略图。
readObject
有关其他信息,文章发现Java序列化API的秘密(最初可在Sun Developer Network上获得)有一节讨论了transient关键字的使用并介绍了使用transient关键字防止某些字段序列化的场景。
除了原生java之外的序列化系统也可以使用此修饰符。例如,Hibernate不会保留标有@刘晓明或瞬态修饰符的字段。Terracotta也尊重这个修饰符。
我相信修饰符的比喻意义是“此字段仅供内存使用。不要以任何方式将其保留或移动到此特定VM之外。它的不可移植”。即,您不能依赖其在另一个VM内存空间中的值。就像易失性意味着您不能依赖某些内存和线程语义学。
transient用于指示类字段不需要序列化。也许最好的例子是Thread字段。通常没有理由序列化Thread,因为它的状态非常“流特定”。
Thread
在理解transient关键字之前,必须了解序列化的概念。如果读者了解序列化,请跳过第一点。
序列化是使对象的状态持久化的过程。这意味着对象的状态被转换成字节流,用于持久化(例如将字节存储在文件中)或传输(例如通过网络发送字节)。同样,我们可以使用反序列化从字节中恢复对象的状态。这是Java编程中的重要概念之一,因为序列化主要用于网络编程。需要通过网络传输的对象必须转换为字节。为此,每个类或接口都必须实现#0接口。它是一个没有任何方法的标记接口。
默认情况下,所有对象的变量都会转换为持久状态。在某些情况下,你可能希望避免持久化某些变量,因为你不需要持久化这些变量。所以你可以将这些变量声明为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 objecto.writeObject(nameStore);o.close(); // reading from objectObjectInputStream in = new ObjectInputStream(new FileInputStream("nameStore"));NameStore nameStore1 = (NameStore)in.readObject();System.out.println(nameStore1);}}
输出将如下:
First Name : SteveMiddle Name : nullLast Name : Jobs
中间名被声明为transient,因此它不会存储在持久存储中。
为什么Java需要瞬态场?
transient关键字使您可以控制序列化过程,并允许您从该过程中排除某些对象属性。序列化过程用于持久化Java对象,主要是为了在它们传输或不活动时保留它们的状态。有时,不序列化对象的某些属性是有意义的。
哪些字段应该标记为瞬态?
现在我们知道了transient关键字和瞬态字段的用途,知道哪些字段要标记瞬态是很重要的。静态字段也不是序列化的,所以相应的关键字也可以做到这一点。但这可能会破坏你的类设计;这就是transient关键字来拯救的地方。我尽量不允许序列化那些值可以从其他字段派生的字段,所以我把它们标记为瞬态。如果你有一个名为interest的字段,它的值可以从其他字段(principal、rate和time)计算,就没有必要序列化它。
interest
principal
rate
time
另一个很好的例子是文章字数。如果你要保存整篇文章,真的没有必要保存字数,因为它可以在文章“反序列化”时计算。或者想想记录器;Logger实例几乎不需要序列化,所以它们可以是瞬态的。
Logger
谷歌短暂的意义==只持续很短的时间;无常的。
现在,如果您想在java中使任何东西变得瞬态,请使用瞬态关键字。
问:在哪里使用瞬态?
答:通常在java中,我们可以通过在变量中获取数据并将这些变量写入文件来将数据保存到文件中,这个过程称为序列化。现在,如果我们想避免将变量数据写入文件,我们会将该变量设置为瞬态。
transient int result=10;
备注:瞬态变量不能是本地的。
在回答这个问题之前,我需要解释一下序列化,因为如果你理解了科学计算机中序列化的含义,你就可以很容易地理解这个关键字。
当一个对象通过网络传输/保存在物理介质(文件,…)上时,必须对该对象进行“序列化”。序列化转换字节状态对象系列。这些字节在网络上发送/保存,并从这些字节重新创建对象。
示例:
public class Foo implements Serializable{private String attr1;private String attr2;...}
现在,如果这个类中有一个字段您不想传输或保存,您可以使用transient关键字
private transient attr2;
这可以防止在序列化类时包含字段表单。
简单地说,瞬态java关键字保护字段免受被序列化的影响,因为它们的非瞬态字段计数器部分。
在这段代码片段中,我们的抽象类BaseJobs实现了Serializable接口,我们从BaseJobs扩展而来,但我们不需要序列化远程和本地数据源;仅序列化组织名称和isSyned字段。
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(newParams(BACKGROUND).groupBy(GROUP).requireNetwork().persist()); this.organizationName = organizationName;this.isSynced=isSynced; }}
瞬态关键字的简化示例代码。
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);}}
使用瞬态修饰符声明的字段不会参与序列化进程。当一个对象被序列化(保存在任何状态)时,它的瞬态字段的值在序列表示中被忽略,而瞬态字段以外的字段将参与序列化过程。这就是瞬态关键字的主要目的。
因为并非所有变量都具有可序列化的性质。
因此,如果Object可以存在于内存中但不能存在于磁盘上,那么Object就不能序列化,因为反序列化时机器无法恢复内存映射。例如,您不能序列化Stream对象。
Stream
您不能序列化Connection对象,因为它的状态也依赖于远程机器。
Connection