使用对象序列化流可能出现的问题及解决方法
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
- 如果出问题了,如何解决?
- 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现
以下面代码为例来演示具体的问题(测试是需要单一注释掉write和read方法):
public static void main(String[] args) throws IOException, ClassNotFoundException { write(); read(); } public static void read() throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myFile\\oos.txt")); Object obj = ois.readObject(); //向下转型 Student s = (Student)obj; System.out.println(s.getName()+","+s.getAge()); ois.close(); } public static void write() throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myFile\\oos.txt")); //创建对象 Student s = new Student("芜湖",30); oos.writeObject(s); oos.close(); }
首先,正常运行代码的结果:
然后先修改Student类(添加一个toString方法),看看是否会出现问题:
此时将报错,异常信息为:
java.io.InvalidClassException: com.iotest_04.Student;
local class incompatible:
stream classdesc serialVersionUID = 7876145757889773612,
local class serialVersionUID = -7765830796884469996
查询此异常可知:
当序列化运行时检测到类中的以下问题之一时抛出。类的串行版本与从流中读取的类描述符的类型不匹配
该类包含未知的数据类型
该类没有可访问的无参数构造函数
进而分析出问题出现在第一条,那么版本不一致说明的具体是什么,通过查看Serializable接口来分析:
序列化运行时将每个可序列化的类与称为serialVersionUID的版本号相关联,该序列号在反序列化期间用于验证序列化对象的发送者和接收者是否已加载与该序列化兼容的对象的类。 如果接收方加载了一个具有不同于相应发件人类的serialVersionUID的对象的类,则反序列化将导致
InvalidClassException
。 一个可序列化的类可以通过声明一个名为"serialVersionUID"
的字段来显式地声明它自己的serialVersionUID,该字段必须是static,final,类型是long
如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。
直白的理解就是,当你修改了对象所属类的属性时,会发生版本不一致的冲突,此冲突会导致
InvalidClassException异常
问题的原因已经找到,那么如何解决?:所有可序列化的类都明确声明serialVersionUID值,显式声明后就可以随意修改所属类的属性,比如我将刚才添加的toString方法删掉在进行读取
结果可以正常运行
接下来解决第二个问题:
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现?
使用transient关键字修饰
使用transient关键字修饰成员变量age后的运行结果: