java序列化文件结构_从源码解析JAVA序列化是否需要空参构造方法?

本文探讨了JAVA序列化过程中是否需要空参数构造方法的问题。实验结果显示,当父类或子类实现Serializable接口时,序列化是否成功取决于第一个非可序列化超类是否有默认构造方法。如果该超类是Object,则无需空参构造;否则,需要提供空参构造以避免`InvalidClassException`。
摘要由CSDN通过智能技术生成

发现网络上对于反序列化时,什么时候需要空参构造器都模糊不清,也没有一个准确的一个概念,因此我们由现象到源码一探究竟

先上一个父类和子类都没有空参构造器,父类实现Serializable接口,子类间接实现Serializable接口,序列化子类对象sub

class Data implements Serializable{

private int n;

public Data(int n){

this.n = n;

}

public String toString(){

return Integer.toString(n);

}

}

class sub extends Data{

private static int m = 6;

private int k;

public sub(int n) {

super(n);

}

}

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out"));

sub d = new sub(10);

out.writeObject(d);

ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out"));

sub f = (sub) in.readObject();

序列化成功了,说明这种情况父类子类不需要空参构造器,序列化出来的文件是这样的

¬í �sr main.subbÊ��uéªP� �I �kxr main.Dataç��Âût÷?� �I �nxp

接下来尝试父类和子类都没有空参构造器,父类不实现Serializable接口,子类实现Serializable接口,序列化子类对象sub

class Data{

private int n;

public Data(int n){

this.n = n;

}

public String toString(){

return Integer.toString(n);

}

}

class sub extends Data implements Serializable{

private static int m = 6;

private int k;

public sub(int n) {

super(n);

}

}

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out"));

sub d = new sub(10);

out.writeObject(d);

ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out"));

sub f = (sub) in.readObject();

结果会发现提示找不到可用的构造器

Exception in thread "main" java.io.InvalidClassException: main.sub; no valid constructor

at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150)

at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:790)

at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1782)

at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)

at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)

at main.Worm.main(Worm.java:81)

序列化出的文件是这样的

¬í �sr main.subµ|�)CO�I� �I �kxp

会什么会出现这种情况呢,最后我们追踪到ObjectStreamClass类的一个方法,是引起异常的主要原因

//ObjectStreamClass.java

/**

* Returns subclass-accessible no-arg constructor of first non-serializable

* superclass, or null if none found. Access checks are disabled on the

* returned constructor (if any).

*/

private static Constructor> getSerializableConstructor(Class> cl) {

Class> initCl = cl;

while (Serializable.class.isAssignableFrom(initCl)) {

if ((initCl = initCl.getSuperclass()) == null) {

return null;

}

}

try {

Constructor> cons = initCl.getDeclaredConstructor((Class>[]) null);

int mods = cons.getModifiers();

if ((mods & Modifier.PRIVATE) != 0 ||

((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&

!packageEquals(cl, initCl)))

{

return null;

}

cons = reflFactory.newConstructorForSerialization(cl, cons);

cons.setAccessible(true);

return cons;

} catch (NoSuchMethodException ex) {

return null;

}

}

方法的注释是

Returns subclass-accessible no-arg constructor of first non-serializable superclass, or null if none found. Access checks are disabled on the returned constructor (if any)。

翻译过来的意思就是,返回第一个非可序列化超类的子类可访问的no-arg构造函数,如果没有找到则返回null。在返回的构造函数上禁用访问检查(如果有的话)。

因此我们就明白了

为什么在第一个例子中没有丢出InvalidClassException异常,因为第一个例子的第一个非可序列化超类是Object,Object没有显式的构造方法,因此就是默认有默认的空参构造方法,符合条件

第二个例子中丢出InvalidClassException异常是因为第二个例子的第一个非可序列化超类是Date,因为Date没有显示的构造方法,因此丢出异常

最后我们验证一下,如果给Date类加上默认空参构造方法,是否还能编译通过

class Data{

private int n;

public Data(int n){

this.n = n;

}

public Data(){

this.n = 10;

}

public String toString(){

return Integer.toString(n);

}

}

b6797afff7df

最后返回成功了,这也验证了我们的想法。

最后我们得到的结论是,序列化时,如果第一个非可序列化超类非Object类,那么一定要预留一个空参构造方法,否则就会序列化失败

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值