帶你認識JAVA的序列化Serializable接口

我們知道在JAVA類中,很多類都實現了Serializable類的方法,他的意思是將這個類在運行的時候進行序列化,這個接口類的註解是這麼寫的,

/ * @author  unascribed

 * @see java.io.ObjectOutputStream

 * @see java.io.ObjectInputStream

 * @see java.io.ObjectOutput

 * @see java.io.ObjectInput

 * @see java.io.Externalizable

 * @since   JDK1.1

 */

起源於JDK1.1版本,是屬於java.io類裏的接口。

1、那什麼是序列化?

對象的狀態有:

1.      創建階段(Created)

2.      應用階段(In Use)

3.      不可見階段(Invisible)

4.      不可達階段(Unreachable)

5.      收集階段(Collected)

6.      終結階段(Finalized)

7.      對象空間重分配階段(De-allocated)

那麼,如果一個對象在創建之後,如果我想把工程停下來,但是卻又想保留住這個對象的信息,以便下次使用,那麼怎麼辦呢?這個時候就是序列化Serializable起到作用的時候了,它把對象的狀態和信息轉換爲字節序列保存到磁盤上,然後在你想使用的時候,通過一些java類方法可以再次讀取到這個對象的信息和狀態,重新獲取該對象。那麼如果在保存的時候如果有其他對象的引用,那麼序列化過程中把其他對象的信息以遞歸的方式保存下來,整個保存的格式會是一個複雜的樹形,最後讀取也是以這個格式來獲取對象。


2、如何進行序列化?

如果我們想要序列化一個對象,首先要創建某些OutputStream(如FileOutputStream、ByteArrayOutputStream等,其實就是Serializable類注

解上的呢些類的方法),然後將這些OutputStream封裝在一個ObjectOutputStream中。這時候,只需要調用writeObject()方法就可以將對象序列化,

並將其發送給OutputStream(記住:對象的序列化是基於字節的,不能使用Reader和Writer等基於字符的層次結構)。而反序列的過程(即將一個序列還原成

爲一個對象),需要將一個InputStream(如FileInputstream、ByteArrayInputStream等)封裝在ObjectInputStream內,然後調用readObject()即可。

以下是代碼示例:

package com.sheepmu;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
 
public class MyTest implements Serializable
{
    private static final long serialVersionUID = 1L;
    private String name="SheepMu";
    private int age=24;
    public static void main(String[] args)
    {//以下代碼實現序列化
        try
        {
		//輸出流保存的文件名爲 my.out,ObjectOutputStream能把Object輸出成Byte流
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("my.out"));
            MyTest myTest=new MyTest();
            oos.writeObject(myTest); 
            oos.flush();  //緩衝流 
            oos.close(); //關閉流
        } catch (FileNotFoundException e) 
        {        
            e.printStackTrace();
        } catch (IOException e) 
        {
            e.printStackTrace();
        } 
        fan();//調用下面的  反序列化  代碼
    }
    public static void fan()//反序列的過程
    {    
         ObjectInputStream oin = null;//局部變量必須要初始化
        try
        {
            oin = new ObjectInputStream(new FileInputStream("my.out"));
        } catch (FileNotFoundException e1)
        {        
            e1.printStackTrace();
        } catch (IOException e1)
        {
            e1.printStackTrace();
        }      
        MyTest mts = null;
        try {
            mts = (MyTest ) oin.readObject();//由Object對象向下轉型爲MyTest對象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }     
         System.out.println("name="+mts.name);    
         System.out.println("age="+mts.age);    
    }
}



會在此項目的工作空間生成一個 my.out文件。序列化後的內容稍後補齊,先看反序列化後輸出如下:


name=SheepMu
    age=24


3:序列化ID

在很多時候,發現model類中有呢麼一個字段:


private static final long serialVersionUID = xxxxxxxxxxxxxxl;


可以很明顯的看出來,這描述的是一個爲long型的序列化ID,那麼這個序列化ID是用來幹什麼的呢?


因爲序列化的左右就是用來反序列化的,那麼一個已經序列化的文件,在反序列化的時候,我如何知道這段時間中這個對象類是否有變化呢?假如我刪了字段,其實如果將這個對象再反序列化回來是錯誤的,那麼如何標記序列化對象和反序列化的時候的對象是否是一致的呢?就是用的這個序列化ID了,其實就相當於對這個對象hash出來了一個long的數值而已,這就是我的理解,如果這兩個ID不一致,在反序列化的時候是會報錯的。4:序列化的注意事項:

1、靜態類是無法被序列化的。序列化的是對象的狀態不是類的狀態,靜態成員屬於類級別的,序列化會忽略靜態變量,即序列化不保存靜態變量的狀態。

2、transient是一個瞬時狀態,所以也是無法被序列化的。

3、當一個父類實現序列化,子類自動實現序列化,不需要顯式實現Serializable接口。

4、當一個對象的實例變量引用其他對象,序列化該對象時也把引用對象進行序列化。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章