java序列化
来源:互联网 发布:Java捕获控制台 编辑:程序博客网 时间:2024/06/05 00:38
问题
最近在dubbo接口扩展上遇到了问题。dubbo的参数及返回对象,肯定是要可序列化的,即实现Serializable接口。需求是需要在接口参数中,加入一个字段,但是担心对原来的consumer产生影响,因此对java序列化进行了一下梳理测试。
顺便说下关于dubbo接口扩展碰到的这个问题,有几点收获:
- 接口的传参,尽量用对象代替多个简单类型的参数,后者不便于加参数
- 返回数据,同样尽量用对象代替简单类型
- 更好的参数或返回数据扩展方案,应该是采用继承原有参数或返回类型的方式
序列化
java序列化,就是将java对象序列化为字节流,可以进行传递或者保存,在使用方对结果进行反序列化,从而获取到原来对象的属性值。
在需要将内存中对象保存到文件,或者直接传输对象时,会用到序列化。dubbo就是在provider和consumer之间传递对象数据。
类定义改变
回到最初的问题,其根本是java类定义在发送方发生改变后,接收方能否正确反序列化数据。
单元测试代码如下:
class Person implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } class NewPerson implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } class ChildPerson extends Person { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } } @Test public void testSerialize() throws IOException, ClassNotFoundException { Person parent = new Person(); parent.setName("Parent"); String parentSerFile = "parent.ser"; String childSerFile = "child.ser"; // 序列化parent到parentSerFile serialize(parent, parentSerFile); Person person = unSerialize(parentSerFile); System.out.println(person.getName()); // OK// NewPerson newPerson = unSerialize(parentSerFile); // java.lang.ClassCastException// System.out.println(newPerson.getName()); // 序列化child到childSerFile ChildPerson child = new ChildPerson(); child.setName("Child"); child.setAge(10); serialize(child, childSerFile); person = unSerialize(childSerFile); System.out.println(person.getName()); // OK System.out.println(((ChildPerson)person).getAge()); } /** * 序列化对象,保存到文件 */ private void serialize(Object obj, String fileName) throws IOException { FileOutputStream fs = new FileOutputStream(fileName); ObjectOutputStream os = new ObjectOutputStream(fs); os.writeObject(obj); os.close(); } /** * 反序列化对象 */ private <T> T unSerialize(String fileName) throws IOException, ClassNotFoundException { ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(fileName)); T obj = (T) inputStream.readObject(); inputStream.close(); return obj; }
类路径及类名必须一致
发送方类com.test1.Class1,接收方是com.test1.Class2或者com.test2.Class1,都不能正确反序列化,报java.lang.ClassCastException异常,如上述代码中注掉的newPerson部分。
类定义发生改变
若类定义发生改变,即发送方和接收方,虽然类路径和类名一致,但是发送方和接收方的类版本等不一致,此时亦会报错。
如上述代码,在序列化生成parent.ser文件后,将Person类添加字段sex,如下:
class Person implements Serializable { private String name; private String sex; ...
然后进行反序列化,会抛出异常java.io.InvalidClassException。提示:local class incompatible: stream classdesc serialVersionUID = 4485681234731380735, local class serialVersionUID = 5015652288950510004
这就是serialVersionUID的作用了。在Person的类定义中,加入以下代码就OK了:
private static final long serialVersionUID = 4485681234731380735L;
因此,在定义可序列化对象的时候,强烈建议显式定义serialVersionUID,防止类由于版本等的问题,无法匹配从而无法反序列化问题。
在Eclipse中,是会自动提示生成随机serialVersionUID的,在Intellij idea中同样可以,需要开启:File | Settings | Editor | Inspections 中 Java | Serialization issues | Serializable class without ‘serialVersionUID’ , 开启后,用alt+enter就可以显式生成serialVersionUID了。
更好的扩展方式
更好的扩展方式,应该是定义可序列化对象类的子类,将子类对象序列化后,仍然可以反序列化为原父类对象,从而对原来的序列化数据接收方无影响。
如上述测试代码中的将ChildPerson类的对象序列化后的文件,反序列化为Person类对象。
在一个已成熟稳定的系统中,扩展的时候,应尽量采用这种方式;但由于我们的系统刚刚搭建完成,因此我直接显式声明了serialVersionUID
- Serializable java序列化
- Java对象序列化
- java序列化-Serializable
- Serializable java序列化
- Serializable java序列化
- Java对象序列化
- Java对象序列化
- Java对象序列化
- Serializable java序列化
- JAVA序列化Serializable
- java对象序列化
- Java 对象序列化
- DEMO-JAVA序列化
- Java 对象序列化
- java 序列化
- Java对象序列化
- Serializable java序列化
- java序列化介绍
- All in all
- 使用pssh进行并行批量操作
- 浅析C# 中object sender与EventArgs e
- [BZOJ4515][SDOI2016] 游戏 - 树链剖分 - 半平面交 - 标记永久化
- c语言数据类型 32位移植到64位系统需要修改的地方
- java序列化
- 线段树
- redis集群节点新增、删除、重新分配slot实战
- mysql 5.7多源复制中断如何处理
- (十五)、构造方法
- 设计模式 创建型模式之单例模式
- Java学习——入门阶段(仅供参考)
- UML
- 前端学习笔记