一、序列化和反序列化的概念
序列化:指把java对象转换为字节序列的过程。
反序列化:指把字节序列恢复为java对象的过程。
对象的序列化主要有两种用途:
1) 把对象的字节序列保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
1.当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
2.在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
二、JDK类库中的序列化API
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable接口的类的对象才能被序列化。
对象序列化包括如下步骤:
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流,字节数组输出流;
2) 通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流,字节数组输入流;
2) 通过对象输入流的readObject()方法读取对象。
1、类未实现Serializable接口,进行序列化的范例:
定义一个未实现Serializable的类:User
1 package com.paic.egis.smts.activity; 2 3 4 public class User{ 5 private String userId; 6 private String userName; 7 public String getUserId() { 8 return userId; 9 } 10 @Override 11 public String toString() { 12 return "User [userId=" + userId + ", userName=" + userName + "]"; 13 } 14 public void setUserId(String userId) { 15 this.userId = userId; 16 } 17 public String getUserName() { 18 return userName; 19 } 20 public void setUserName(String userName) { 21 this.userName = userName; 22 } 23 24 }
序列化:
1 package com.paic.egis.smts.activity; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.ObjectInputStream; 8 import java.io.ObjectOutputStream; 9 10 public class Test { 11 12 public static void main(String[] args) throws Exception{ 13 serialize(); 14 User o = (User) deSerialize(); 15 System.out.println(o.toString()); 16 17 } 18 19 public static void serialize() throws IOException{ 20 User u = new User(); 21 u.setUserId("11"); 22 u.setUserName("df"); 23 FileOutputStream fo = new FileOutputStream(new File("d://a.txt")); 24 ObjectOutputStream os = new ObjectOutputStream(fo); 25 os.writeObject(u); 26 } 27 28 public static Object deSerialize() throws IOException, ClassNotFoundException { 29 FileInputStream fi = new FileInputStream(new File("d://a.txt")); 30 ObjectInputStream oi = new ObjectInputStream(fi); 31 return oi.readObject(); 32 } 33 34 }
运行结果如下:
运行报错!
2、类实现Serializable接口,进行序列化和反序列化的范例:
定义一个实现Serializable的类:UserSerialize
1 package com.paic.egis.smts.activity; 2 3 import java.io.Serializable; 4 5 public class UserSerialize implements Serializable{ 6 /** 7 * 8 */ 9 private static final long serialVersionUID = 1L; 10 private String userId; 11 private String userName; 12 public String getUserId() { 13 return userId; 14 } 15 @Override 16 public String toString() { 17 return "User [userId=" + userId + ", userName=" + userName + "]"; 18 } 19 public void setUserId(String userId) { 20 this.userId = userId; 21 } 22 public String getUserName() { 23 return userName; 24 } 25 public void setUserName(String userName) { 26 this.userName = userName; 27 } 28 29 }
序列化和反序列化:
1 package com.paic.egis.smts.activity; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.ObjectInputStream; 8 import java.io.ObjectOutputStream; 9 10 public class Test { 11 12 public static void main(String[] args) throws Exception{ 13 serialize(); 14 UserSerialize o = (UserSerialize) deSerialize(); 15 System.out.println(o.toString()); 16 17 } 18 19 public static void serialize() throws IOException{ 20 UserSerialize u = new UserSerialize(); 21 u.setUserId("11"); 22 u.setUserName("df"); 23 FileOutputStream fo = new FileOutputStream(new File("d://a.txt")); 24 ObjectOutputStream os = new ObjectOutputStream(fo); 25 os.writeObject(u); 26 } 27 28 public static Object deSerialize() throws IOException, ClassNotFoundException { 29 FileInputStream fi = new FileInputStream(new File("d://a.txt")); 30 ObjectInputStream oi = new ObjectInputStream(fi); 31 return oi.readObject(); 32 } 33 34 }
运行结果如下:
三、serialVersionUID的作用
serialVersionUID作用:序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
有两种生成方式:
一个是默认的1L,比如:private static final long serialVersionUID = 1L;
一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:private static final long serialVersionUID = xxxxL;
下面举例说明下:
还是上面说到的类:UserSerialize,把定义的serialVersionUID去掉。
1 package com.paic.egis.smts.activity; 2 3 import java.io.Serializable; 4 5 public class UserSerialize implements Serializable{ 6 // /** 7 // * 8 // */ 9 // private static final long serialVersionUID = 1L; 10 private String userId; 11 private String userName; 12 public String getUserId() { 13 return userId; 14 } 15 @Override 16 public String toString() { 17 return "User [userId=" + userId + ", userName=" + userName + "]"; 18 } 19 public void setUserId(String userId) { 20 this.userId = userId; 21 } 22 public String getUserName() { 23 return userName; 24 } 25 public void setUserName(String userName) { 26 this.userName = userName; 27 } 28 29 }
序列化和反序列化:
1 package com.paic.egis.smts.activity; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.ObjectInputStream; 8 import java.io.ObjectOutputStream; 9 10 public class Test { 11 12 public static void main(String[] args) throws Exception{ 13 serialize(); 14 UserSerialize o = (UserSerialize) deSerialize(); 15 System.out.println(o.toString()); 16 17 } 18 19 public static void serialize() throws IOException{ 20 UserSerialize u = new UserSerialize(); 21 u.setUserId("11"); 22 u.setUserName("df"); 23 FileOutputStream fo = new FileOutputStream(new File("d://a.txt")); 24 ObjectOutputStream os = new ObjectOutputStream(fo); 25 os.writeObject(u); 26 } 27 28 public static Object deSerialize() throws IOException, ClassNotFoundException { 29 FileInputStream fi = new FileInputStream(new File("d://a.txt")); 30 ObjectInputStream oi = new ObjectInputStream(fi); 31 return oi.readObject(); 32 } 33 34 }
运行结果:
是成功的。
下面我们修改下UserSerialize类:添加一个熟悉sex
1 package com.paic.egis.smts.activity; 2 3 import java.io.Serializable; 4 5 public class UserSerialize implements Serializable{ 6 // /** 7 // * 8 // */ 9 // private static final long serialVersionUID = 1L; 10 private String userId; 11 private String userName; 12 13 private String sex; 14 15 public String getSex() { 16 return sex; 17 } 18 public void setSex(String sex) { 19 this.sex = sex; 20 } 21 public String getUserId() { 22 return userId; 23 } 24 @Override 25 public String toString() { 26 return "User [userId=" + userId + ", userName=" + userName + "]"; 27 } 28 public void setUserId(String userId) { 29 this.userId = userId; 30 } 31 public String getUserName() { 32 return userName; 33 } 34 public void setUserName(String userName) { 35 this.userName = userName; 36 } 37 38 }
这时执行反序列化操作:
1 package com.paic.egis.smts.activity; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 import java.io.ObjectInputStream; 8 import java.io.ObjectOutputStream; 9 10 public class Test { 11 12 public static void main(String[] args) throws Exception{ 13 // serialize(); 14 UserSerialize o = (UserSerialize) deSerialize(); 15 System.out.println(o.toString()); 16 17 } 18 19 public static void serialize() throws IOException{ 20 UserSerialize u = new UserSerialize(); 21 u.setUserId("11"); 22 u.setUserName("df"); 23 FileOutputStream fo = new FileOutputStream(new File("d://a.txt")); 24 ObjectOutputStream os = new ObjectOutputStream(fo); 25 os.writeObject(u); 26 } 27 28 public static Object deSerialize() throws IOException, ClassNotFoundException { 29 FileInputStream fi = new FileInputStream(new File("d://a.txt")); 30 ObjectInputStream oi = new ObjectInputStream(fi); 31 return oi.readObject(); 32 } 33 34 }
运行结果:
Exception in thread "main" java.io.InvalidClassException: com.paic.egis.smts.activity.UserSerialize; local class incompatible: stream classdesc serialVersionUID = -3074015237131537750, local class serialVersionUID = -126714174808369076 at java.io.ObjectStreamClass.initNonProxy(Unknown Source) at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source) at java.io.ObjectInputStream.readClassDesc(Unknown Source) at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) at java.io.ObjectInputStream.readObject0(Unknown Source) at java.io.ObjectInputStream.readObject(Unknown Source) at com.paic.egis.smts.activity.Test.deSerialize(Test.java:31) at com.paic.egis.smts.activity.Test.main(Test.java:14)
意思就是说,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID
因此强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。