为什么需要?
将内存中的对象信息持久化下来。
Serializable接口?
要想序列化一个对象,该对象的类定义必须实现Serializable接口,该接口只是一个标记接口,并不会承载任何序列化的功能。序列化过程中会检测对象是否是Serializable类型,如果不是就会抛出异常。
serialVersionUID?
以前写用ide程序时,总会碰到提示,让生成一个serialVersionUID,这个id是干嘛的?
简单来说,serialVersionUID是用来做版本控制的。
比方说从客户端传来一个序列化对象,服务端这里做了数据类的改动,无法兼容之前的数据了,服务端就可以为数据类新定义一个id,这样老的数据还带着老的id,这样在服务端就无法解析了。
首先需要在实现了Serializable接口的类定义一个id,序列化时会将这个id写入序列化文件,反序列化时会拿序列化文件中的id与程序内类的id作比较,只有相同时才能反序列化成功。
id可以代表不同的序列化的版本,这就是serialVersionUID的作用。
所以如果我们需要类向前兼容,那么必须保证前后的id是一致的,否则就无法保证兼容。另外最好是手动显示生成一个id,否则java会使用一个默认id,这个默认id可能换了环境就不同了,导致无法反序列化。
看下具体的逻辑:
public final Object readObject()
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}
我们会调用ObjectInputStream对象的readObject方法来反序列化。
后面会走到:
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}
private ObjectStreamClass readClassDesc(boolean unshared)
throws IOException
{
byte tc = bin.peekByte();
ObjectStreamClass descriptor;
switch (tc) {
case TC_NULL:
descriptor = (ObjectStreamClass) readNull();
break;
case TC_REFERENCE:
descriptor = (ObjectStreamClass) readHandle(unshared);
break;
case TC_PROXYCLASSDESC:
descriptor = readProxyDesc(unshared);
break;
case TC_CLASSDESC:
descriptor = readNonProxyDesc(unshared);
break;
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
if (descriptor != null) {
validateDescriptor(descriptor);
}
return descriptor;
}
private ObjectStreamClass readNonProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_CLASSDESC) {
throw new InternalError();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);
passHandle = NULL_HANDLE;
ObjectStreamClass readDesc = null;
try {
readDesc = readClassDescriptor();
} catch (ClassNotFoundException ex) {
throw (IOException) new InvalidClassException(
"failed to read class descriptor").initCause(ex);
}
Class<?> cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
final boolean checksRequired = isCustomSubclass();
try {
if ((cl = resolveClass(readDesc)) == null) {
resolveEx = new ClassNotFoundException("null class");
} else if (checksRequired) {
ReflectUtil.checkPackageAccess(cl);
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
skipCustomData();
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
拿到了一个ObjectStreamClass类的对象,该类是对序列化类的封装。
最后会走到这里:
if (model.serializable == osc.serializable &&
!cl.isArray() &&
suid != osc.getSerialVersionUID()) {
throw new InvalidClassException(osc.name,
"local class incompatible: " +
"stream classdesc serialVersionUID = " + suid +
", local class serialVersionUID = " +
osc.getSerialVersionUID());
}
public long getSerialVersionUID() {
// REMIND: synchronize instead of relying on volatile?
if (suid == null) {
suid = AccessController.doPrivileged(
new PrivilegedAction<Long>() {
public Long run() {
return computeDefaultSUID(cl);
}
}
);
}
return suid.longValue();
}
如果类里面没有定义,就会使用default的方式生成一个id,判断id是否相同。
反序列化与单例?
使用常规的单例模式无法阻止反序列化破坏,尽管这个场景可能极少需要被考虑。
解决方案就是在类定义内重写Object readResolve方法。
class Person implements Serializable{
String name;
int age;
private Person(){
}
private static Person singlton = new Person();
public static Person get(){
return singlton;
}
public Object readResolve(){
return get();
}
}
原理?
在ObjectStreamClass内有一个成员变量:
/** if true, invoke resolveObject() */
private boolean enableResolve;
代表了类中是否定义了readResolve方法:
boolean hasReadResolveMethod() {
requireInitialized();
return (readResolveMethod != null);
}
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
handles.finish(passHandle);
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
检测到实现了readResolve方法,就会调用这个方法来覆盖之前生成的反序列化对象。