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

記得好久之前寫代碼的時候,每次新建一個實體都會下意識的繼承Serializable接口,大部分人都知道這是對對象的序列化,但是大家真的知道序列化嗎?這篇文章就簡單的說下java中的序列化,讓你更多的理解java這門語言。前端

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

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

將數據對象轉換爲二進制流的過程就稱爲對象的序列化(Serialization),反過來,將二進制流轉換爲對象就是反序列化(Deserializable)。序列化的用處是什麼呢?共兩點:後端

一、數據持久化:在不少應用中,須要對好多對象進行序列化,存到物理硬盤,較長時間的保存,好比,Session對象,當有數萬用戶併發訪問的時候,就會有數萬的Session對象,內存會承受很大的壓力,這個時候,就會把一些對象先序列化到硬盤中,須要使用的時候再還原到內存中。序列化對象要保留充分的信息,用來恢復數據對象,可是爲了節約存儲空間和網絡帶寬,序列化出的二進制流要儘量小。安全

二、網絡傳輸:當兩個進程在互相通訊的時候,就會進行數據傳輸,不論是何種類型的數據,都必需要轉成二進制流來傳輸,接受方收到後再轉爲數據對象。bash

重點來了,序列化在代碼中是怎麼實現的呢?如下介紹三種:服務器

一、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方法將屬性賦值爲對應類型的初始值。

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

二、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;
    }
複製代碼

三、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關鍵字來修飾這個屬性,這樣在反序列化以後該屬性就會爲空,若是必定要傳遞的話,可使用對稱加密或非對稱加密獨立傳輸,在數據傳輸的問題上,咱們必定要具有安全意識。

但願看完這篇文章的你能有所收穫。

分享如下本身的思考:「對於用戶來講,系統太靈活是他們的負擔,意味着他們要作更多的選擇,對於技術人員來講,架構靈活能夠應對更多的運營策略。怎樣折中,是個問題。」

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

關注公衆號,最新文章會出如今那裏哦

相關文章
相關標籤/搜索