紧接着上一篇,我们来看看另一个序列化类[b]JavaSerializer[/b](参考源码版本3.0.13)。
这个类一般是用来序列化我们自定义类的(DefaultSerializer),所以更多的时候我们用到的是它,今天打算分析两个方面:
1.JavaSerializer本身实现分析;
2.分析下反序列化的时候为什么会存在父类同名field覆盖子类field值问题;
[b][size=medium]JavaSerializer实现[/size][/b]
接着我们看下JavaSerializer的writeObject方法:
说了这么多,还是看一个例子比较直观:
序列化测试类(HelloWorld):
序列化代码:
序列化结果:(图画的不好,有空重新画一幅)
[img]http://dl.iteye.com/upload/attachment/0072/9403/4202d314-fe1f-3cfa-a139-cbcab9c658e7.jpg[/img]
[b][size=medium]JavaSerializer存在的问题[/size][/b]
之前在文章开始,我提到在序列化的时候存在父类覆盖子类同名field的情况,现在我们就来看下源码,
分析下为什么会出现这样的情况?(只是谈谈自己看法,若是有不对的地方,还希望各位能够提点一下,^_^)
JavaSerializer的实现我们前面已经分析,现在我们分析下JavaDeSerializer的实现(关键就在于序列化和反序列化的不一致引起的)
我们看下getFieldMap(cl)的实现:
[b]从上面代码我们得出一个结论:对于同名字段fieldMap中存放的是子类field;
另外在序列化的时候,对于同名field是子类值在前,
父类值在后,于是在反序列化赋值的时候就变成:子类值->子类field, 父类值->子类field 所以就发生了值覆盖问题![/b]
我们看一个例子:
然后我们写一下序列化代码:
下面是序列化结果:
[img]http://dl.iteye.com/upload/attachment/0072/9476/13b913c5-7bfe-38cc-9a7c-122190497d5b.jpg[/img]
这时候,我们反序列化一下:
朋友们可以先猜测一下,这里的输出结果是多少? 我想大家看了前面的分析一定能看出,这里的结果是2!
通过debug可以发现,intValue先被覆值成1,最后被赋值成2!
[size=medium][b]总结[/b][/size]
1.JavaSerializer如何序列化一个自定义类;
2.自定义类的序列化格式:Mt 类名长度 具体类名 字段1 字段2 ... 结束标记z:
3.JavaSerialzer序列化存在的问题;
这个类一般是用来序列化我们自定义类的(DefaultSerializer),所以更多的时候我们用到的是它,今天打算分析两个方面:
1.JavaSerializer本身实现分析;
2.分析下反序列化的时候为什么会存在父类同名field覆盖子类field值问题;
[b][size=medium]JavaSerializer实现[/size][/b]
// 首先看一下JavaSerializer的构造方法
public JavaSerializer(Class cl, ClassLoader loader)
{
...
// 存放简单类型字段
ArrayList primitiveFields = new ArrayList();
// 存放复杂类型字段
ArrayList compoundFields = new ArrayList();
// 获取子类到父类所有field字段(这里你可能就会想,万一子类和父类存在同名filed怎么办?)
for (; cl != null; cl = cl.getSuperclass()) {
Field []fields = cl.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
// 过滤掉这两种类型的field(可以思考下为什么要过滤掉这两种类型)
if (Modifier.isTransient(field.getModifiers())
|| Modifier.isStatic(field.getModifiers()))
continue;
field.setAccessible(true);
// 按field类型存入arrayList(若是基本类型或者是java.lang包下的类则当做是primitiveField,否则当做compoundField)
if (field.getType().isPrimitive()
|| (field.getType().getName().startsWith("java.lang.")
&& ! field.getType().equals(Object.class)))
primitiveFields.add(field);
else
compoundFields.add(field);
}
}
// 合并成一个list并转换成数组 (ps: 这样看来之前用两个list来存放似乎没有什么意义?!)
ArrayList fields = new ArrayList();
fields.addAll(primitiveFields);
fields.addAll(compoundFields);
_fields = new Field[fields.size()];
fields.toArray(_fields);
}
接着我们看下JavaSerializer的writeObject方法:
public void writeObject(Object obj, AbstractHessianOutput out)
throws IOException
{
// 省略部分代码...
Class cl = obj.getClass();
try {
// 写类头,输出格式: 'Mt' [类名长度] [类名] 譬如HelloWorld.java输出的就是:Mt 数值10 字符HelloWorld
out.writeMapBegin(cl.getName());
// 循环序列化每一个field
for (int i = 0; i < _fields.length; i++) {
Field field = _fields[i];
// 序列化field名,格式:'S' [字段名长度] [字段名]
out.writeString(field.getName());
// 序列化filed值(内部获取该field对应的序列化类,然后执行序列化(其实又回到了Hessian源码学习(三)中所说的内容)
out.writeObject(field.get(obj));
}
// 输出结束标记:'z'
out.writeMapEnd();
} catch (IllegalAccessException e) {
throw new IOException(String.valueOf(e));
}
}
说了这么多,还是看一个例子比较直观:
序列化测试类(HelloWorld):
public class HelloWorld {
private int field1;
public String field2;
protected boolean field3;
short[] field4;
序列化代码:
HelloWorld obj = new HelloWorld();
obj.setField1(1234);
obj.setField2("diaocow");
obj.setField3(false);
obj.setField4(new short[] { (short) 125, (short) 28 });
OutputStream os = new FileOutputStream("hessianOutput");
AbstractHessianOutput out = new HessianOutput(os);
out.setSerializerFactory(new SerializerFactory());
out.writeObject(obj);
序列化结果:(图画的不好,有空重新画一幅)
[img]http://dl.iteye.com/upload/attachment/0072/9403/4202d314-fe1f-3cfa-a139-cbcab9c658e7.jpg[/img]
[b][size=medium]JavaSerializer存在的问题[/size][/b]
之前在文章开始,我提到在序列化的时候存在父类覆盖子类同名field的情况,现在我们就来看下源码,
分析下为什么会出现这样的情况?(只是谈谈自己看法,若是有不对的地方,还希望各位能够提点一下,^_^)
JavaSerializer的实现我们前面已经分析,现在我们分析下JavaDeSerializer的实现(关键就在于序列化和反序列化的不一致引起的)
public JavaDeserializer(Class cl)
{
...
// 最关键的一段代码
_fieldMap = getFieldMap(cl);
...
}
我们看下getFieldMap(cl)的实现:
protected HashMap getFieldMap(Class cl)
{
// 创建一个hashmap存放field (有没有觉得和JavaSerializer不一致,JavaSerializer是把field存放到一个list中去)
HashMap fieldMap = new HashMap();
// 循环读取从子类到父类的field
for (; cl != null; cl = cl.getSuperclass()) {
Field []fields = cl.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (Modifier.isTransient(field.getModifiers()) ||
Modifier.isStatic(field.getModifiers()))
continue;
// 重点! 由于是从子类循环到父类,所以若是存在同名字段,fieldMap中放的都是子类字段!
else if (fieldMap.get(field.getName()) != null)
continue;
try {
field.setAccessible(true);
} catch (Throwable e) {
e.printStackTrace();
}
fieldMap.put(field.getName(), field);
}
}
return fieldMap;
[b]从上面代码我们得出一个结论:对于同名字段fieldMap中存放的是子类field;
另外在序列化的时候,对于同名field是子类值在前,
父类值在后,于是在反序列化赋值的时候就变成:子类值->子类field, 父类值->子类field 所以就发生了值覆盖问题![/b]
我们看一个例子:
public class FatherClass {
private int intValue;
public void setFatherValue(int intValue) {
this.intValue = intValue;
}
public int getFatherValue() {
return this.intValue;
}
}
public class ChildClass extends FatherClass {
private int intValue;
public void setChildValue(int intValue) {
this.intValue = intValue;
}
public int getChildrValue() {
return this.intValue;
}
}
然后我们写一下序列化代码:
ChildClass childClass = new ChildClass();
childClass.setFatherValue(2);
childClass.setChildValue(1);
OutputStream os = new FileOutputStream("hessianOutput");
AbstractHessianOutput out = new HessianOutput(os);
out.setSerializerFactory(new SerializerFactory());
out.writeObject(childClass);
下面是序列化结果:
[img]http://dl.iteye.com/upload/attachment/0072/9476/13b913c5-7bfe-38cc-9a7c-122190497d5b.jpg[/img]
这时候,我们反序列化一下:
InputStream in = new FileInputStream("hessianOutput");
AbstractHessianInput hessianInput = new HessianInput(in);
ChildClass childClass = (ChildClass) hessianInput.readObject();
System.out.println("ChildClassValue:" + childClass.getChildrValue());
朋友们可以先猜测一下,这里的输出结果是多少? 我想大家看了前面的分析一定能看出,这里的结果是2!
通过debug可以发现,intValue先被覆值成1,最后被赋值成2!
[size=medium][b]总结[/b][/size]
1.JavaSerializer如何序列化一个自定义类;
2.自定义类的序列化格式:Mt 类名长度 具体类名 字段1 字段2 ... 结束标记z:
3.JavaSerialzer序列化存在的问题;