序列化和反序列化
序列化:将Java对象转换为有序字节流的过程;转化过程保证对象的完整性和可传递性,转换完成后形成有序字节流,以便在网络上进行传输或保存到磁盘中;
反序列化:将有序字节流转化为Java对象的过程;根据有序字节流中保存的对象状态和信息,反序列化出对象。
序列化优点
- 将对象序列化转换为字节流,存储到磁盘中;减少内存压力,也可以永久保存对象,等到下次JVM启动时在通过反序列化还原对象。
- 方便在网络中进行传输
- 通过序列化,将对象保存在内存中,可以再通过此数据得到多个对象的副本。
过程
- 只有实现了Serializable接口的类才能序列化
- 序列化和反序列化API:
- java.io.ObjectInputStream:对象输入流。该类的readObject()方法从输入流中读取字节序列,然后将字节序列反序列化为一个对象并返回。
- java.io.ObjectOutputStream:对象输出流。该类的writeObject(Object obj)方法将将传入的obj对象进行序列化,把得到的字节序列写入到目标输出流中进行输出。
public class TestSerialVersionUID {
private static String pathName = "D:/Customer.txt";
public static void main(String[] args) throws Exception {
serializeCustomer();// 序列化Customer对象
Customer customer = deserializeCustomer();// 反序列Customer对象
System.out.println(customer);
}
private static void serializeCustomer() throws IOException {
Customer customer = new Customer("gacl", 25);
customer.setNum("static Num");
// ObjectOutputStream 对象输出流
try (ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
new File(pathName)))) {
oo.writeObject(customer);
System.out.println("Customer对象序列化成功!");
}
}
private static Customer deserializeCustomer() throws Exception {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File(pathName)))) {
Customer customer = (Customer) ois.readObject();
System.out.println("Customer对象反序列化成功!");
return customer;
}
}
}
@Data
class Customer implements Serializable {
private static final long serialVersionUID = -2812038111916495724L;
//Customer类中没有定义serialVersionUID
private String name;
private int age;
//新增
private int sex;
private static String num;
public Customer(String name, int age) {
this.name = name;
this.age = age;
}
public Customer(String name, int age, int sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public static void setNum(String n){
num = n;
}
public static String getNum(){
return num;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", num=" + num +
'}';
}
}
在运行程序时,main方法分两次运行,两次执行分别注释掉序列化和反序列化方法,模拟在两个JVM下运行,运行结果:
//第一次运行结果
Customer对象序列化成功!
//第二次运行结果
Customer对象反序列化成功!
Customer{name='gacl', age=25, sex=0, num=null}
注意
- 父类实现序列化,子类自动实现序列化,不需要显示实现Serializable接口;
- 当一个属性为对象类型,序列化时也要对对象类型属性进行序列化,即该对象类型也要支持序列化;
- 声明为static和transient(例如:ArrayList中elementData)的不能被序列化,transient代表对象临时数据;
- 静态成员属于类级别(随着类的加载而加载的,与类共存亡),所以不能序列化,即序列化信息中静态成员数据不能被序列化,为默认值,,注意,如果在同一台机器(同一个JVM中,同一个进程)同时测试序列化和反序列化,则反序列化会直接从已经加载好的JVM中读取静态成员数据。
- transient是java的关键字,它只能修饰变量,而不能修改类和方法,因此在序列化对象时,将不会序列化被transient修饰的变量
- serialVersionUID作用:
- 序列化时为了验证版本一致性,保持版本兼容,在进行反序列化时,会将字节流中保存的serialVersionUID的值和本地对象的值进行比较(如果没有显示指定,会自动生成一个),如果相同则说明兼容可以反序列化,如果不相同,则会报错
- 序列化类如果进行兼容升级(如:新增属性),不要修改serialVersionUID字段,避免反序列化失败
- 如果进行不兼容升级,需要修改seriaVersionUID,避免反序列化混乱。
- Java有很多基础类已经实现了serializable接口,比如ArrayList等。