Java之transient关键字(java的transient关键字)
2022-03-01
transient关键字介绍;
transient关键字在阅读JDK源码中经常出现,其中Java中transient关键字的作用就是让某些被transient关键字修饰的成员变量不被序列化。
序列化是什么?
专业术语定义的序列化:
Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
简易来说术语定义的序列化即为:
序列化:对象(数据,类型,属性等信息) ——> 字节序列;
反序列化:字节序列 ——> 对象(数据,类型,属性等信息);
图解序列化解释:
为何Java需要序列化?
Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象 可以被写到数据库或文件中,也可用于 网络传输,一般当我们使用 缓存cache(内存空间不够有可能会本地存储到硬盘或网络传输的时候,经常需要让我们的实体类实现Serializable
接口,目的就是为了让其可序列化;
何时需要使用transient关键词修饰:
当开发过程中包含一些私密信息或者私密性文件,为了保证数据的安全性,不希望信息在网络操作中被传输,对其加上transient关键字。即该字段的数据的生命周期仅仅在内存,而不被刷入磁盘中。
何时不需要使用transient关键字:
1.类中的字段值可以被其他字段推出;
2,内存空间不足时,减少使用transient关键字,节省存储空间;
3.依据项目需求,可具体选择一些字段不被transient关键字修饰;
序列化和transient的使用:
1、需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable 接口(一个标志接口,没有任何抽象方法),Java 中大多数类都实现了该接口,比如:String
,Integer
类等,不实现此接口的类将不会使任何状态序列化或反序列化,会抛NotSerializableException
异常 。
未实现Serializable接口进行序列化异常:
package TransientTest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
class UserInfo { //没有实现Serializable接口
private String name;
private transient String password;
//函数构造
public UserInfo(String name,String psw) {
this.name = name;
this.password=psw;
}
//继承object类重写toString方法
@Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class TransientDemo {
public static void main(String[] args) {
UserInfo userInfo=new UserInfo("XXX","123456789");
System.out.println("序列化之前信息:"+userInfo);
try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt"));
output.writeObject(new UserInfo("XXX","123456789"));
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行后即出现无法序列的异常;
package TransientTest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class UserInfo implements Serializable {
/**
* 实现Serializable接口
*/
private static final long serialVersionUID = 1L;
private String name;
private transient String password;
//函数构造
public UserInfo(String name,String psw) {
this.name = name;
this.password=psw;
}
//继承object类重写toString方法
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class TransientDemo {
public static void main(String[] args) {
UserInfo userInfo=new UserInfo("XXX","123456789");
System.out.println("序列化之前信息:"+userInfo);
try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt"));
output.writeObject(new UserInfo("XXX","123456789"));
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2、底层会判断,如果当前对象是 Serializable
的实例,才允许做序列化,Java对象 instanceof Serializable
来判断。
3、在 Java 中使用对象流ObjectOutputStream
来完成序列化以及ObjectInputStream
流反序列化。
4、该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient
关键字修饰。
加入transient关键字后的序列化:
package TransientTest;
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 UserInfo implements Serializable {
/**
* 加入transient关键字
*/
private static final long serialVersionUID = 1L;
private String name;
private transient String password;
//函数构造
public UserInfo(String name,String psw) {
this.name = name;
this.password=psw;
}
//继承object类重写toString方法
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class TransientDemo {
public static void main(String[] args) {
UserInfo userInfo=new UserInfo("XX","123");
System.out.println("序列化之前信息:"+userInfo);
try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
output.writeObject(new UserInfo("XX","123"));
output.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
@SuppressWarnings("resource")
ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException
System.out.println("序列化之后信息:"+o);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
普通序列化:
package TransientTest;
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 UserInfo implements Serializable {
/**
* 未加入transient关键字
*/
private static final long serialVersionUID = 1L;
private String name;
private String password;
//函数构造
public UserInfo(String name,String psw) {
this.name = name;
this.password=psw;
}
//继承object类重写toString方法
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
public class TransientDemo {
public static void main(String[] args) {
UserInfo userInfo=new UserInfo("XX","123");
System.out.println("序列化之前信息:"+ userInfo);
try {
ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
output.writeObject(new UserInfo("XX","123"));
output.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
@SuppressWarnings("resource")
ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
Object o = input.readObject();
System.out.println("序列化之后信息:"+ o );
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
transient
的属性在对象被序列化的时候不会被保存(变量不会持久化);
java类中serialVersionUID:
既然提到了transient关键字就不得不提到序列化,既然提到了序列化,就不得不提到serialVersionUID
了,基本上有序列化就会存在这个serialVersionUID。
serialVersionUID
适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID
来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID
与本地相应实体类的serialVersionUID
进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。
transient关键字小结
1、变量被transient修饰,变量将不会被序列化
2、transient关键字只能修饰变量,而不能修饰方法和类。
3、被static关键字修饰的变量不参与序列化,一个静态static变量不管是否被transient修饰,均不能被序列化。
4、final变量值参与序列化,final transient同时修饰变量,final不会影响transient,一样不会参与序列化
5.本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口
6.反序列化后类中static型变量的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。
7.被transient关键字修饰导致不被序列化,其优点是可以节省存储空间。优化程序!随之而来的是会导致被transient修饰的字段会重新计算,初始化!