The transient
keyword may be very unfamiliar and has not been used much, but the transient
keyword plays an important role in java. Missing status!
In the process of learning javatransient
The reason why the keyword is rare is actually inseparable from its function: The main function of the transient
keyword is to make some Member attribute variables modified by the transient keyword are not serialized. In fact, it is precisely for this reason that serialization operations are rarely used in the learning process, usually in actual development! As for serialization, I believe that many novices have been confused or have no specific concept. This article will introduce it below.
1. What is serialization?
Speaking of serialization, another concept that comes with it is deserialization. Don’t panic, little white children’s shoes. Remembering serialization is equivalent to remembering deserialization. , because deserialization is the reverse of serialization, so the blogger recommends just remembering the concept of serialization to avoid confusing yourself.
Serialization defined by professional terms:
Java provides a mechanism for object serialization. An object can be represented by a byte sequence that contains information such as the object's data, the object's type, and the attributes stored in the object. After the byte sequence is written to the file, it is equivalent to persisting the information of an object in the file. Conversely, the byte sequence can be read back from the file, the object reconstructed, and deserialized. The object's data, the object's type, and the data information stored in the object can all be used to create objects in memory.
Yichun’s term definition Serialization:
Serialization: Byte——> Object
Actually, what I summarized is the above conclusion. If you don’t understand, just refer to the definition of professional terms. Once you understand, just remember my words. If you can’t remember, please beat me to death (I’m just kicking my m He is a genius)
Diagram understanding serialization:
What? You don’t understand what a byte is? In fact, I have already introduced serialization in an article about IO flow. Don’t worry, it’s absolutely very detailed. Just look at the name of the article and you’ll know it.
The most sassy, comprehensive and detailed IO flow tutorial in history. Even novices can understand it!
2. Why serialization?
The concept of serialization was mentioned in the previous section. After knowing the concept, we must know why we need to serialize.
Before talking about the reasons why serialization is necessary, let me give you a chestnut as a blogger:
Just like when you go to buy food on the street, the general operation is to package it in plastic bags. When I get home and want to cook, I take out the dishes. And this series of operations is like serialization and deserialization!
Serialization of objects in Java refers to converting objects into representations in the form of byte sequences. These byte sequences contain the data and information of the object, a serialized object can be written to a database or file , and can also be used for network transmission , generally when we use cache cache (Insufficient memory space may result in local storage to the hard disk) or When calling rpc remotely (network transmission), we often need to implement our entity classesSerializable
Interface, the purpose is to make it serializable.
●In the development processTo use the transientkeyword modified chestnut:
If a user has some password and other information, For security reasons, if you do not want to be transmitted during network operations, the variables corresponding to this information can be added with the transient keyword. In other words, the life cycle of this field only exists in the caller's memory and will not be written to disk for persistence.
●In the development processNo need for transientKeyword modified chestnuts:
1. Field values in the class can be deduced based on other fields come out.
2. Depending on the specific business requirements, which fields do not want to be serialized;
I wonder if you have ever thought about why they should not be serialized? In fact, it is mainly to save storage space. Optimize the program!
PS: I remember when I looked at the
HashMap
source code before, I found that a field was modified withtransient
. I think it makes sense. There is really no need to modify this The modCount field is serialized because it is meaningless. modCount is mainly used to determine whether the HashMap has been modified (like during put and remove operations,modCount
will increase by itself). For this kind of variable, it can be any value at the beginning. The value, of course, is 0 (it will be 0 when it is new, deserialized, or cloned), and there is no need to persist its value.
Of course, the ultimate goal after serialization is to deserialize and restore it to the original Java object. Otherwise, what else would you do after serialization? Just like buying groceries, wrap the last part in a plastic bag. It is still for the convenience and safety of getting home before removing the plastic bag, so the serialized byte sequence can be restored to a Java object. This process is deserialization.
3. The use of serialization and transient
1. The class of the object that needs to be serialized must Implement serialization interface: Java.lang.Serializable interface (a flag interface without any abstract methods). Most classes in Java implement this interface, such as: String
,Integer
classes, etc., classes that do not implement this interface will not serialize or deserialize any state, and will throw NotSerializableException
exception.
2. The bottom layer will judge that if the current object is an instance of Serializable
, serialization is allowed, and the Java object instanceof Serializable
will be used to judge.
3, Use object streamObjectOutputStream
in Java to complete serialization and ObjectInputStream
stream deserialization
==ObjectOutputStream: Serialization operation through writeObject() method==
==ObjectInputStream: Deserialization operation through readObject() method==
4. All properties of this class must be serializable. If there is an attribute that does not need to be serializable, the attribute must be marked as transient and modified with the transient
keyword.
Because of the bytes, the operation of the stream must be involved, that is, the object stream is also called the serialization stream ObjectOutputstream. Let’s analyze the serialization operation code in various situations!
Here, I really strongly recommend readers who read the Yichun blog, please try to knock it, remember to take it with you at a glance or copy it and run it, especially for little white children's shoes, believe me ! You will definitely gain something different. Don’t feel like it’s a waste of time, sometimes slow is faster, Yichun experienced it firsthand!
3.1. The Serializable interface is not implemented for serialization
package TransientTest; import java.io.*; class UserInfo { //================================注意这里没有实现Serializable接口 private String name; private transient String password; public UserInfo(String name,String psw) { this.name = name; this.password=psw; } @Override public String toString() { return "UserInfo{" + "name='" + name + '\'' + ", password='" + password + '\'' + '}'; } } public class TransientDemo { public static void main(String[] args) { UserInfo userInfo=new UserInfo("老王","123"); System.out.println("序列化之前信息:"+userInfo); try { ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); output.writeObject(new UserInfo("老王","123")); output.close(); } catch (IOException e) { e.printStackTrace(); } } }
Running result
3.2. Serialization of the Serializable interface
When we add the Serializable interface and run it again, we will find that the content of the userinfo.txt
file that appears in the project is as follows:
In fact, this is not the point. The point is that the serialization operation was successful!
3.3. Ordinary serialization situation
package TransientTest; import java.io.*; class UserInfo implements Serializable{ //第一步实现Serializable接口 private String name; private String password;//都是普通属性============================== public UserInfo(String name,String psw) { this.name = name; this.password=psw; } @Override public String toString() { return "UserInfo{" + "name='" + name + '\'' + ", password='" + password + '\'' + '}'; } } public class TransientDemo { public static void main(String[] args) throws ClassNotFoundException { UserInfo userInfo=new UserInfo("程序员老王","123"); System.out.println("序列化之前信息:"+userInfo); try { ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作 output.writeObject(new UserInfo("程序员老王","123")); output.close(); } catch (IOException e) { e.printStackTrace(); } try { ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作 Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException System.out.println("序列化之后信息:"+o); } catch (IOException e) { e.printStackTrace(); } } }
Running results:
序列化之前信息:UserInfo{name='程序员老王', password='123'} 序列化之后信息:UserInfo{name='程序员老王', password='123'}
3.4. Transient serialization situation
package TransientTest; import java.io.*; class UserInfo implements Serializable{ //第一步实现Serializable接口 private String name; private transient String password; //特别注意:属性由transient关键字修饰=========== public UserInfo(String name,String psw) { this.name = name; this.password=psw; } @Override public String toString() { return "UserInfo{" + "name='" + name + '\'' + ", password='" + password + '\'' + '}'; } } public class TransientDemo { public static void main(String[] args) throws ClassNotFoundException { UserInfo userInfo=new UserInfo("程序员老王","123"); System.out.println("序列化之前信息:"+userInfo); try { ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作 output.writeObject(new UserInfo("程序员老王","123")); output.close(); } catch (IOException e) { e.printStackTrace(); } try { ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作 Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException System.out.println("序列化之后信息:"+o); } catch (IOException e) { e.printStackTrace(); } } }
Running result:
序列化之前信息:UserInfo{name='程序员老王', password='123'} 序列化之后信息:UserInfo{name='程序员老王', password='null'}
Pay special attention to the result, add the transient modified attribute value to the default valuenull
! If the attribute modified by transient is of type int, then its value must be 0 after being serialized. Of course, you can try it. What does this mean? It means that attributes marked as transient
will not be saved when the object is serialized (or the variable will not be persisted)
3.5, static serialization situation
package TransientTest; import java.io.*; class UserInfo implements Serializable{ //第一步实现Serializable接口 private String name; private static String password; //特别注意:属性由static关键字修饰============== public UserInfo(String name, String psw) { this.name = name; this.password=psw; } @Override public String toString() { return "UserInfo{" + "name='" + name + '\'' + ", password='" + password + '\'' + '}'; } } public class TransientDemo { public static void main(String[] args) throws ClassNotFoundException { UserInfo userInfo=new UserInfo("程序员老王","123"); System.out.println("序列化之前信息:"+userInfo); try { ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作 output.writeObject(new UserInfo("程序员老王","123")); output.close(); } catch (IOException e) { e.printStackTrace(); } try { ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作 Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException System.out.println("序列化之后信息:"+o); } catch (IOException e) { e.printStackTrace(); } } }
Run result:
序列化之前信息:UserInfo{name='程序员老王', password='123'} 序列化之后信息:UserInfo{name='程序员老王', password='123'}
At this time, you will mistakenly think that the static modification has also been serialized. In fact, it is not the case. In fact, it is easy to get confused here! Obviously taking out null
(default value) can indicate that it will not be serialized. It is clearly not changed to the default value here, why do you still say that static
will not be serialized?
In fact, the value of the static variable name in the class after deserialization is actually the value of the corresponding static variable in the current JVM. This value is in the JVM and is not deserialized. derived. That is to say, variables modified by static do not participate in serialization! But we can’t say it without proof, yes, then let’s compare the two programs and we will understand!
The first program: This is a name attribute program that has not been modified by static:
package Thread; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; class UserInfo implements Serializable { private String name; private transient String psw; public UserInfo(String name, String psw) { this.name = name; this.psw = psw; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPsw() { return psw; } public void setPsw(String psw) { this.psw = psw; } public String toString() { return "name=" + name + ", psw=" + psw; } } public class TestTransient { public static void main(String[] args) { UserInfo userInfo = new UserInfo("程序员老过", "456"); System.out.println(userInfo); try { // 序列化,被设置为transient的属性没有被序列化 ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt")); o.writeObject(userInfo); o.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } try { //在反序列化之前改变name的值 =================================注意这里的代码 userInfo.setName("程序员老改"); // 重新读取内容 ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt")); UserInfo readUserInfo = (UserInfo) in.readObject(); //读取后psw的内容为null System.out.println(readUserInfo.toString()); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }
Running results:
name=程序员老过, psw=456 name=程序员老过, psw=null
It can be seen from the program running results that in Before deserialization, I tried to change the value of name to the programmer, but it was unsuccessful!
第二个程序:这是一个被static修饰的name属性程序:
package Thread; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; class UserInfo implements Serializable { private static final long serialVersionUID = 996890129747019948L; private static String name; private transient String psw; public UserInfo(String name, String psw) { this.name = name; this.psw = psw; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPsw() { return psw; } public void setPsw(String psw) { this.psw = psw; } public String toString() { return "name=" + name + ", psw=" + psw; } } public class TestTransient { public static void main(String[] args) { UserInfo userInfo = new UserInfo("程序员老过", "456"); System.out.println(userInfo); try { // 序列化,被设置为transient的属性没有被序列化 ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt")); o.writeObject(userInfo); o.close(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } try { //在反序列化之前改变name的值 userInfo.setName("程序员老改"); // 重新读取内容 ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt")); UserInfo readUserInfo = (UserInfo) in.readObject(); //读取后psw的内容为null System.out.println(readUserInfo.toString()); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }
运行结果:
name=程序员老过, psw=456 name=程序员老改, psw=null
从程序运行结果中可以看出,在反序列化之前试着改变name的值为程序员老改,结果是成功的!现在对比一下两个程序是不是就很清晰了?
static关键字修饰的成员属性优于非静态成员属性加载到内存中,同时静态也优于对象进入到内存中,被static修饰的成员变量不能被序列化,序列化的都是对象,静态变量不是对象状态的一部分,因此它不参与序列化。所以将静态变量声明为transient变量是没有用处的。因此,反序列化后类中static型变量name的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。
如果对static关键字还是不太清楚理解的童鞋可以参考这篇文章,应该算是不错的:深入理解static关键字
3.6、final序列化情况
对于final关键字来讲,final变量将直接通过值参与序列化,至于代码程序我就不再贴出来了,大家可以试着用final修饰验证一下!
主要注意的是final 和transient可以同时修饰同一个变量,结果也是一样的,对transient没有影响,这里主要提一下,希望各位以后在开发中遇到这些情况不会满头雾水!
4、java类中serialVersionUID作用
既然提到了transient关键字就不得不提到序列化,既然提到了序列化,就不得不提到serialVersionUID
了,它是啥呢?基本上有序列化就会存在这个serialVersionUID。
serialVersionUID
适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID
来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID
与本地相应实体类的serialVersionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException
,在开发中有时候可写可不写,建议最好还是写上比较好。
5、transient关键字小结
1、变量被transient修饰,变量将不会被序列化
2、transient关键字只能修饰变量,而不能修饰方法和类。
3、被static关键字修饰的变量不参与序列化,一个静态static变量不管是否被transient修饰,均不能被序列化。
4、final变量值参与序列化,final transient同时修饰变量,final不会影响transient,一样不会参与序列化
第二点需要注意的是:本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口
第三点需要注意的是:反序列化后类中static型变量的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。
结语:被transient关键字修饰导致不被序列化,其优点是可以节省存储空间。优化程序!随之而来的是会导致被transient修饰的字段会重新计算,初始化!
本文来自 java入门 栏目,欢迎学习!
The above is the detailed content of Learn more about the transient keyword in Java. For more information, please follow other related articles on the PHP Chinese website!