序列化与反序列化
一、概念
- 序列化: 将数据结构或对象转换成二进制字节流的过程
- 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
序列化和反序列针对的是对象。
序列化的用途:
(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的值,那么对象的值为修改后的值。
本文详细介绍了Java中的序列化与反序列化,包括概念、static与transient关键字的影响、序列化UID问题以及实际操作示例。强调了序列化在存储对象和深克隆中的作用,同时解释了static和transient修饰符如何影响序列化过程。通过一个User实体类和测试类的代码展示了序列化和反序列化的实现,并指出指定serialVersionUID的重要性以及不指定时可能出现的问题。此外,还举例说明了transient关键字如何使变量在反序列化后变为默认值,而static变量不受序列化影响。
1910

被折叠的 条评论
为什么被折叠?



