自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java對(duì)象的序列化與反序列化

開發(fā) 開發(fā)工具
序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程。一般將一個(gè)對(duì)象存儲(chǔ)至一個(gè)儲(chǔ)存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過程中,可以是字節(jié)或是XML等格式。

序列化與反序列化

序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程。一般將一個(gè)對(duì)象存儲(chǔ)至一個(gè)儲(chǔ)存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過程中,可以是字節(jié)或是XML等格式。而字節(jié)的或XML編碼格式可以還原完全相等的對(duì)象。這個(gè)相反的過程又稱為反序列化。

Java對(duì)象的序列化與反序列化

在Java中,我們可以通過多種方式來創(chuàng)建對(duì)象,并且只要對(duì)象沒有被回收我們都可以復(fù)用該對(duì)象。但是,我們創(chuàng)建出來的這些Java對(duì)象都是存在于JVM的堆內(nèi)存中的。只有JVM處于運(yùn)行狀態(tài)的時(shí)候,這些對(duì)象才可能存在。一旦JVM停止運(yùn)行,這些對(duì)象的狀態(tài)也就隨之而丟失了。

[[222942]]

但是在真實(shí)的應(yīng)用場(chǎng)景中,我們需要將這些對(duì)象持久化下來,并且能夠在需要的時(shí)候把對(duì)象重新讀取出來。Java的對(duì)象序列化可以幫助我們實(shí)現(xiàn)該功能。

對(duì)象序列化機(jī)制(object serialization)是Java語言內(nèi)建的一種對(duì)象持久化方式,通過對(duì)象序列化,可以把對(duì)象的狀態(tài)保存為字節(jié)數(shù)組,并且可以在有需要的時(shí)候?qū)⑦@個(gè)字節(jié)數(shù)組通過反序列化的方式再轉(zhuǎn)換成對(duì)象。對(duì)象序列化可以很容易的在JVM中的活動(dòng)對(duì)象和字節(jié)數(shù)組(流)之間進(jìn)行轉(zhuǎn)換。

在Java中,對(duì)象的序列化與反序列化被廣泛應(yīng)用到RMI(遠(yuǎn)程方法調(diào)用)及網(wǎng)絡(luò)傳輸中。

相關(guān)接口及類

Java為了方便開發(fā)人員將Java對(duì)象進(jìn)行序列化及反序列化提供了一套方便的API來支持。其中包括以下接口和類:

  • java.io.Serializable
  • java.io.Externalizable
  • ObjectOutput
  • ObjectInput
  • ObjectOutputStream
  • ObjectInputStream

Serializable 接口

類通過實(shí)現(xiàn) java.io.Serializable 接口以啟用其序列化功能。未實(shí)現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化??尚蛄谢惖乃凶宇愋捅旧矶际强尚蛄谢?。序列化接口沒有方法或字段,僅用于標(biāo)識(shí)可序列化的語義。

當(dāng)試圖對(duì)一個(gè)對(duì)象進(jìn)行序列化的時(shí)候,如果遇到不支持 Serializable 接口的對(duì)象。在此情況下,將拋出 NotSerializableException。

雖然Serializable接口中并沒有定義任何屬性和方法,但是如果一個(gè)類想要具備序列化能力也比必須要實(shí)現(xiàn)它。其實(shí),主要是因?yàn)樾蛄谢谡嬲膱?zhí)行過程中會(huì)使用instanceof判斷一個(gè)類是否實(shí)現(xiàn)類Serializable,如果未實(shí)現(xiàn)則直接拋出異常。關(guān)于這部分內(nèi)容,我會(huì)單開一篇文章講解。

如果要序列化的類有父類,要想同時(shí)將在父類中定義過的變量持久化下來,那么父類也應(yīng)該集成java.io.Serializable接口。

