Java 序列化機制

1、爲何要序列化?

一、通常狀況下,只有當 JVM 處於運行時,Java 對象纔可能存在,即這些對象的生命週期不會比 JVM 的生命週期更長。但在現實應用中,就可能要求在 JVM 中止運行以後可以保存(持久化)指定的對象,並在未來從新讀取被保存的對象。Java 對象序列化就可以幫助咱們實現該功能。java

二、在網絡或者進程通訊中傳遞對象時,咱們都須要使用序列化將 Java 對象轉換爲字節序列傳輸,具體表現爲:發送數據前序列化對象,接收數據後反序列化對象。網絡

2、序列化是什麼?

序列化指的是容許將實現序列化的 Java 對象轉換位字節序列,這些字節序列能夠保存在磁盤上,或經過網絡傳輸,以達到之後恢復成原來的對象。序列化機制使得對象能夠脫離程序的運行而獨立存在。ide

通俗易懂的講,Java 序列化是指把 Java 對象轉換爲字節序列的過程,而 Java 反序列化是指把字節序列恢復爲 Java 對象的過程。性能

3、Java 序列化機制

1.使用 Serializable 接口實現序列化

在 Java 中, 只要一個類實現了 java.io.Serializable 接口,那麼它就能夠被序列化。code

@Data
public class UserA implements Serializable {
    /**
     * 序列化ID
     */
    private static final long serialVersionUID = 1L;

    private int age;
    private static String name = "張三";

    @Override
    public String toString() {
        return "User{age=" + age + ",name=" + name + "}";
    }    
}

2.使用 Externalizable 接口實現序列化

Externalizable 繼承自 Serializable 接口,須要咱們重寫 writeExternal() 與 readExternal() 方法來決定要序列化哪些信息,而且必需要提供一個 public 的無參的構造器。對象

Externalizable 的性能要優於 Serializable,但也增長了複雜性。繼承

@Data
public class UserB implements Externalizable {
    /**
     * 序列化ID
     */
    private static final long serialVersionUID = 1L;

    private int age;
    private static String name = "李四";


    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        age = in.readInt();
    }

    @Override
    public String toString() {
        return "User{age=" + age + ",name=" + name + "}";
    }
}

3.序列化和反序列化

經過實現 Serializable 接口或者 Externalizable 接口,Java 對象已經具有序列化的資質了,那如何進行序列化和反序列化呢?這裏利用了 ObjectOutputStream 和 ObjectInputStream 對對象進行序列化及反序列化。接口

public static void main(String[] args) {
        // 序列化
        serialize();
        // 反序列化
        deserialize();
    }

    private static void serialize() {
        UserA user = new UserA();
        user.setAge(26);
        //序列化對象到文件中
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\userA"))) {
            objectOutputStream.writeObject(user);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void deserialize() {
        try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\userA"))) {
            UserA user = (UserA) objectInputStream.readObject();
            System.out.println(user);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

4、序列化分析

  1. serialVersionUID,即序列化ID。虛擬機可否進行反序列化,不只取決於類路徑和功能代碼是否一致,一個很是重要的一點是兩個類的序列化 ID 是否一致,不然反序列化時會拋出 InvalidClassException 異常。serialVersionUID 默認是 1L,能夠不顯示指定。
  2. 靜態變量不會被序列化。由於靜態變量存在於虛擬機方法區,屬於全局變量,因此在序列化或者反序列時,並不會保存靜態變量。
  3. transient 關鍵字。做用是控制變量的序列化,在變量聲明前加上該關鍵字,能夠阻止該變量被序列化到文件中,在被反序列化後,transient 變量的值被設爲初始值,如 int 型的是 0,對象型的是 null。
  4. 父子類的序列化。要想將父類對象也序列化,就須要讓父類也實現 Serializable(或 Externalizable) 接口。
  5. 引用類型成員變量的序列化。要想引用對象也序列化,就須要讓引用對象也實現 Serializable(或 Externalizable) 接口。
相關文章
相關標籤/搜索