面試問你java中的序列化怎麼答?

邊緣煩惱發表於2019-03-26

記得很久以前寫程式碼的時候,每次新建一個實體都會下意識的繼承Serializable介面,大部分人都知道這是對物件的序列化,可是你們真的知道序列化嗎?這篇文章就簡單的說下java中的序列化,讓你更多的理解java這門語言。

面試問你java中的序列化怎麼答?

關於上篇文章說的,在應用登入前使用第三方的人機驗證,如果第三方的產品突然出現故障,無法使用,這種狀況我們應該怎麼應對,在團隊中我們也討論過這種情況,我們的方案就是客戶端不直接的請求第三方,而是由後端伺服器充當一箇中介的角色,起轉發作用,這樣在第三方出現問題,我們伺服器端會做處理,這也是不把雞蛋放在同一個籃子裡的思想。

接下來,簡單的說下序列化,

將資料物件轉換為二進位制流的過程就稱為物件的序列化(Serialization),反過來,將二進位制流轉換為物件就是反序列化(Deserializable)。序列化的用處是什麼呢?共兩點:

1、資料持久化:在很多應用中,需要對好多物件進行序列化,存到物理硬碟,較長時間的儲存,比如,Session物件,當有數萬使用者併發訪問的時候,就會有數萬的Session物件,記憶體會承受很大的壓力,這個時候,就會把一些物件先序列化到硬碟中,需要使用的時候再還原到記憶體中。序列化物件要保留充分的資訊,用來恢復資料物件,但是為了節約儲存空間和網路頻寬,序列化出的二進位制流要儘可能小。

2、網路傳輸:當兩個程式在互相通訊的時候,就會進行資料傳輸,不管是何種型別的資料,都必須要轉成二進位制流來傳輸,接受方收到後再轉為資料物件。

重點來了,序列化在程式碼中是怎麼實現的呢?以下介紹三種:

1、java原生序列化:java類通過實現Serializable介面來實現。這個介面沒有任何方法,只是標識,java序列化保留了物件的後設資料,以及物件資料,相容性最好,但是不支援跨語言,效能也一般。

public class BaseEntity implements Serializable {
    private static final long serialVersionUID = -7333816285916354999L;
    private Long id;
    public BaseEntity() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
}
複製程式碼

實現這個介面,idea會給你一個警告,它會建議你設定一個serialVersionUID,如果你不設定,編譯器會根據類的內部實現,包括類名、介面名、方法和屬性來自動生成serialVersionUID 如果類的原始碼有修改,重新編譯後這個值也會變化。

在修改類的時候,我們要根據相容性來決定是否修改serialVersionUID,如果是相容性質的升級,不建議修改,因為可能會反序列化失敗。如果是不相容的,就需要修改,避免反序列化變得混亂。

java原生序列化,在反序列化的時候不會呼叫類的無參構造方法,而是呼叫native方法將屬性賦值為對應型別的初始值。

最後,基於效能及相容性,不推薦使用。

2、Hessian序列化:Hessian序列化是一種支援動態型別、跨語言、基於物件傳輸的網路協議,java物件序列化後的二進位制流,可以被其他語言反序列化。它的特性:

自描述序列化型別,不依賴外部描述檔案或介面定義,用一個位元組表示常用的基礎型別,極大縮短二進位制流;

語言無關,支援指令碼語言。

協議簡單,比java原生的要高效。

需要注意一點:Hessian會把複雜物件所有屬性儲存在一個Map中進行序列化,所以在父類和子類含有相同欄位的情況下,先序列化子類,後序列化父類,這樣的結果是子類的同名屬性會被父類覆蓋掉。

以下是程式碼實現

/**
     * Hessian實現序列化
     *
     * @param TestClass
     * @return
     * @throws IOException
     */
    private static byte[] serialize(TestClass TestClass) {
        ByteArrayOutputStream byteArrayOutputStream = null;
        HessianOutput hessianOutput = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            // Hessian的序列化
            hessianOutput = new HessianOutput(byteArrayOutputStream);
            hessianOutput.writeObject(TestClass);
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                byteArrayOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                hessianOutput.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
複製程式碼

3、JSON序列化:

JSON是一種輕量級的資料交換格式,這種序列化方式就是講資料物件轉換為JSON字串。在序列化的過程中捨棄了型別資訊。反序列化是隻有在提供了型別資訊的情況下才能完成。

  public static <T> T fromJson(String json, Class<T> type) {
        if (json != null && !json.equals("")) {
            try {
                return getObjectMapper().readValue(json, type);
            } catch (Exception var3) {
                var3.printStackTrace();
                return null;
            }
        } else {
            return null;
        }
    }
複製程式碼

相信大部分讀者的公司和前端和客戶端資料的互動格式都是JSON吧,因為JSON的這種格式可讀性較好,而且也方便除錯。

序列化通常會用於網路傳輸資料物件,而物件中常常會含有敏感資料,所以黑客常常會攻擊這點,攻擊手段通常是利用反序列化過程構造惡意程式碼,怎麼應對這種情況呢?可以使用transient關鍵字來修飾這個屬性,這樣在反序列化之後該屬性就會為空,如果一定要傳遞的話,可以使用對稱加密或非對稱加密獨立傳輸,在資料傳輸的問題上,我們一定要具備安全意識。

希望看完這篇文章的你能有所收穫。

分享以下自己的思考:“對於使用者來說,系統太靈活是他們的負擔,意味著他們要做更多的選擇,對於技術人員來說,架構靈活可以應對更多的運營策略。怎樣折中,是個問題。”

這樣的分享我會一直持續,你的關注、轉發和點贊是對我最大的支援,感謝。

關注公眾號,最新文章會出現在那裡哦

面試問你java中的序列化怎麼答?

相關文章