下面是一個(gè)實(shí)現(xiàn)了java.io.Serializable接口的類

  1. package com.hollischaung.serialization.SerializableDemos; 
  2. import java.io.Serializable
  3. /** 
  4. * Created by hollis on 16/2/17. 
  5. * 實(shí)現(xiàn)Serializable接口 
  6. */ 
  7. public class User1 implements Serializable { 
  8.    private String name
  9.    private int age; 
  10.    public String getName() { 
  11.        return name
  12.    } 
  13.    public void setName(String name) { 
  14.        this.name = name
  15.    } 
  16.    public int getAge() { 
  17.        return age; 
  18.    } 
  19.    public void setAge(int age) { 
  20.        this.age = age; 
  21.    } 
  22.    @Override 
  23.    public String toString() { 
  24.        return "User{" + 
  25.                "name='" + name + '\'' + 
  26.                ", age=" + age + 
  27.                '}'
  28.    } 

通過下面的代碼進(jìn)行序列化及反序列化

  1. package com.hollischaung.serialization.SerializableDemos; 
  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. /** 
  11. * Created by hollis on 16/2/17. 
  12. * SerializableDemo1 結(jié)合SerializableDemo2說明 一個(gè)類要想被序列化必須實(shí)現(xiàn)Serializable接口 
  13. */ 
  14. public class SerializableDemo1 { 
  15.  
  16.    public static void main(String[] args) { 
  17.        //Initializes The Object 
  18.        User1 user = new User1(); 
  19.        user.setName("hollis"); 
  20.        user.setAge(23); 
  21.        System.out.println(user); 
  22.  
  23.        //Write Obj to File 
  24.        try (FileOutputStream fos = new FileOutputStream("tempFile"); ObjectOutputStream oos = new ObjectOutputStream( 
  25.            fos)) { 
  26.            oos.writeObject(user); 
  27.        } catch (IOException e) { 
  28.            e.printStackTrace(); 
  29.        } 
  30.  
  31.        //Read Obj from File 
  32.        File file = new File("tempFile"); 
  33.        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) { 
  34.            User1 newUser = (User1)ois.readObject(); 
  35.            System.out.println(newUser); 
  36.        } catch (IOException | ClassNotFoundException e) { 
  37.            e.printStackTrace(); 
  38.        } 
  39.    } 
  40.  
  41. //OutPut
  42. //User{name='hollis', age=23} 
  43. //User{name='hollis', age=23} 

如果你觀察夠細(xì)微的話,你可能會(huì)發(fā)現(xiàn),我在上面的測(cè)試代碼中使用了IO流,但是我并沒有顯示的關(guān)閉他。這其實(shí)是Java 7中的新特性try-with-resources。這其實(shí)是Java中的一個(gè)語法糖,背后原理其實(shí)是編譯器幫我們做了關(guān)閉IO流的工作。后面我會(huì)單獨(dú)出一篇文章介紹下如何使用語法糖提高代碼質(zhì)量。

上面的代碼中,我們將代碼中定義出來的User對(duì)象通過序列化的方式保存到文件中,然后再從文件中將他到序列化成Java對(duì)象。結(jié)果是我們的對(duì)象的屬性均被持久化了下來。

Externalizable接口

除了Serializable 之外,java中還提供了另一個(gè)序列化接口Externalizable

為了了解Externalizable接口和Serializable接口的區(qū)別,先來看代碼,我們把上面的代碼改成使用Externalizable的形式。

  1. package com.hollischaung.serialization.ExternalizableDemos; 
  2.  
  3. import java.io.Externalizable; 
  4. import java.io.IOException; 
  5. import java.io.ObjectInput; 
  6. import java.io.ObjectOutput; 
  7.  
  8. /** 
  9. * Created by hollis on 16/2/17. 
  10. * 實(shí)現(xiàn)Externalizable接口 
  11. */ 
  12. public class User1 implements Externalizable { 
  13.  
  14.    private String name
  15.    private int age; 
  16.  
  17.    public String getName() { 
  18.        return name
  19.    } 
  20.    public void setName(String name) { 
  21.        this.name = name
  22.    } 
  23.    public int getAge() { 
  24.        return age; 
  25.    } 
  26.    public void setAge(int age) { 
  27.        this.age = age; 
  28.    } 
  29.    public void writeExternal(ObjectOutput out) throws IOException { 
  30.  
  31.    } 
  32.    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 
  33.  
  34.    } 
  35.    @Override 
  36.    public String toString() { 
  37.        return "User{" + 
  38.                "name='" + name + '\'' + 
  39.                ", age=" + age + 
  40.                '}'
  41.    } 
  1. package com.hollischaung.serialization.ExternalizableDemos; 
  2.  
  3. import java.io.*; 
  4.  
  5. /** 
  6. * Created by hollis on 16/2/17. 
  7. * 對(duì)一個(gè)實(shí)現(xiàn)了Externalizable接口的類進(jìn)行序列化及反序列化 
  8. */ 
  9. public class ExternalizableDemo1 { 
  10.  
  11.   public static void main(String[] args) { 
  12.       //Write Obj to file 
  13.       User1 user = new User1(); 
  14.       user.setName("hollis"); 
  15.       user.setAge(23); 
  16.       try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"))){ 
  17.           oos.writeObject(user); 
  18.       } catch (IOException e) { 
  19.           e.printStackTrace(); 
  20.       } 
  21.  
  22.       //Read Obj from file 
  23.       File file = new File("tempFile"); 
  24.       try(ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file))){ 
  25.           User1 newInstance = (User1) ois.readObject(); 
  26.           //output 
  27.           System.out.println(newInstance); 
  28.       } catch (IOException | ClassNotFoundException e ) { 
  29.           e.printStackTrace(); 
  30.       } 
  31.   } 
  32. //OutPut
  33. //User{name='null', age=0} 

