序列化与反序列化
一、概念
- 序列化: 将数据结构或对象转换成二进制字节流的过程
- 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
序列化和反序列针对的是对象。
序列化的用途:
(1)序列化可以将对象转化成字节流后存储在磁盘中,在需要的时候反序列化还原成对象,节约了内存空间。
(2)使用深克隆中使用。
二、static与transient
使用 transient
关键字修饰:
transient
关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient
修饰的变量值不会被持久化和恢复。
transient
只能修饰变量,不能修饰类和方法。transient
修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰int
类型,那么反序列后结果就是0
。
使用static关键字修饰:
static
变量因为不属于任何对象(Object),所以无论有没有 transient
关键字修饰,均不会被序列化。
值得注意的是,二者还是有区别的,使用了transient修饰的字段,序列化结果是默认值,对象修改属性值后反序列仍然为默认值,即序列化结果。但static修饰的字段,对象属性修改后为实时值,不受序列化结果影响。
虽然对象的static属性不受序列化影响,但是静态类的对象同样可以进行序列化和反序列化。
三、序列化UID问题
序列化的UID是用于在翻序列化的过程中比对类的UID是否与本地一致,如果没有指定UID,该类被修改过则会重新用摘要算法生成新的UID,会出现无法与本地UID匹配的问题,导致加载失败,所以一般我们会指定serialVersionUID,保证在类修改后,仍能正常加载,不同的类可以指定相同的UID,原因是只在该类运行过程中进行判断,不会涉及其他类。
private final static Long serialVersionUID = 1L;
四、实践
User实体类
package serialize.ex01;
import java.io.Serializable;
public class User implements Serializable {
//序列化ID,字段名不能错
private static final long serialVersionUID = 1L;
private String name = "AAA";
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
package serialize.ex01;
import java.io.*;
import java.lang.reflect.InvocationHandler;
public class Test {
public static void main(String[] args) {
User user = new User();
user.setAge(18);
user.setName("CCC");
// 序列化
try(FileOutputStream fileOutputStream = new FileOutputStream(new File("C:\\Users\\siuguan\\Desktop\\studentObj.txt"));
ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream)) {
outputStream.writeObject(user);
System.out.println("序列化成功");
}catch (IOException e){
e.printStackTrace();
}
//反序列化
try(FileInputStream fileInputStream = new FileInputStream(new File("C:\\Users\\siuguan\\Desktop\\studentObj.txt"));
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)){
User user1 = (User) objectInputStream.readObject();
System.out.println(user1);
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
}
执行结果:
序列化成功
User{name='CCC', age=18}
值得注意的是,要指定serialVersionUID ,如果不指定的话,序列化ID将有默认的摘要算法生成,因此,对象发生改变时,序列化ID又再次生成了一次,两次的序列化ID不同,会导致反序列化的时候出错。
private static final long serialVersionUID = 1L;
如果把User的属性修改成:
private transient String name;
private static int age;
则name序列化后null,而age则不受序列化影响,即在序列化修改age的值,那么对象的值为修改后的值。