Java 关键字 transient
序列化与反序列化
学习 transient
关键字之前呢,我们需要先了解一下 序列化
与 反序列化
。
- Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
举个栗子
你去街上买菜,一般操作都是用塑料袋给包装起来,直到回家要做菜的时候就把菜给拿出来。而这一系列操作就像极了序列化和反序列化!
transient 关键字
作用
让某些被 transient 关键字修饰的成员属性变量不被序列化
使用场景
如果用户有一些密码、薪资等信息,为了安全起见,不希望在网络操作中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
序列化与transient的使用
- 需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable 接口(一个标志接口,没有任何抽象方法),Java 中大多数类都实现了该接口,比如:String,Integer类等。不实现该接口的类进行序列化或反序列化会抛
NotSerializableException
异常 - 对象流
ObjectOutputStream->writeObject()方法做序列化操作
ObjectInputStream->readObject()方法做反序列化操作 - 被 transient 修饰的成员变量,在序列化的时候其值会被忽略,在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null
- transient 关键字只能修饰变量,而不能修饰方法和类
final
只是保证变量不能被修改,不影响 transient 对变量的不序列化作用static
修饰的变量不参与序列化,static 的作用域是类而非某个具体对象
代码栗子
实践是检验真理的唯一标准,疑惑的时候最好动手敲一下demo 看看,顺便也能加深理解和记忆。
栗子1→加 transient 与 不加 transient
import java.io.*;
class UserInfo implements Serializable {
private transient String name;//给name加了transient修饰
private String psw;
public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
}
@Override
public String toString(){
return "name=" + name + ";psw=" + psw;
}
}
public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("小菜", "666");
System.out.println("序列化前->" + userInfo.toString());
try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("userInfo.txt"));
outputStream.writeObject(userInfo);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("userInfo.txt"));
Object o = inputStream.readObject();
System.out.println("序列化后->" + o);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
执行结果
序列化前->name=小菜;psw=666
序列化后->name=null;psw=666
可见加了 transient 修饰的变量 name 反序列化后是 null,即 name 没有参与序列化。
栗子2→加了 final transient 修饰变量
import java.io.*;
class UserInfo implements Serializable {
private final transient String name;//给name加了final transient修饰
private String psw;
public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
}
@Override
public String toString(){
return "name=" + name + ";psw=" + psw;
}
}
public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("小菜", "666");
System.out.println("序列化前->" + userInfo.toString());
try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("userInfo.txt"));
outputStream.writeObject(userInfo);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("userInfo.txt"));
Object o = inputStream.readObject();
System.out.println("序列化后->" + o);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
执行结果
序列化前->name=小菜;psw=666
序列化后->name=null;psw=666
可见加了final transient 修饰的变量 name 反序列化后依然是 null,即 name 没有参与序列化,final 的作用仅是保证变量不能被修改。
栗子3→加了 static transient 修饰变量
import java.io.*;
class UserInfo implements Serializable {
private static transient String name;//给name加了static transient修饰
private String psw;
public UserInfo(String name, String psw) {
UserInfo.name = name;
this.psw = psw;
}
@Override
public String toString(){
return "name=" + name + ";psw=" + psw;
}
public void setName(String name) {
UserInfo.name = name;
}
}
public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("小菜", "666");
System.out.println("序列化前->" + userInfo.toString());
try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("userInfo.txt"));
outputStream.writeObject(userInfo);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
userInfo.setName("xiaocai");//反序列化之前修改 name 的值
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("userInfo.txt"));
Object o = inputStream.readObject();
System.out.println("序列化后->" + o);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
执行结果
序列化前->name=小菜;psw=666
序列化后->name=xiaocai;psw=666
在反序列化之前修改 name 的值,接着反序列化后,可见 name 是新的值 xiaocai
。即被 static 修饰的变量不参与序列化。这里可能有点疑惑,从文件用 ObjectInputStream
反序列化出来的对象存到新的对象 Object o
, 跟上面用 ObjectOutputStream
序列化进文件的对象不是同一个对象了呀,为什么 name 属性会共享了呢?原来 static
的作用域是 类
而非某个具体对象,name 在 TestTransient 类里边是一个 static 变量,作用域是整个类,值当然是共享的啦。
总结
- 使用 transient 关键字的好处是节省存储空间,优化程序
- 被 transient 修饰的字段会重新计算,初始化。