java序列化

269 阅读4分钟

1、序列化与反序列化

  1. 什么叫序列化? 序列化是把一个对象的信息转成可以存储和传输的过程。

  2. 为什么要序列化? 序列化主要是为了传输数据以及存储数据。

  3. 存储的话,可以是缓存,磁盘等。传输的话,可以是XML格式的,字节格式。对象序列化机制是java持久化的一种方式,通过序列化可以轻松在字节和对象间转换。

  4. 反序列化就是对序列化的反向操作,它可以把已存储在磁盘或缓存的数据或经过传输到达目的地的数据转成我们需要的类型数据。

  5. 目前比较流行的格式是JSON,它是轻量级的数据交换格式,方便我们查看和前后端交互。JSON会转成字符串,字符串实现了序列化接口,所以天然是可以传输的。除了字符串,它还能转成字节。

  6. transient关键字:它能控制变量的序列化,也就是说,加上此关键字的变量,不会序列化它。

  7. 序列化ID:在序列化和反序列时会对比版本是否一致性,在JVM中会验证序列化时和反序列化时的id,如果不一致会报错。

2、要想实现序列化必须实现Serializable

  1. 对象通过实现这个接口能够序列化,在序列化时候会判断这个接口,如果没实现会出错。如果子类继承父类了,子类实现了序列化,父类没有,那么在序列化和反序列过程中,子类的属性值会保留,子类继承父类的属性值不会保留,会给一个默认值。还有序列化不保存静态变量
@Test
void name() throws IOException, ClassNotFoundException {
	AnimalPO po = new AnimalPO();
	po.setAge(12);
	po.setName("洞洞波");
	po.setGender("男");
	//序列化保存
	ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("a.txt"));
	stream.writeObject(po);
	stream.close();
	//反序列化
	ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.txt"));
	AnimalPO animalPO = (AnimalPO) inputStream.readObject();
	inputStream.close();
	//输出
	System.out.println(animalPO.toString());
}

以上结果: AnimalPO(id=null, name=洞洞波, age=12, shiftDate=null, gender=男)

  1. 那么,我们看一下不实现Serializable接口的情况: 报错了,提示 不可序列化异常。

  2. 下面我们看一下底层源码是怎么判断的 源码中会判断是否实现Serializable,如果没有直接抛出异常

  3. 除了这个接口,还有string,枚举和数组(Array)也会放行,因为string和枚举都实现了Serializable这个接口,而数组是重写了readObject和writeObject,所以你会 看到arraylist的源码是数组,被transient修饰(以为不会被序列化),但是实际是会被序列化的,因为它自定义了序列化策略。

3、自定义序列化策略

让PO类实现Externalizable,其实Externalizable这个类还实现了Serializable, 说白了,从头到尾还是会判断Serializable这个接口

public class AnimalPO implements Externalizable {
}

实现Externalizable这个接口后,会重写两个方法,分别是writeExternal和readExternal,那么可以测试一下,先不在这两个方法内写东西。还是执行上面的那段代码,结果是:

AnimalPO(id=null, name=null, age=null, shiftDate=null, gender=null)

然后我们在重写的方法里实现序列化和反序列化的逻辑。

public class AnimalPO implements Externalizable {

 
    @TableField("name")
    private String name;


    @TableField("age")
    private Integer age;


    @TableField("gender")
    private String gender;
	
@Override
public void writeExternal(ObjectOutput out) throws IOException {
	out.writeObject(this.name);
	out.writeObject(this.gender);
	out.writeInt(this.age);

}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
	this.name = (String) in.readObject();
	this.gender = (String) in.readObject();
	this.age = in.readInt();

}
}

在执行一次,得到想要的结果:

AnimalPO(id=null, name=洞洞波, age=12, shiftDate=null, gender=男)

4、总结:

  1. 要想实现序列化必须实现Serializable接口或者自己自定义序列化要实现Externalizable接口。

  2. 如果自定义了Externalizable接口,那么需要自己重写writeExternal和readExternal才能实现序列化

  3. arraylist底层数组被transient修饰,因为数组的特性,一旦创建就不可修改, 如果10的长度只是有了1个,剩下的9个就会被NULL填充,不添加transient会把NULL也序列化了,所有会用transient修饰防止序列化NULL,提高效率。因为数组是自定义的序列化策略,所以正常数据是不受transient影响的。

  4. 上面提到的子类继承父类,实现序列化的情况,铁子们可以自己测试,这里就不贴出来了。