通過上面的實(shí)例的輸出結(jié)果可以發(fā)現(xiàn),對(duì)User1類進(jìn)行序列化及反序列化之后得到的對(duì)象的所有屬性的值都變成了默認(rèn)值。也就是說,之前的那個(gè)對(duì)象的狀態(tài)并沒有被持久化下來。這就是Externalizable接口和Serializable接口的區(qū)別:

Externalizable繼承了Serializable,該接口中定義了兩個(gè)抽象方法:writeExternal()與readExternal()。當(dāng)使用Externalizable接口來進(jìn)行序列化與反序列化的時(shí)候需要開發(fā)人員重寫writeExternal()與readExternal()方法。

由于上面的代碼中,并沒有在這兩個(gè)方法中定義序列化實(shí)現(xiàn)細(xì)節(jié),所以輸出的內(nèi)容為空。還有一點(diǎn)值得注意:在使用Externalizable進(jìn)行序列化的時(shí)候,在讀取對(duì)象時(shí),會(huì)調(diào)用被序列化類的無參構(gòu)造器去創(chuàng)建一個(gè)新的對(duì)象,然后再將被保存對(duì)象的字段的值分別填充到新對(duì)象中。所以,實(shí)現(xiàn)Externalizable接口的類必須要提供一個(gè)public的無參的構(gòu)造器。

如果實(shí)現(xiàn)了Externalizable接口的類中沒有無參數(shù)的構(gòu)造函數(shù),在運(yùn)行時(shí)會(huì)拋出異常:java.io.InvalidClassException。如果一個(gè)Java類沒有定義任何構(gòu)造函數(shù),編譯器會(huì)幫我們自動(dòng)添加一個(gè)無參的構(gòu)造方法,可是,如果我們?cè)陬愔卸x了一個(gè)有參數(shù)的構(gòu)造方法了,編譯器便不會(huì)再幫我們創(chuàng)建無參構(gòu)造方法,這點(diǎn)需要注意。

按照要求修改之后代碼如下:

  1. package com.hollischaung.serialization.ExternalizableDemos; 
  2.  
  3. import java.io.Externalizable; 
  4. import java.io.IOException; 
  5. import java.io.ObjectInput; 
  6. import java.io.ObjectOutput; 
  7.  
  8. /** 
  9. * Created by hollis on 16/2/17. 
  10. * 實(shí)現(xiàn)Externalizable接口,并實(shí)現(xiàn)writeExternal和readExternal方法 
  11. */ 
  12. public class User2 implements Externalizable { 
  13.  
  14.    private String name
  15.    private int age; 
  16.  
  17.    public String getName() { 
  18.        return name
  19.    } 
  20.    public void setName(String name) { 
  21.        this.name = name
  22.    } 
  23.    public int getAge() { 
  24.        return age; 
  25.    } 
  26.    public void setAge(int age) { 
  27.        this.age = age; 
  28.    } 
  29.    public void writeExternal(ObjectOutput out) throws IOException { 
  30.        out.writeObject(name); 
  31.        out.writeInt(age); 
  32.    } 
  33.    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 
  34.        name = (String) in.readObject(); 
  35.        age = in.readInt(); 
  36.    } 
  37.  
  38.    @Override 
  39.    public String toString() { 
  40.        return "User{" + 
  41.                "name='" + name + '\'' + 
  42.                ", age=" + age + 
  43.                '}'
  44.    } 

再執(zhí)行測(cè)試得到以下結(jié)果

  1. //OutPut
  2. //User{name='hollis', age=23} 

這次,就可以把之前的對(duì)象狀態(tài)持久化下來了。

