今天在阅读ArrayList源码时,发现一个以前没有遇到过的关键字transient,于事百度了一下,百度到这篇博客 transient关键字详解,作者讲得很详细,我也对transient关键字有了一定的了解,所以我自己也整理一下,一是加强理解,二是便于自己以后直接查看。
1. transient关键字的用途
首先我们需要知道,当一个对象实现了Serializable接口后,该对象就可以被序列化(一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序)。并且,该对象的所有属性和方法都会被自动序列化。而在开发过程中,遇到不希望序列化某些属性(包含敏感信息的属性)的需求时,这时就需要transient关键字了。所以,transient的用途:
一个对象实例被transient修饰的属性在其序列化的过程中不会被序列化(或者说变量持久化),执行序列化时,JVM会忽略transient变量的原始值并将默认值保存到文件中。并且,当该对象实例被反序列化时:被transient修饰的属性的值不会被恢复,而且使用默认值来表示。
2. 使用方法
在不希望序列化的属性前添加transient关键字即可。
示例:
package com.fuqi.transientlean;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* @Description: User实体类
* @Author 傅琦
* @Date 2019/5/29 21:56
* @Version V1.0
*/
@AllArgsConstructor
@Data
public class User implements Serializable {
private static final long serialVersionUID = 8645066796013228213L;
private String username;
private int age;
private transient String password;
}
这里我在工程中添加了Lombok依赖,并且在IDE中安装了Lombok的插件(具体安装方式可直接百度),所以可以直接通过注解来生成一个实体类的构造方法、getter()、setter()、hashCode()、toString()方法等常见方法。
测试方法:
package com.fuqi.transientlean;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @Description: 测试transient关键字的作用
* @Author 傅琦
* @Date 2019/5/29 22:02
* @Version V1.0
*/
public class TestTransient {
public static void main(String[] args){
// 初始化一个对象
User user = new User("李雷", 20, "lilei123");
System.out.println(user);
// 将对象进行序列化
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
oos.writeObject(user);
oos.close();
}catch (Exception e){
e.printStackTrace();
}
// 反序列化对象
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
User userRead = (User) ois.readObject();
System.out.println(userRead);
}catch (Exception e){
e.printStackTrace();
}
}
}
测试结果截图:
上图中密码字段反序列化出来值为null,说明被transient修饰的属性不会被序列化,保存不到磁盘中,反序列化时也就取不出原来的值,只能使用默认值。
Transient 与 Static
当被修饰的变量同时也被static关键字所修饰时,会是什么效果呢?
这里我新建了一个Male类来测试
package com.fuqi.transientlean;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* @Description: Male实体类
* @Author 傅琦
* @Date 2019/5/29 22:12
* @Version V1.0
*/
@AllArgsConstructor
@Data
public class Male implements Serializable {
private static final long serialVersionUID = 405573135826470637L;
private transient static String name = "李雷";
private int age;
private transient int salary;
public static String getName() {
return name;
}
public static void setName(String name) {
Male.name = name;
}
}
上面的代码中,name属性同时被transient和static关键字所修饰,现在对其进行简单的测试:
package com.fuqi.transientlean;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @Description: 测试transient关键字的作用
* @Author 傅琦
* @Date 2019/5/29 22:02
* @Version V1.0
*/
public class TestTransient {
public static void main1(String[] args){
// 初始化一个对象
User user = new User("李雷", 20, "lilei123");
System.out.println(user);
// 将对象进行序列化
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
oos.writeObject(user);
oos.close();
}catch (Exception e){
e.printStackTrace();
}
// 反序列化对象
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
User userRead = (User) ois.readObject();
System.out.println(userRead);
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
Male male = new Male(15, 25000);
System.out.println(Male.getName() + ": " + male);
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("male.txt"));
oos.writeObject(male);
oos.close();
}catch (Exception e){
e.printStackTrace();
}
// 反序列化对象
try {
// 反序列化之前修改name属性的值
Male.setName("韩梅梅");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("male.txt"));
Male maleRead = (Male) ois.readObject();
System.out.println(Male.getName() + ": " + maleRead);
}catch (Exception e){
e.printStackTrace();
}
}
}
这里将上次的测试方法改一下方法命,再新写一个main方法来测试,测试结果如下图:
从上图中可以看到,name属性并不为null,反而是有值的。原因何在?
这是因为:
static关键字修饰的属性,也叫静态变量,静态变量不是对象状态的一部分,因此它不参与序列化。所以将静态变量声明为transient变量是没有用处的。因此,反序列化后类中static型变量name的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。
Final 与 Transient
对Male代码稍作修改,再直接测试:
package com.fuqi.transientlean;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* @Description: Male实体类
* @Author 傅琦
* @Date 2019/5/29 22:12
* @Version V1.0
*/
@AllArgsConstructor
@Data
public class Male implements Serializable {
private static final long serialVersionUID = 405573135826470637L;
private transient static String name = "李雷";
private int age;
private transient int salary;
// 加一个final关键字修饰的属性
private final transient String description = "男";
public static String getName() {
return name;
}
public static void setName(String name) {
Male.name = name;
}
}
测试方法直接使用前面测试static关键字的方法,其结果为:
上图的结果说明final变量声明为transient变量不会产生任何影响。
3. 小结
- 一旦一个变量被transient修饰,该变量将不再是对象持久化的一部分,该变量的内容在序列化后无法在访问到。
- transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
- 被static关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
- 被final关键字修饰的变量不会被transient关键字所影响,是可以正常序列化的,final变量将直接通过值参与序列化。