通过反序列化生成对象的过程主要由以下几个步骤:
1、创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2、 通过对象输入流的readObject()方法读取对象。
其中正是readObject方法返回了一个对象,这个对象就是根据序列化生成的文件而创建的对象,所以反序列化如何创建对象关键就在于readObject方法的实现,那就来探析一下它的实现,它的源码如下:
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();
}
}
}
这个方法会从对象输入流中读取一个对象,其中还包括这个对象的类名、类的签名、类的非静态非瞬时属性值、以及它的所有超类。
该对象所引用的对象是传递式读取的,所以readObject方法可以重建对象的完整等效图,也就复刻一个和原来被序列化对象一样的对象。
由于该方法返回值就是一个Object类型,所以我们重点看实际返回的obj这个对象是怎么创建,可以看出它是通过另一个方法创建,继续查看readObject0方法的源码:
private Object readObject0(boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode();
if (oldMode) {
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}
byte tc;
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}
depth++;
totalObjectRefs++;
try {
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
return readHandle(unshared);
case TC_CLASS:
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
这个方法是readObject方法的底层实现,由于它的返回值类型也是对象类型,所以我们重点看返回的实际对象是哪个,通过观察和debug发现,开关语句执行时下面这行代码,checkResolve方法接收的参数类型是对象类型,返回值类型也是对象类型。
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
那我们来查看readOrdinaryObject对象:
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) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
此处我们重点看的还应该是产生实际返回值的地方,也就是这块:obj = desc.isInstantiable() ? desc.newInstance() : null;
desc.newInstance() 方法原理是利用反射创建了一个对象,本质是调用非序列化父类的无参构造器。
如果该类是 Externalizable 类型的,则调用它自身的访问权限是 public 无参构造方法。
如果该类是 Serializable 类型的,则调用该类的第一个非 Serializable 类型的父类的无参构造方法。
所以这就是为什么通过反序列化创建对象的时候,并不会执行被序列化对象的构造方法。
对于实现 Serializable 接口的类,并不要求该类具有一个无参的构造方法, 因为在反序列化的过程中实际上是去其继承树上找到一个没有实现 Serializable 接口的父类(最终会找到 Object ),然后构造该类的对象,再逐层往下的去设置各个可以反序列化的属性(也就是没有被 transient 修饰的非静态属性)。
我们通过一个具体的案例就可以看出来:
import java.io.Serializable;
public class Users implements Serializable {
private String username;
private int age;
private String sex;
public Users() {
System.out.println("调用了构造方法");
}
/* get和set方法 */ …………
}
创建一个测试类:
import java.io.*;
public class TestUsers {
public static void main(String[] args) {
try {
System.out.println("开始序列化。。。。。");
Users users = new Users();
users.setUsername("fym");
users.setAge(23);
users.setSex("nan");
FileOutputStream fs = new FileOutputStream("users.ser");
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject(users);
os.close();
fs.close();
System.out.println("开始反序列化。。。。。");
FileInputStream fileInputStream = new FileInputStream("users.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Users o = (Users)objectInputStream.readObject();
System.out.println(o);
fileInputStream.close();
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果:
开始序列化。。。。。
调用了构造方法
开始反序列化。。。。。
Users{username='fym', age=23, sex='nan'}
我们将其改造一下,让User类继承一个没有序列化的父类:
class ParentsUser{
public ParentsUser(){
System.out.println("调用了父类构造方法");
}
}
public class Users extends ParentsUser implements Serializable {
……
}
再一次执行上面的测试类,输出结果如下:
开始序列化。。。。。
调用了父类构造方法
调用了构造方法
开始反序列化。。。。。
调用了父类构造方法
Users{username='fym', age=23, sex='nan'}
发现,反序列化创建对象的时候,果然调用了离 User 类最近的没有序列化的超类的无参构造函数。