Java——IO流 對象的序列化和反序列化流ObjectOutputStream和ObjectInputStream

對象的輸入輸出流 : 主要的做用是用於寫入對象信息與讀取對象信息。 對象信息一旦寫到文件上那麼對象的信息就能夠作到持久化了
  對象的輸出流: ObjectOutputStream
  對象的輸入流:  ObjectInputStreamhtml

使用:java

對象的輸出流將指定的對象寫入到文件的過程,就是將對象序列化的過程,對象的輸入流將指定序列化好的文件讀出來的過程,就是對象反序列化的過程。既然對象的輸出流將對象寫入到文件中稱之爲對象的序列化,那麼可想而知對象所對應的class必需要實現Serializable接口。(查看源碼可得知:Serializable接口沒有任何的方法,只是做爲一個標識接口存在)。jvm

一、將User類的對象序列化ide

 1 class User implements Serializable{//必須實現Serializable接口
 2     String uid;
 3     String pwd;
 4     public User(String _uid,String _pwd){
 5         this.uid = _uid;
 6         this.pwd = _pwd;
 7     }
 8     @Override
 9     public String toString() {
10         return "帳號:"+this.uid+" 密碼:"+this.pwd;
11     }
12 }
13 
14 public class Demo1 {
15 
16     public static void main(String[] args) throws IOException {
17         //假設將對象信息寫入到obj.txt文件中,事先已經在硬盤中創建了一個obj.txt文件
18         File f = new File("F:\\obj.txt");
19         writeObjec(f);
20         System.out.println("OK");
21     }
22     
23     //定義方法把對象的信息寫到硬盤上------>對象的序列化。
24     public static void writeObjec(File f) throws IOException{
25         FileOutputStream outputStream = new FileOutputStream(f);//建立文件字節輸出流對象
26         ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
27         objectOutputStream.writeObject(new User("酒香逢","123"));
28         //最後記得關閉資源,objectOutputStream.close()內部已經將outputStream對象資源釋放了,因此只須要關閉objectOutputStream便可
29         objectOutputStream.close();
30     }
31 }

運行程序獲得記事本中存入的信息:可見已經序列化到記事本中ui

二、將序列化到記事本的內容反序列化this

 1 public class Demo1 {
 2 
 3     public static void main(String[] args) throws IOException, ClassNotFoundException {
 4         //假設將對象信息寫入到obj.txt文件中,事先已經在硬盤中創建了一個obj.txt文件
 5         File f = new File("F:\\obj.txt");
 6         //writeObjec(f);
 7         readObject(f);
 8         System.out.println("OK");
 9     }
10     
11     //定義方法把對象的信息寫到硬盤上------>對象的序列化。
12     public static void writeObjec(File f) throws IOException{
13         FileOutputStream outputStream = new FileOutputStream(f);//建立文件字節輸出流對象
14         ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
15         objectOutputStream.writeObject(new User("酒香逢","123"));
16         //最後記得關閉資源,objectOutputStream.close()內部已經將outputStream對象資源釋放了,因此只須要關閉objectOutputStream便可
17         objectOutputStream.close();
18     }
19     //把文件中的對象信息讀取出來-------->對象的反序列化
20     public static void readObject(File f) throws IOException, ClassNotFoundException{
21         FileInputStream inputStream = new FileInputStream(f);//建立文件字節輸出流對象
22         ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
23         User user = (User)objectInputStream.readObject();
24         System.out.println(user);
25     }
26 }

運行代碼獲得的結果:spa

帳號:酒香逢 密碼:123
OKcode

可是,若是這時候這個obj.txt是咱們項目中一個文件,而項目到後期在原來User類的基礎上添加成員變量String userName;htm

這時候若是咱們再反序列化,則會引起下面的異常:對象

 

Exception in thread "main" java.io.InvalidClassException: xuliehua.User; local class incompatible: stream classdesc serialVersionUID = 2161776237447595412, local class serialVersionUID = -3634244984882257127
  at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:604)
  at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
  at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
  at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
  at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
  at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
  at xuliehua.Demo1.readObject(Demo1.java:48)
  at xuliehua.Demo1.main(Demo1.java:32)

 

異常信息解讀:

serialVersionUID 是用於記錄class文件的版本信息的,serialVersionUID這個數字是JVM(JAVA虛擬界)經過一個類的類名、成員、包名、工程名算出的一個數字。而這時候序列化文件中記錄的serialVersionUID與項目中的不一致,即找不到對應的類來反序列化。

三、若是序列化與反序列化的時候可能會修改類的成員,那麼最好一開始就給這個類指定一個serialVersionUID,若是一類已經指定的serialVersionUID,而後
在序列化與反序列化的時候,jvm都不會再本身算這個 class的serialVersionUID了。

去掉剛纔添加的成員變量userName;,而且在User類中指定一個serialVersionUID 

 1 class User implements Serializable{//必須實現Serializable接口
 2     
 3     private static final long serialVersionUID = 1L;
 4     String uid;
 5     String pwd;
 6     //String userName="名字";//新添加的成員變量
 7     public User(String _uid,String _pwd){
 8         this.uid = _uid;
 9         this.pwd = _pwd;
10     }
11     @Override
12     public String toString() {
13         return "帳號:"+this.uid+" 密碼:"+this.pwd;
14     }
15 }

從新序列化到obj.txt文件中,而後再類中再將userName添加回來(將上面User類中userName字段解註釋),再一次執行反序列化操做,執行的結果跟以前反序列化的結果是一致的。可見這樣解決後咱們後期修改類也是可行的。

四、若是在User類中再添加成員變量,而這個變量爲一個class ,如Address,那麼Address類也必需要實現Serializable接口。

 1 class Address implements Serializable{
 2     String country;
 3     String city;
 4 }
 5 
 6 class User implements Serializable{//必須實現Serializable接口
 7     
 8     private static final long serialVersionUID = 1L;
 9     String uid;
10     String pwd;
11     String userName="名字";//新添加的成員變量
12     Address address;//成員變量爲Address
13     public User(String _uid,String _pwd){
14         this.uid = _uid;
15         this.pwd = _pwd;
16     }
17     @Override
18     public String toString() {
19         return "帳號:"+this.uid+" 密碼:"+this.pwd;
20     }
21 }

五、最後再提一下關鍵字transient關鍵字,當你不想要某些字段序列化時候,能夠用transient關鍵字修飾

 1 class User implements Serializable{//必須實現Serializable接口
 2     
 3     private static final long serialVersionUID = 1L;
 4     String uid;
 5     String pwd;
 6     transient String userName="名字";//新添加的成員變量//添加關鍵字transient後,序列化時忽略
 7     Address address;//成員變量爲Address
 8     public User(String _uid,String _pwd){
 9         this.uid = _uid;
10         this.pwd = _pwd;
11     }
12     @Override
13     public String toString() {
14         return "帳號:"+this.uid+" 密碼:"+this.pwd;
15     }
16 }

最後總結一下對象輸入輸出流使用時須要注意:

1. 若是對象須要被寫出到文件上,那麼對象所屬的類必需要實現Serializable接口。 Serializable接口沒有任何的方法,是一個標識接口而已。
2. 對象的反序列化建立對象的時候並不會調用到構造方法的、(這點文中沒有說到,想要驗證的同窗在構造方法後面加一句System.out.println("構造方法執行嗎?");,實際上構造方法是不執行的,天然這句話也沒有輸出了)
3. serialVersionUID 是用於記錄class文件的版本信息的,serialVersionUID這個數字是經過一個類的類名、成員、包名、工程名算出的一個數字。
4. 使用ObjectInputStream反序列化的時候,ObjeectInputStream會先讀取文件中的serialVersionUID,而後與本地的class文件的serialVersionUID
進行對比,若是這兩個id不一致,反序列則失敗。
5. 若是序列化與反序列化的時候可能會修改類的成員,那麼最好一開始就給這個類指定一個serialVersionUID,若是一類已經指定的serialVersionUID,而後
在序列化與反序列化的時候,jvm都不會再本身算這個 class的serialVersionUID了。
6. 若是一個對象某個數據不想被序列化到硬盤上,可使用關鍵字transient修飾。
7. 若是一個類維護了另一個類的引用,則另一個類也須要實現Serializable接口。

 

 

原創:https://www.cnblogs.com/fnz0/p/5410856.html

相關文章
相關標籤/搜索