什麼是對象序列化?

「對象序列化」是什麼意思? 你能用一些例子解釋一下嗎? php


#1樓

序列化是將對象保存在存儲介質(例如文件或內存緩衝區)中或以二進制形式經過網絡鏈接傳輸對象的過程。 序列化的對象獨立於JVM,而且能夠由任何JVM從新序列化。 在這種狀況下,「內存中」 java對象狀態將轉換爲字節流。 用戶沒法理解這種類型的文件。 它是一種特殊類型的對象,即被JVM(Java虛擬機)重用。 序列化對象的過程也稱爲縮小或編組對象。 html

要序列化的對象必須實現java.io.Serializable接口。 對象的默認序列化機制將寫入對象的類,類簽名以及全部非瞬態和非靜態字段的值。 java

class ObjectOutputStream extends java.io.OutputStream implements ObjectOutput,

ObjectOutput接口擴展了DataOutput接口,並添加了用於序列化對象並將字節寫入文件的方法。 ObjectOutputStream擴展java.io.OutputStream並實現ObjectOutput接口。 它將對象,數組和其餘值序列化爲流。 所以, ObjectOutputStream的構造函數寫爲: 算法

ObjectOutput ObjOut = new ObjectOutputStream(new FileOutputStream(f));

上面的代碼已用於經過ObjectOutputStream( )構造函數建立ObjectOutput類的實例,該構造函數將FileOuputStream的實例做爲參數。 數據庫

經過實現ObjectOutputStream類使用ObjectOutput接口。 ObjectOutputStream構造爲序列化對象。 數組

在Java中反序列化對象 服務器

序列化的相反操做稱爲反序列化,即從一系列字節中提取數據被稱爲反序列化,也稱爲膨脹或解組。 網絡

ObjectInputStream擴展java.io.InputStream並實現ObjectInput接口。 它從輸入流中反序列化對象,數組和其餘值。 所以, ObjectInputStream的構造函數寫爲: 併發

ObjectInputStream obj = new ObjectInputStream(new FileInputStream(f));

程序的上述代碼建立ObjectInputStream類的實例,以反序列化由ObjectInputStream類序列化的文件。 上面的代碼使用FileInputStream類的實例建立實例,該實例保存指定的文件對象,該對象必須反序列化,由於ObjectInputStream()構造函數須要輸入流。 oracle


#2樓

Java 對象序列化

在此處輸入圖片說明

Serialization是一種將Java對象圖轉換爲字節數組以便存儲( to disk file )或across a network傳輸( across a network )的機制,而後經過反序列化,咱們能夠恢復對象圖。 使用引用共享機制能夠正確還原對象圖。 可是在存儲以前,請檢查輸入文件/網絡中的serialVersionUID和.class文件serialVersionUID是否相同。 若是不是,則拋出java.io.InvalidClassException

每一個版本化的類都必須標識其可以寫入流並能夠從中讀取的原始類版本。 例如,版本化類必須聲明:

serialVersionUID語法

// ANY-ACCESS-MODIFIER static final long serialVersionUID = (64-bit has)L; private static final long serialVersionUID = 3487495895819393L;

serialVersionUID對序列化過程相當重要。 可是對於開發人員來講,將其添加到java源文件中是可選的。 若是不包括serialVersionUID,則序列化運行時將生成serialVersionUID並將其與類相關聯。 序列化的對象將包含此serialVersionUID以及其餘數據。

-強烈建議全部可序列化的類顯式聲明一個serialVersionUID, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations ,所以可能致使反序列化期間意外的serialVersionUID衝突,從而致使反序列化失敗。

檢查可序列化的類

在此處輸入圖片說明


