为什么Java有瞬态字段?

为什么Java有瞬态字段?

Java中的transient关键字用于表示一个字段不应被序列化。

从Java语言规范,Java SE 7版 , 第8.3.1.3节。 transient领域 :

variables可能被标记为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被序列化,而不是同时保留原始图像和缩略图图像。 这意味着需要更less的存储来保存序列化的对象。 (当然,根据系统的要求,这可能是也可能不是理想的,这只是一个例子。)

在反序列化时,调用readObject方法来执行任何必要的操作,以将对象的状态恢复到发生序列化的状态。 在这里,缩略图需要被生成,所以readObject方法被覆盖,所以通过调用generateThumbnail方法来生成缩略图。

有关其他信息,请查阅Java序列化API文章(最初在Sun开发人员networking上提供) 的秘密,其中有一节讨论了使用transient关键字来防止某些字段序列化的场景。

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

什么是序列化?

序列化是使对象的状态持久化的过程。 这意味着对象的状态被转换成一个字节stream并存储在一个文件中。 以同样的方式,我们可以使用反序列化从字节中取回对象的状态。 这是Java编程中的重要概念之一,因为序列化主要用于networking编程。 需要通过networking传输的对象必须转换为字节。 为此,每个类或接口都必须实现Serializable接口。 这是一个没有任何方法的标记界面。

现在什么是transient关键字及其目的?

默认情况下,所有对象的variables都被转换为持久状态。 在某些情况下,您可能希望避免保留一些variables,因为您不需要保留这些variables。 所以你可以声明这些variables是transient 。 如果该variables被声明为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 ,所以它不会被存储在永久存储器中。

资源

允许你定义你不想串行化的variables。

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

我的小小贡献:

什么是Java中的瞬态variables?
在简单的句子中,用transient关键字修改的variables在java中变成瞬态variables。

为什么我们需要在Java中的瞬态variables?
Transient关键字为您提供了对序列化过程的一些控制,并使您能够灵活地从序列化过程中排除一些对象属性。 有一段时间,不要序列化某个对象的某些属性是有意义的,我们将看到哪些variables不应该被序列化,并且应该在下一节中使其变成暂态的。

你应该标记瞬态的variables?
既然我们知道瞬变关键字的目的或者有瞬态variables,那么考虑哪个variables应该被标记为瞬态的是有意义的。 我的规则是,任何variables的值可以从其他variables计算,不需要保存。 例如,如果您有一个名为“interest”的字段,其值可以来自其他字段,例如原则,比率,时间等,则无需将其序列化。
另一个例子是字数统计,如果你保存文章,那么不需要保存字数,因为它可以在文章被反序列化时创build。 另外一个很好的例子是“Logger”,因为大多数情况下你有loginJava的日志logging实例,但是你肯定不希望序列化正确。

瞬态variables是一个不可序列化的variables。

当想到这可能是有用的一个例子是在特定对象实例的上下文中唯一有意义的variables,并且一旦对对象进行了序列化和反序列化,variables就变为无效。 在这种情况下,将这些variables变为空值是非常有用的,这样可以在需要时使用有用的数据重新初始化它们。

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

因为并不是所有的variables都是可序列化的

除本地java以外的序列化系统也可以使用这个修饰符。 例如,Hibernate将不会保留用@Transient或者transient修饰符标记的字段。 兵马俑也尊重这个修饰符。

我相信修饰符的象征意义是“这个字段只用于内存中的使用,不要以任何方式坚持或移动到这个特定的虚拟机之外,它是不可移植的”。 即你不能依靠其在另一个虚拟机内存空间的价值。 就像volatile一样,你不能依赖某些内存和线程的语义。

序列化是将对象状态保存为持久格式(如文件stream或数据库)的过程,稍后将其从stream中还原(反序列化)。 在Java中,如果类实现java.io.Serializable接口,则类的对象是可序列化的。 这是一个标记接口,它告诉JVM该类有资格进行序列化。

 public class User implements Serializable { private static final long serialVersionUID = 1234L; private String username; private String email; private transient String password; private Date birthday; private int age; public User(String username, String email, String password, Date birthday, int age) { this.username = username; this.email = email; this.password = password; this.birthday = birthday; this.age = age; } public void printInfo() { System.out.println("username: " + username); System.out.println("email: " + email); System.out.println("password: " + password); System.out.println("birthday: " + birthday); System.out.println("age: " + age); } // getters and setters } 

这个模型类有三个重点:它必须实现Serializable接口。 否则,当试图序列化类的一个对象时,我们会得到java.io.NotSerializableException。 一个名为serialVersionUID的常量被声明并赋值为一个long值:

 private static final long serialVersionUID = 1234L; 

这是一个常规常量,应该在类实现Serializable接口时声明。 串行版本UID强有力地确保了一个类的序列化和反序列化版本对象之间的兼容性,因为序列化和反序列化的过程可以发生在不同的计算机和系统上。 虽然这个声明是可选的,但总是build议为可序列化类声明serialVersionUID。

注意密码字段被标记为瞬态的:

 private transient String password; 

因为我们不想在序列化对象时存储密码。 规则是,当一个variables被标记为瞬态时,它的对象在序列化过程中不会被序列化。

瞬态variables是一个不可序列化的variables。 您可以使用transient关键字向Java虚拟机指示指示的variables不是对象持久状态的一部分。

Java支持的访问修饰符是静态的,最终的,抽象的,同步的,本地的,易失性的,瞬态的和strictfp。

下表列出了可以应用于variables,方法和类的访问说明符和修饰符Java的列表。

 SPECIFIER/MODIFIER LOCAL VARIABLE INSTANCEVARIABLE METHOD CLASS public NA AAA protected NA AA NA default AAAA private NA AA NA final AAAA static NA AA NA synchronized NA NA A NA native NA NA A NA volatile NA A NA NA transient NA A NA NA strictfp NA NA AA 

在回答这个问题之前,我必须向你解释SERIALIZATION ,因为如果你理解科学计算机中的序列化,你可以很容易地理解这个关键字。

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

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

现在,如果你不想要这个对象的TRANSFERT / SAVED字段,你可以使用关键字 transient

 private transient attr2; 

当你不想分享一些与序列化相关的敏感数据时,就需要它了。

按照谷歌短暂的意思==只持续很短的时间; 暂时的。

现在如果想在java中使用瞬态关键字做任何事情。

问:在哪里使用瞬态?

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

transient int result = 10;

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

简单地说,将暂时的java关键字保护从已被序列化的字段作为它们的非瞬态字段计数器部分。

在这个代码片段中,我们的抽象类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; } }