天天看點

帶你認識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、當一個對象的執行個體變量引用其他對象,序列化該對象時也把引用對象進行序列化。