首頁  >  文章  >  Java  >  Java序列化與反序列化是什麼? Java序列化與反序列化的實作方法

Java序列化與反序列化是什麼? Java序列化與反序列化的實作方法

不言
不言轉載
2018-10-13 14:55:573291瀏覽

這篇文章帶給大家的內容是關於Java序列化與反序列化是什麼? Java序列化與反序列化的實作方法,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

序列化和反序列化作為Java裡一個較為基礎的知識點,但我相信很多人能了解的也就是那麼幾句而已,甚至都不了解,如果再深究問一下Java如何實現序列化和反序列化的,就可能不知所措了!不知道怎麼說好,什麼是序列化、什麼是反序列化、什麼場景的時候才會用到等,面試官說:知不知道序列化和反序列化底層是如何實現的嗎?  以下我們來討論討論一些序列化與反序列化的東東。

首先要弄清楚概念上的東西,  什麼是序列化和反序列化:

Java 中是將Java物件轉換為位元組序列的過程,而Java反序列化是指將位元組序列還原為Java物件的過程。

序列化 :  物件序列化的主要功能就是在傳遞和保存物件的時候,保證物件的完整性和可傳遞性。序列化是把物件轉換為有序的位元組流,以便在網路上傳輸或保存在本地檔案中,序列化後的位元組流保存了Java物件的狀態以及相關描述資訊。序列化機制的核心作用就是物件狀態的保存與重建。

反序列化: 客戶端從檔案或網路中取得到序列化的物件位元組流之後,根據位元組流中所儲存的物件狀態及描述訊息,透過反序列化進行重建物件。

本質上講,序列化就是把實體對象狀態依照一定的格式寫入到有序位元組流,反序列化就是從有序位元組流重建對象,恢復對象狀態。類似於檔案壓縮與解壓縮的概念吧。

為什麼要序列化與反序列化: 

我們知道,當進程進行遠端通訊的時候,可以互相傳送各種類型的數據,包括文字、圖片、音訊、視訊等, 而這些資料都會以二進位序列的形式在網路上傳送。

所以, 那麼當兩個Java進程進行通訊時,能否實現進程間的物件傳送呢?答案是可以的,那麼如何做到呢?這就需要Java序列化與反序列化了

換種說法就是: 發送方需要把這個Java物件轉換為位元組序列,然後在網路上傳送;另一方面,接收方需要從字節序列中恢復出Java物件。

弄清楚 為什麼需要Java序列化和反序列化後,我們自然會想Java序列化的好處。

一是實現了資料的持久化,透過序列化可以把資料永久地保存到硬碟上(通常存放在檔案裡)。

二是,利用序列化實現遠端通信,也就是在網路上傳送物件的位元組序列。

總的來說可以歸納為以下幾點: 

(1)永久保存對象,保存物件的位元組序列到本機檔案或資料庫中; 
(2)透過序列化以位元組流的形式使物件在網路中進行傳遞和接收; 
(3)透過序列化在進程間傳遞物件;

序列化演算法一般會按步驟做如下事情:

(1)將物件實例相關的類別元資料輸出。 
(2)遞歸地輸出類別的超類別描述直到不再有超類別。 
(3)類別元資料完了以後,開始從最頂層的超類別開始輸出物件實例的實際資料值。 
(4)從上到下遞歸輸出實例的資料

那麼  Java中又該如何實現序列化與反序列化呢?

一: JDK類別庫中序列化與反序列化API  

(1)java.io.ObjectOutputStream:表示物件輸出流;

它的writeObject(Object obj)方法可以對參數指定的obj物件進行序列化,把得到的位元組序列寫到一個目標輸出流中;

(2)java.io.ObjectInputStream:表示物件輸入流;

它的readObject()方法來源輸入流中讀取字節序列,再把它們反序列化成為一個對象,並將其返回;

二:實現序列化的要求

只有實作了Serializable或Externalizable介面的類別的物件才能被序列化,否則拋出例外!

 三:實現Java物件序列化與反序列化的方法

假定一個User類,它的物件需要序列化,可以有以下三種方法:

(1)若User類別僅僅實現了Serializable接口,則可以按照以下方式進行序列化和反序列化

ObjectOutputStream採用預設的序列化方式,對User物件的非transient的實例變數進行序列化。 
ObjcetInputStream採用預設的反序列化方式,對User物件的非transient的實例變數進行反序列化。

(2)若User類別僅實作了Serializable接口,並且也定義了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),則採用以下方式進行序列化與反序列化。

ObjectOutputStream调用User对象的writeObject(ObjectOutputStream out)的方法进行序列化。 
ObjectInputStream会调用User对象的readObject(ObjectInputStream in)的方法进行反序列化。

(3)若User类实现了Externalnalizable接口,且User类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。

ObjectOutputStream调用User对象的writeExternal(ObjectOutput out))的方法进行序列化。 
ObjectInputStream会调用User对象的readExternal(ObjectInput in)的方法进行反序列化。

四 :JDK类库中序列化的步骤

1  . 创建一个对象输出流,可以包装一个其它类型的目标输出流,如文件输出流:

ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("D:\\os.out"));

2  . 通过对象输出流的writeObject()方法写对象:

os.writeObject(new User("zhangsan", "123456", "male"));

五  : JDK类库中反序列化的步骤

1  . 创建一个对象输入流,它可以包装一个其它类型输入流,如文件输入流:

ObjectInputStream ois= new ObjectInputStream(new FileInputStream("object.out"));

2  .通过对象输出流的readObject()方法读取对象:

User user = (User) ois.readObject();

为保证正确读取数据,完成反序列化,必须保证向对象输出流写对象的顺序与从对象输入流中读对象的顺序一致。

举个栗子:

public class SerialDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //序列化
        FileOutputStream outputos = new FileOutputStream("object.out");
        ObjectOutputStream oos = new ObjectOutputStream(outputos );
        User user1 = new User("zhangsan", "123456", "male");
        oos.writeObject(user1);
        oos.flush();
        oos.close();
        //反序列化
        FileInputStream inputos= new FileInputStream("object.out");
        ObjectInputStream ois = new ObjectInputStream(inputos);
        User user2 = (User) ois.readObject();
        System.out.println(user2.getUserName()+ " " + 
            user2.getPassword() + " " + user2.getSex());
        //反序列化的输出结果为:zhangsan123456 male
    }
}

public class User implements Serializable {
    private String userName;
    private String password;
    private String sex;
    //全参构造方法、get和set方法
}

  

相关的注意事项:

1、序列化时,只对对象的状态进行保存,而不管对象的方法;

2、当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;

3、当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

4、并非所有的对象都可以序列化,至于为什么不可以,有很多原因了,比如:

安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行RMI传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的;

资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分配,而且,也是没有必要这样实现;

5、声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。

6、序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。为它赋予明确的值。显式地定义serialVersionUID有两种用途:

在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;

在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

7、Java有很多基础类已经实现了serializable接口,比如String,Vector等。但是也有一些没有实现serializable接口的;

8、如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这是能用序列化解决深拷贝的重要原因;

以上是Java序列化與反序列化是什麼? Java序列化與反序列化的實作方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除