ObjectOutput和ObjectInput 接口

上面的writeExternal方法和readExternal方法分別接收ObjectOutput和ObjectInput類型參數(shù)。這兩個(gè)類作用如下。

ObjectInput 擴(kuò)展自 DataInput 接口以包含對(duì)象的讀操作。

DataInput 接口用于從二進(jìn)制流中讀取字節(jié),并根據(jù)所有 Java 基本類型數(shù)據(jù)進(jìn)行重構(gòu)。同時(shí)還提供根據(jù) UTF-8 修改版格式的數(shù)據(jù)重構(gòu) String 的工具。

對(duì)于此接口中的所有數(shù)據(jù)讀取例程來說,如果在讀取所需字節(jié)數(shù)之前已經(jīng)到達(dá)文件末尾 (end of file),則將拋出 EOFException(IOException 的一種)。如果因?yàn)榈竭_(dá)文件末尾以外的其他原因無法讀取字節(jié),則將拋出 IOException 而不是 EOFException。尤其是,在輸入流已關(guān)閉的情況下,將拋出 IOException。

ObjectOutput 擴(kuò)展 DataOutput 接口以包含對(duì)象的寫入操作。

DataOutput 接口用于將數(shù)據(jù)從任意 Java 基本類型轉(zhuǎn)換為一系列字節(jié),并將這些字節(jié)寫入二進(jìn)制流。同時(shí)還提供了一個(gè)將 String 轉(zhuǎn)換成 UTF-8 修改版格式并寫入所得到的系列字節(jié)的工具。

對(duì)于此接口中寫入字節(jié)的所有方法,如果由于某種原因無法寫入某個(gè)字節(jié),則拋出 IOException。

ObjectOutputStream、ObjectInputStream類

通過前面的代碼片段中我們也能知道,我們一般使用ObjectOutputStream的writeObject方法把一個(gè)對(duì)象進(jìn)行持久化。再使用ObjectInputStream的readObject從持久化存儲(chǔ)中把對(duì)象讀取出來。

更多關(guān)于ObjectInputStream和ObjectOutputStream的相關(guān)知識(shí),我會(huì)單獨(dú)有一篇文章介紹,敬請(qǐng)期待。

transient 關(guān)鍵字

transient 關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient 變量的值被設(shè)為初始值,如 int 型的是 0,對(duì)象型的是 null。關(guān)于transient 關(guān)鍵字的拓展同樣下一篇文章介紹。

序列化ID

虛擬機(jī)是否允許反序列化,不僅取決于類路徑和功能代碼是否一致,一個(gè)非常重要的一點(diǎn)是兩個(gè)類的序列化 ID 是否一致(就是 private static final long serialVersionUID)

序列化 ID 在 Eclipse 下提供了兩種生成策略,一個(gè)是固定的 1L,一個(gè)是隨機(jī)生成一個(gè)不重復(fù)的 long 類型數(shù)據(jù)(實(shí)際上是使用 JDK 工具生成),在這里有一個(gè)建議,如果沒有特殊需求,就是用默認(rèn)的 1L 就可以,這樣可以確保代碼一致時(shí)反序列化成功。那么隨機(jī)生成的序列化 ID 有什么作用呢,有些時(shí)候,通過改變序列化 ID 可以用來限制某些用戶的使用。

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2011-06-01 15:05:02

序列化反序列化

2012-04-13 10:45:59

XML

2009-06-14 22:01:27

Java對(duì)象序列化反序列化

2022-08-06 08:41:18

序列化反序列化Hessian

2023-12-13 13:49:52

Python序列化模塊

2009-08-24 17:14:08

C#序列化

2009-09-09 14:45:41

XML序列化和反序列化

2009-09-09 15:47:27

XML序列化和反序列化

2009-08-06 11:16:25

C#序列化和反序列化

2011-05-18 15:20:13

XML

2010-03-19 15:54:21

Java Socket

2019-11-20 10:07:23

web安全PHP序列化反序列化

2009-08-25 14:24:36

C#序列化和反序列化

2011-06-01 14:50:48

2016-09-21 00:15:27

2021-11-18 07:39:41

Json 序列化Vue

2016-01-05 15:10:59

2023-11-20 08:44:18

數(shù)據(jù)序列化反序列化

2009-09-09 16:10:11

.NET序列化和反序列

2009-08-25 14:43:26

C#序列化和反序列化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)