Java對象只能序列化。 若是一個類或其任何超類實現了java.io.Serializable接口或其子接口java.io.Externalizable

  • 爲了成功序列化其對象,類必須實現java.io.Serializable接口 。 可序列化是一個標記接口,用於通知編譯器必須添加實現該類的可序列化行爲。 Java虛擬機(JVM)負責其自動序列化。

    瞬態關鍵字: java.io.Serializable interface

    在序列化對象時,若是咱們不但願序列化對象的某些數據成員,則可使用暫態修飾符。 瞬態關鍵字將防止該數據成員被序列化。

    • 聲明爲瞬態或靜態的字段將被序列化過程忽略。

    瞬態揮發性

    +--------------+--------+-------------------------------------+ | Flag Name | Value | Interpretation | +--------------+--------+-------------------------------------+ | ACC_VOLATILE | 0x0040 | Declared volatile; cannot be cached.| +--------------+--------+-------------------------------------+ |ACC_TRANSIENT | 0x0080 | Declared transient; not written or | | | | read by a persistent object manager.| +--------------+--------+-------------------------------------+
    class Employee implements Serializable { private static final long serialVersionUID = 2L; static int id; int eno; String name; transient String password; // Using transient keyword means its not going to be Serialized. }
  • 實現Externalizable接口可使對象承擔對對象序列化表格的內容和格式的徹底控制。 調用Externalizable接口的方法writeExternal和readExternal來保存和恢復對象狀態。 當由類實現時,它們可使用ObjectOutput和ObjectInput的全部方法來寫入和讀取本身的狀態。 對象負責處理髮生的任何版本控制。

    class Emp implements Externalizable { int eno; String name; transient String password; // No use of transient, we need to take care of write and read. @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(eno); out.writeUTF(name); //out.writeUTF(password); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.eno = in.readInt(); this.name = in.readUTF(); //this.password = in.readUTF(); // java.io.EOFException } }
  • 只有支持java.io.Serializable或java.io.Externalizable接口的對象才能written to read from流中read from 。 每一個可序列化對象的類都通過編碼,包括類名稱和類簽名,對象的字段和數組的值以及從初始對象引用的任何其餘對象的關閉。

文件的可序列化示例

public class SerializationDemo {
    static String fileName = "D:/serializable_file.ser";

    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        Employee emp = new Employee( );
        Employee.id = 1; // Can not Serialize Class data.
        emp.eno = 77;
        emp.name = "Yash";
        emp.password = "confidential";
        objects_WriteRead(emp, fileName);

        Emp e = new Emp( );
        e.eno = 77;
        e.name = "Yash";
        e.password = "confidential";
        objects_WriteRead_External(e, fileName);

        /*String stubHost = "127.0.0.1";
        Integer anyFreePort = 7777;
        socketRead(anyFreePort); //Thread1
        socketWrite(emp, stubHost, anyFreePort); //Thread2*/

    }
    public static void objects_WriteRead( Employee obj, String serFilename ) throws IOException{
        FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );
        objectOut.writeObject( obj );
        objectOut.close();
        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            FileInputStream fis = new FileInputStream( new File( serFilename ) );
            ObjectInputStream ois = new ObjectInputStream( fis );
            Object readObject;
            readObject = ois.readObject();
            String calssName = readObject.getClass().getName();
            System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException

            Employee emp = (Employee) readObject;
            System.out.format("Obj[No:%s, Name:%s, Pass:%s]", emp.eno, emp.name, emp.password);

            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static void objects_WriteRead_External( Emp obj, String serFilename ) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File( serFilename ));
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        obj.writeExternal( objectOut );
        objectOut.flush();

        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            // create a new instance and read the assign the contents from stream.
            Emp emp = new Emp();

            FileInputStream fis = new FileInputStream(new File( serFilename ));
            ObjectInputStream ois = new ObjectInputStream( fis );

            emp.readExternal(ois);

            System.out.format("Obj[No:%s, Name:%s, Pass:%s]", emp.eno, emp.name, emp.password);

            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

網絡上的可序列化示例

將對象的狀態分佈在不一樣的地址空間中,或者在同一臺計算機上的不一樣進程中,甚至在經過網絡鏈接的多臺計算機中,均可以經過共享數據和調用方法來協同工做。

/**
 * Creates a stream socket and connects it to the specified port number on the named host. 
 */
public static void socketWrite(Employee objectToSend, String stubHost, Integer anyFreePort) {
    try { // CLIENT - Stub[marshalling]
        Socket client = new Socket(stubHost, anyFreePort);
        ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
        out.writeObject(objectToSend);
        out.flush();
        client.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
// Creates a server socket, bound to the specified port. 
public static void socketRead(  Integer anyFreePort ) {
    try { // SERVER - Stub[unmarshalling ]
        ServerSocket serverSocket = new ServerSocket( anyFreePort );
        System.out.println("Server serves on port and waiting for a client to communicate");
            /*System.in.read();
            System.in.read();*/

        Socket socket = serverSocket.accept();
        System.out.println("Client request to communicate on port server accepts it.");

        ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
        Employee objectReceived = (Employee) in.readObject();
        System.out.println("Server Obj : "+ objectReceived.name );

        socket.close();
        serverSocket.close();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}

@看到


#3樓

勇於回答6歲的問題,對Java初學者來講只是一個很是高級的瞭解

什麼是序列化?

將對象轉換爲字節,而後將字節轉換回對象(反序列化)。

何時使用序列化?

當咱們想要持久化對象時。 當咱們但願對象存在於JVM的生存期以後。

真實示例:

ATM:當賬戶持有人嘗試經過ATM從服務器提取資金時,賬戶持有人信息(例如提款詳細信息)將被序列化併發送到服務器,在服務器上將詳細信息反序列化並用於執行操做。

如何在Java中執行序列化。

  1. 實現java.io.Serializable接口(標記接口,所以沒有實現方法)。

  2. 持久化對象:使用java.io.ObjectOutputStream類,它是一個過濾器流,它是較低層字節流的包裝(將Object寫入文件系統或在網絡上傳輸扁平化對象並在另外一側重建)。

    • writeObject(<<instance>>) -寫入對象
    • readObject() -讀取序列化的對象

記得:

序列化對象時,將僅保存對象的狀態,而不保存對象的類文件或方法。

當序列化一個2字節的對象時,您會看到51字節的序列化文件。

步驟如何對對象進行序列化和反序列化。

答案:它是如何轉換爲51字節文件的?

  • 首先寫入序列化流魔術數據(STREAM_MAGIC =「 AC ED」,STREAM_VERSION = JVM的版本)。
  • 而後,它寫出與實例關聯的類的元數據(類的長度,類的名稱,serialVersionUID)。
  • 而後,它遞歸地寫出超類的元數據,直到找到java.lang.Object爲止。
  • 而後從與實例關聯的實際數據開始。
  • 最後,將與實例相關聯的對象的數據從元數據開始寫入實際內容。

若是您對有關Java序列化的部門信息感興趣,請檢查此連接

編輯 :一個更好的閱讀連接

這將回答一些常見問題:

  1. 如何不序列化類中的任何字段。
    答:使用瞬態關鍵字

  2. 當子類被序列化時,父類會被序列化嗎?
    回答:否,若是父級未擴展Serializable interface parent字段不會被序列化。

  3. 父級序列化後,子類會序列化嗎?
    回答:是的,默認狀況下,子類也被序列化。

  4. 如何避免子類被序列化?
    答: 重寫writeObject和readObject方法,並拋出NotSerializableException

    b。 您也能夠在子類中將全部字段標記爲瞬態。

  5. 某些系統級類(例如Thread,OutputStream及其子類和Socket)不可序列化。

#4樓

將文件做爲對象返回: http : //www.tutorialspoint.com/java/java_serialization.htm

import java.io.*;

        public class SerializeDemo
        {
           public static void main(String [] args)
           {
              Employee e = new Employee();
              e.name = "Reyan Ali";
              e.address = "Phokka Kuan, Ambehta Peer";
              e.SSN = 11122333;
              e.number = 101;

              try
              {
                 FileOutputStream fileOut =
                 new FileOutputStream("/tmp/employee.ser");
                 ObjectOutputStream out = new ObjectOutputStream(fileOut);
                 out.writeObject(e);
                 out.close();
                 fileOut.close();
                 System.out.printf("Serialized data is saved in /tmp/employee.ser");
              }catch(IOException i)
              {
                  i.printStackTrace();
              }
           }
        }

    import java.io.*;
    public class DeserializeDemo
    {
       public static void main(String [] args)
       {
          Employee e = null;
          try
          {
             FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
             ObjectInputStream in = new ObjectInputStream(fileIn);
             e = (Employee) in.readObject();
             in.close();
             fileIn.close();
          }catch(IOException i)
          {
             i.printStackTrace();
             return;
          }catch(ClassNotFoundException c)
          {
             System.out.println("Employee class not found");
             c.printStackTrace();
             return;
          }
          System.out.println("Deserialized Employee...");
          System.out.println("Name: " + e.name);
          System.out.println("Address: " + e.address);
          System.out.println("SSN: " + e.SSN);
          System.out.println("Number: " + e.number);
        }
    }

#5樓

我本身博客中的兩分錢:

這是序列化的詳細說明 :(我本身的博客)

序列化:

序列化是持久化對象狀態的過程。 它以字節序列的形式表示和存儲。 能夠將其存儲在文件中。 從文件讀取對象狀態並還原它的過程稱爲反序列化。

序列化有什麼須要?

在現代體系結構中,始終須要存儲對象狀態而後再檢索它。 例如在Hibernate中,要存儲對象,咱們應該使類Serializable。 它的做用是,一旦對象狀態以字節形式保存,就能夠將其轉移到另外一個系統,該系統能夠從狀態中讀取並檢索類。 對象狀態能夠來自數據庫或其餘jvm,也能夠來自單獨的組件。 藉助序列化,咱們能夠檢索對象狀態。

代碼示例和說明:

首先讓咱們看一下Item類:

public class Item implements Serializable{

    /**
    *  This is the Serializable class
    */
    private static final long serialVersionUID = 475918891428093041L;
    private Long itemId;
    private String itemName;
    private transient Double itemCostPrice;
    public Item(Long itemId, String itemName, Double itemCostPrice) {
        super();
        this.itemId = itemId;
        this.itemName = itemName;
        this.itemCostPrice = itemCostPrice;
      }

      public Long getItemId() {
          return itemId;
      }

     @Override
      public String toString() {
          return "Item [itemId=" + itemId + ", itemName=" + itemName + ", itemCostPrice=" + itemCostPrice + "]";
       }


       public void setItemId(Long itemId) {
           this.itemId = itemId;
       }

       public String getItemName() {
           return itemName;
       }
       public void setItemName(String itemName) {
            this.itemName = itemName;
        }

       public Double getItemCostPrice() {
            return itemCostPrice;
        }

        public void setItemCostPrice(Double itemCostPrice) {
             this.itemCostPrice = itemCostPrice;
        }
}

在上面的代碼中,能夠看到Item類實現了Serializable

這是使類可序列化的接口。

如今咱們能夠看到一個名爲serialVersionUID的變量被初始化爲Long變量。 該數字由編譯器根據類的狀態和類屬性來計算。 當jvm從文件中讀取對象狀態時,該數字將幫助jvm識別對象狀態。

爲此,咱們能夠看一下正式的Oracle文檔:

序列化運行時與每一個可序列化的類關聯一個版本號,稱爲serialVersionUID,在反序列化期間使用該版本號來驗證序列化對象的發送者和接收者是否已加載了該對象的與序列化兼容的類。 若是接收方已爲該對象加載了一個與相應發送方類具備不一樣的serialVersionUID的類,則反序列化將致使InvalidClassException。 可序列化的類能夠經過聲明一個名爲「 serialVersionUID」的字段來顯式聲明其本身的serialVersionUID,該字段必須是靜態的,最終的且類型爲long:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 若是可序列化的類未明確聲明serialVersionUID,則序列化運行時將根據該類的各個方面爲該類計算默認的serialVersionUID值,如Java(TM)對象序列化規範中所述。 可是,強烈建議全部可序列化的類顯式聲明serialVersionUID值,由於默認的serialVersionUID計算對類詳細信息高度敏感,而類詳細信息可能會根據編譯器的實現而有所不一樣,所以可能在反序列化期間致使意外的InvalidClassExceptions。 所以,爲了保證不一樣Java編譯器實現之間的serialVersionUID值一致,可序列化的類必須聲明一個顯式的serialVersionUID值。 還強烈建議顯式serialVersionUID聲明在可能的狀況下使用private修飾符,由於此類聲明僅適用於當即聲明的類-serialVersionUID字段做爲繼承成員沒有用。

若是您發現有另外一個關鍵字咱們使用過,則它是瞬時的

若是字段不可序列化,則必須將其標記爲瞬態。 在這裏,咱們將itemCostPrice標記爲瞬態,而且不但願將其寫入文件中

如今讓咱們看一下如何在文件中寫入對象的狀態,而後從那裏讀取它。

public class SerializationExample {

    public static void main(String[] args){
        serialize();
       deserialize();
    } 

    public static void serialize(){

         Item item = new Item(1L,"Pen", 12.55);
         System.out.println("Before Serialization" + item);

         FileOutputStream fileOut;
         try {
             fileOut = new FileOutputStream("/tmp/item.ser");
             ObjectOutputStream out = new ObjectOutputStream(fileOut);
             out.writeObject(item);
             out.close();
             fileOut.close();
             System.out.println("Serialized data is saved in /tmp/item.ser");
           } catch (FileNotFoundException e) {

                  e.printStackTrace();
           } catch (IOException e) {

                  e.printStackTrace();
           }
      }

    public static void deserialize(){
        Item item;

        try {
                FileInputStream fileIn = new FileInputStream("/tmp/item.ser");
                ObjectInputStream in = new ObjectInputStream(fileIn);
                item = (Item) in.readObject();
                System.out.println("Serialized data is read from /tmp/item.ser");
                System.out.println("After Deserialization" + item);
        } catch (FileNotFoundException e) {
                e.printStackTrace();
        } catch (IOException e) {
               e.printStackTrace();
        } catch (ClassNotFoundException e) {
               e.printStackTrace();
        }
     }
}

在上面,咱們能夠看到對象的序列化和反序列化的示例。

爲此,咱們使用了兩個類。 爲了序列化對象,咱們使用了ObjectOutputStream。 咱們使用了writeObject方法將對象寫入文件中。

對於反序列化,咱們使用了ObjectInputStream,它從文件中的對象讀取。 它使用readObject從文件中讀取對象數據。

上面代碼的輸出以下:

Before SerializationItem [itemId=1, itemName=Pen, itemCostPrice=12.55]
Serialized data is saved in /tmp/item.ser
After DeserializationItem [itemId=1, itemName=Pen, itemCostPrice=null]

請注意,反序列化對象中的itemCostPricenull,由於它沒有被寫入。

在本文的第一部分中,咱們已經討論了Java序列化的基礎知識。

如今,讓咱們深刻討論它及其工做原理。

首先讓咱們從serialversionuid開始

serialVersionUID用做Serializable類中的版本控件。

若是未明確聲明serialVersionUID,則JVM將根據Serializable類的各類屬性爲您自動進行聲明。

Java的計算serialversionuid的算法 (在此處閱讀更多詳細信息)

  1. 類名。
    1. 類修飾符寫爲32位整數。
    2. 每一個接口的名稱按名稱排序。
    3. 對於按字段名稱排序的類的每一個字段(私有靜態字段和私有瞬態字段除外:字段名稱。字段的修飾符以32位整數形式編寫。字段的描述符。
    4. 若是存在類初始化器,則寫出如下內容:方法的名稱。
    5. 方法的修飾符java.lang.reflect.Modifier.STATIC,用32位整數表示。
    6. 方法的描述符()V。
    7. 對於每一個按方法名稱和簽名排序的非私有構造函數:方法名稱。 方法的修飾符,寫爲32位整數。 方法的描述符。
    8. 對於按方法名稱和簽名排序的每一個非私有方法:方法的名稱。 方法的修飾符,寫爲32位整數。 方法的描述符。
    9. SHA-1算法在DataOutputStream產生的字節流上執行,併產生五個32位值sha [0..4]。 哈希值由SHA-1消息摘要的第一和第二個32位值組成。 若是消息摘要的結果(五個32位字H0 H1 H2 H3 H4)位於五個名爲sha的int值的數組中,則哈希值的計算方式以下:
long hash = ((sha[0] >>> 24) & 0xFF) |
>            ((sha[0] >>> 16) & 0xFF) << 8 |
>            ((sha[0] >>> 8) & 0xFF) << 16 |
>            ((sha[0] >>> 0) & 0xFF) << 24 |
>            ((sha[1] >>> 24) & 0xFF) << 32 |
>            ((sha[1] >>> 16) & 0xFF) << 40 |
>            ((sha[1] >>> 8) & 0xFF) << 48 |
>        ((sha[1] >>> 0) & 0xFF) << 56;

Java的序列化算法

序列化對象的算法以下所述:
1.它寫出與實例關聯的類的元數據。
2.它遞歸地寫出超類的描述,直到找到java.lang.object爲止。
3.一旦完成元數據信息的寫入,便從與實例關聯的實際數據開始。 可是這一次,它是從最高級的超類開始的。
4.它從最小超類到最大派生類遞歸地寫入與實例關聯的數據。

注意事項:

  1. 類中的靜態字段沒法序列化。

    public class A implements Serializable{ String s; static String staticString = "I won't be serializable"; }
  2. 若是serialversionuid在讀取的類中不一樣,則將拋出InvalidClassException異常。

  3. 若是一個類實現可序列化,則其全部子類也將可序列化。

    public class A implements Serializable {....}; public class B extends A{...} //also Serializable
  4. 若是一個類具備另外一個類的引用,則全部引用都必須是可序列化的,不然將不執行序列化過程。 在這種狀況下, NotSerializableException在運行時引起。

例如:

public class B{
     String s,
     A a; // class A needs to be serializable i.e. it must implement Serializable
}
相關文章
相關標籤/搜索