一、序列化和反序列化的概念
序列化:指把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接口或Externalizable接口的類的對象才能被序列化。
對象序列化包括如下步驟:
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,為它賦予明確的值。
四、transient關鍵字的作用
在實際開發過程中,我們常常會遇到這樣的問題,這個類的有些屬性需要序列化,而其他屬性不需要被序列化,打個比方,如果一個用戶有一些敏感信息(如密碼,銀行卡號等),為了安全起見,不希望在網絡操作(主要涉及到序列化操作,本地序列化緩存也適用)中被傳輸,這些信息對應的變量就可以加上transient關鍵字。換句話說,這個字段的生命周期僅存於調用者的內存中而不會寫到磁盤里持久化。
java 的transient關鍵字為我們提供了便利,你只需要實現Serilizable接口,將不需要序列化的屬性前添加關鍵字transient,序列化對象的時候,這個屬性就不會序列化到指定的目的地中。
范例如下:
UserSerialize類:
package com.paic.egis.smts.activity; import java.io.Serializable; public class UserSerialize implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private String userId; private String userName; private transient String sex; public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return "UserSerialize [userId=" + userId + ", userName=" + userName + ", sex=" + sex + "]"; } }
執行序列化和反序列化
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("111"); 22 u.setUserName("df"); 23 u.setSex("y"); 24 FileOutputStream fo = new FileOutputStream(new File("d://a.txt")); 25 ObjectOutputStream os = new ObjectOutputStream(fo); 26 os.writeObject(u); 27 } 28 29 public static Object deSerialize() throws IOException, ClassNotFoundException { 30 FileInputStream fi = new FileInputStream(new File("d://a.txt")); 31 ObjectInputStream oi = new ObjectInputStream(fi); 32 return oi.readObject(); 33 } 34 35 }
結果如下:

set字段為null,說明反序列化時根本沒有從文件中獲取到信息
另靜態變量不管是否被transient修飾,均不能被序列化
范例:
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 13 private static 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 public void setUserId(String userId) { 25 this.userId = userId; 26 } 27 public String getUserName() { 28 return userName; 29 } 30 public void setUserName(String userName) { 31 this.userName = userName; 32 } 33 @Override 34 public String toString() { 35 return "UserSerialize [userId=" + userId + ", userName=" + userName 36 + ", sex=" + sex + "]"; 37 } 38 39 }
反序列化:
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 import com.paic.pafa.exceptions.BusinessException; 11 12 public class Test { 13 14 public static void main(String[] args) throws Exception{ 15 // serialize(); 16 UserSerialize o = (UserSerialize) deSerialize(); 17 System.out.println(o.toString()); 18 } 19 20 public static void serialize() throws IOException{ 21 UserSerialize u = new UserSerialize(); 22 u.setUserId("111"); 23 u.setUserName("df"); 24 u.setSex("yy"); 25 FileOutputStream fo = new FileOutputStream(new File("d://a.txt")); 26 ObjectOutputStream os = new ObjectOutputStream(fo); 27 os.writeObject(u); 28 } 29 30 public static Object deSerialize() throws IOException, ClassNotFoundException { 31 FileInputStream fi = new FileInputStream(new File("d://a.txt")); 32 ObjectInputStream oi = new ObjectInputStream(fi); 33 return oi.readObject(); 34 } 35 36 }
執行反序列化之前已執行過serialize()序列化操作。
執行結果如下:

若同時執行序列化和反序列化,代碼如下:
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 public static void serialize() throws IOException{ 19 UserSerialize u = new UserSerialize(); 20 u.setUserId("111"); 21 u.setUserName("df"); 22 u.setSex("yy"); 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 }
執行結果如下:

可能會比較奇怪為什么sex有值,這時因為這個值並不是從文件中反序列化讀出來的,是從當前JVM中對應static變量的值。
五、Externalizable接口的作用
它是Serializable接口的子類,有時我們不希望序列化那么多,可以使用這個接口,這個接口的writeExternal()和readExternal()方法可以指定序列化哪些屬性;
范例如下:
UserSerialize類:
1 package com.paic.egis.smts.activity; 2 3 import java.io.Externalizable; 4 import java.io.IOException; 5 import java.io.ObjectInput; 6 import java.io.ObjectOutput; 7 8 public class UserSerialize implements Externalizable{ 9 /** 10 * 11 */ 12 private static final long serialVersionUID = 1L; 13 private String userId; 14 private String userName; 15 16 private String sex; 17 18 public String getSex() { 19 return sex; 20 } 21 public void setSex(String sex) { 22 this.sex = sex; 23 } 24 public String getUserId() { 25 return userId; 26 } 27 public void setUserId(String userId) { 28 this.userId = userId; 29 } 30 public String getUserName() { 31 return userName; 32 } 33 public void setUserName(String userName) { 34 this.userName = userName; 35 } 36 @Override 37 public String toString() { 38 return "UserSerialize [userId=" + userId + ", userName=" + userName 39 + ", sex=" + sex + "]"; 40 } 41 @Override 42 public void writeExternal(ObjectOutput objectoutput) throws IOException { 43 objectoutput.writeObject(userId); 44 45 } 46 @Override 47 public void readExternal(ObjectInput objectinput) throws IOException, 48 ClassNotFoundException { 49 userId = (String) objectinput.readObject(); 50 51 } 52 53 }
序列化和反序列化:
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 public static void serialize() throws IOException{ 19 UserSerialize u = new UserSerialize(); 20 u.setUserId("111"); 21 u.setUserName("df"); 22 u.setSex("yy"); 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 }
執行結果如下:

