23. java安全编码指南之:序列化Serialization
简介
序列化是java中一个非常常用又会被人忽视的功能,我们将对象写入文件需要序列化,同时,对象如果想要在网络上传输也需要进行序列化。
序列化的目的就是保证对象可以正确的传输,那么我们在序列化的过程中需要注意些什么问题呢?
一起来看看吧。
序列化简介
如果一个对象要想实现序列化,只需要实现Serializable接口即可。
奇怪的是Serializable是一个不需要任何实现的接口。如果我们implements Serializable但是不重写任何方法,那么将会使用JDK自带的序列化格式。
但是如果class发送变化,比如增加了字段,那么默认的序列化格式就满足不了我们的需求了,这时候我们需要考虑使用自己的序列化方式。
如果类中的字段不想被序列化,那么可以使用transient关键字。
同样的,static表示的是类变量,也不需要被序列化。
注意serialVersionUID
serialVersionUID 表示的是对象的序列ID,如果我们不指定的话,是JVM自动生成的。在反序列化的过程中,JVM会首先判断serialVersionUID 是否一致,如果不一致,那么JVM会认为这不是同一个对象。
如果我们的实例在后期需要被修改的话,注意一定不要使用默认的serialVersionUID,否则后期class发送变化之后,serialVersionUID也会同样的发生变化,最终导致和之前的序列化版本不兼容。
writeObject和readObject
如果要自己实现序列化,那么可以重写writeObject和readObject两个方法。
注意,这两个方法是private的,并且是non-static的:
private void writeObject(final ObjectOutputStream stream)
throws IOException {
stream.defaultWriteObject();
}
private void readObject(final ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
}
如果不是private和non-static的,那么JVM就不能够发现这两个方法,就不会使用他们来做自定义序列化。
readResolve和writeReplace
如果class中的字段比较多,而这些字段都可以从其中的某一个字段中自动生成,那么我们其实并不需要序列化所有的字段,我们只把那一个字段序列化就可以了,其他的字段可以从该字段衍生得到。
readResolve和writeReplace就是序列化对象的代理功能。
首先,序列化对象需要实现writeReplace方法,表示替换成真正想要写入的对象:
public class CustUserV3 implements java.io.Serializable{
private String name;
private String address;
private Object writeReplace()
throws java.io.ObjectStreamException
{
log.info("writeReplace {}",this);
return new CustUserV3Proxy(this);
}
}