今天调试项目的时候报了一个InvalidClassException错误。经过调试,发现是服务器和客户端版本不一样,导致某个bean里面的serialVersionUID不一样。今天更新一下博客,复习一下对象的序列化。
对象要想序列化,就要继承Serializable接口。但是Serializable接口里面没有任何代码。这是因为jvm在序列化对象之前会先做一下instanceof判断。
student代码:
package codeTest; import java.io.Serializable; public class Student implements Serializable{ private String name; private String passwd; private Node n; private String n1; public final String getName() { return name; } public final void setName(String name) { this.name = name; } public final String getPasswd() { return passwd; } public final void setPasswd(String passwd) { this.passwd = passwd; } public final Node getN() { return n; } public final void setN(Node n) { this.n = n; } public String toString(){ return "name:"+this.name+"\t"+"passwds:"+this.passwd+"\t"+"node:"+this.n+"\r\n"; } }
执行代码
Student s = new Student(); System.out.println(s instanceof Serializable);
结果为true。
表示这个student类可以被实例化。
node类:
package codeTest; import java.io.Serializable; public class Node implements Serializable{ //节点内容,需要什么加什么 private String name; //构造函数 public String toString(){ return "这是一个node2"; } }
需要注意的是我们要序列化student,就要让student里面的属性也实现序列化接口,如node类。
序列化可以用来做什么? 一般情况下是用来做远程调用的。比如A客户端向B服务器端发送一个类的实例,就是将类的实例序列化成字符流的形式,通过网络传送给B服务器端。服务器端接受到A发来的流,然后强制将其转换为类的实例(当然,发送什么类需要AB协商好.)
今天的测试小程序就不进行远程接口调用了,今天只测试一个类实例的持久化。白点说就是把一个类实例化,然后将这个实例化的类保存到一个文件里面,然后在读取这个文件,将实例化的类的对象反序列化还原回来。
序列化student类的方法:
public static void outPut() { Student s = new Student(); s.setName("张三"); s.setPasswd("123123"); Node d = new Node(); s.setN(d); try { ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("d://test.txt")); output.writeObject(s); output.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
反序列化方法:
public static void inPut(){ try { ObjectInputStream input = new ObjectInputStream(new FileInputStream("d://test.txt")); Student s1 = (Student)input.readObject(); System.out.println(s1); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
执行序列化方法,可以看到d盘下多了一个test.txt文件,打开发现全是乱码。
执行序列化方法,结果:
name:张三 passwds:123123 node:这是一个node2
可以看到,反序列化方法把序列化接到的文件给解析出来了。当我们生成一个具体对象,想要传输或者保存时,可以用这种序列化的形式。
以上的student类没有指定版本号serialVersionUID,那么在jvm进行序列化的时候就会默认生成一个版本号serialVersionUID,在反序列化的时候就会根据这个版本号进行校对,如果不对应的话就会抛出文首提到的那个错误InvalidClassException。serialVersionUID和文件的类型,名字,属性,方法都相关。比如我在student里面加入任意方法或属性,就会报错:
java.io.InvalidClassException: codeTest.Student; local class incompatible: stream classdesc serialVersionUID = 2222222222222222222, local class serialVersionUID = 222221222222222222 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350) at codeTest.serialVersionTest.inPut(serialVersionTest.java:48) at codeTest.serialVersionTest.main(serialVersionTest.java:22)
但很多时候我们希望对旧版本兼容,就要用到serialVersionUID属性。在student里面加入private static final long serialVersionUID 值可以任意指定。这时候发现,只要serialVersionUID的值不变,不论怎么修改student的其他属性(node的serialVersionUID也要不变),即使将student的其他属性全部删掉,变成
package codeTest; import java.io.Serializable; public class Student implements Serializable{ /** * @Fields serialVersionUID : TODO(用一句话描述这个变量表示什么) */ private static final long serialVersionUID = 222122222222222L; }
执行反序列化方法结果为:
codeTest.Student@768965fb
兼容性太强了有么有。。。
如果希望序列化的地方也要更新,那么只需要把版本号改一下就ok了。