JAVA IO之JAVA的序列化与反序列化
-
序列化:把对象转换成字节序列的过程称为对象的序列化,通俗的将就是将程序中的数据写入IO流中。
-
反序列化:把字节序列恢复成对象的过程称为对象的反序列化,和序列化的过程相反,从流中将数据读取出来。
-
序列化的目的:
- 为了保证数据的持久性。
-
序列化的使用场景:
- 把内存中的对象状态保存到文件中或者数据库时。
- 用套接字在网络中传送对象时。
- 通过 RMI 传输对象的时候。
-
关于 Serializable 接口的描述
- 类通过实现 java.io.Serializable 接口以启用其序列化功能。
- 未实现此接口的类将无法使其任何状态序列化或反序列化。
- 可序列化类的所有子类本身都是可序列化的。因为实现接口也是间接的等同于继承
- 序列化接口没有方法或字段,仅用于标识可序列化的语义。
- 被 static 修饰的静态属性无法被序列化,除了 serialVersionUID,序列化类可以通过声明名为 “serialVersionUID” 的字段( 该字段必须是静态 (static)、最终 (final) 的 long 型字段) 显式声明其自己的 serialVersionUID
-
序列化的实现:
-
类通过实现 java.io.Serializable 接口以启用其序列化功能。
-
编译器会把 Car.java 文件编译成字节码文件 Car.class ,Car 类实现了 Serializable 接口,就会根据类的定义给 Car.class 添加一个 serialVersionUID 序列号。Car.java 序列化生成 Car.txt 。在反序列化时 会将 Car.txt 中的序列号和 Car.class 中的序列号进行比较,如果一样则反序列化成功,如果不一样,则失败;当我们对原有的实体类进行属性拓展时,因为实现了 Serializable 接口,再次序列化时可能会抛出异常 InvalidClassException.
- 原因:
- 编译器赋给类的 序列号 根据类的一些列属性计算来的。当类的属性发生变化时,编译器会赋给类新的 序列号 ,因此导致反序列化时,Car.txt 的序列号与 Car.class 的 序列号不一致,因此导致序列号冲突,反序列化失败。
- 解决办法:
- 修改完类后,先序列化一遍,覆盖掉原来的内容,再反序列化。
- 在实体类中显示声明 序列号,等同于将类的序列号定死,这样无论怎样对实体类进行修改序列号都不会发生改变。
- 原因:
-
实体类 Car:
import java.io.Serializable; public class Car implements Serializable { // serialVersionId 序列ID是唯一的 private static final long serialVersionUID = 1029561775672513562L; private String Id; private String carName; private Double price; private static Integer AGE; public String getId() { return Id; } public void setId(String id) { Id = id; } public String getCarName() { return carName; } public void setCarName(String carName) { this.carName = carName; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public static Integer getAGE() { return AGE; } public static void setAGE(Integer AGE) { Car.AGE = AGE; } @Override public String toString() { return "Car{" + "Id=" + Id + ", carName='" + carName + '\'' + ", price=" + price + '}'; } }
-
测试类:
import com.entity.Car; import java.io.*; public class SerializableTest{ public static void main (String args[]) throws Exception{ serializeCar(); System.out.println(deserializeCar().toString()); } // 序列化 public static void serializeCar() throws Exception { Car car = new Car(); car.setId("0001"); car.setCarName("QQQQ"); car.setPrice(1666.33); // ObjectOutPutStream 对象流,将car对象序列化。 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("F:/car.txt"))); oos.writeObject(car); System.out.println("Car 对象序列化成功"); oos.close(); } // 反序列化 public static Car deserializeCar() throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("F:/car.txt"))); Car car = (Car)ois.readObject(); System.out.println("Car 对象反序列化成功!"); return car; } }
-
如何检验 static 修饰的静态变量没有被序列化:
- 拿上面的案例讲,AGE 属性是 Car 类中的一个 static 属性,先将 AGE 设置成 22 ,然后进行序列化与反序列化得到结果:
- 然后注释掉 serializerCar() 序列化方法,修改 Car 的 AGE 属性为 25,直接反序列化,可以观察到,AGE 的属性发生了变化。
正是由于 AGE 被 static 修饰,不能被序列化,没有持久化到 Car.txt,因此第二次反序列化后,AGE 的属性也发生了改变。AGE 作为类变量,它不与单个对象绑定,是和 Car 类绑定的,静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化,所以这么想,你把他都序列化到每个对象,那不是一个东西存了n遍,浪费时间空间,所以存在 class 上,一次就OK了。
-