用遊戲來講序列化與反序列化機制

  • 2022 年 6 月 12 日
  • 筆記

一、寫在最前

想像一下,你剛開始玩一個遊戲,一進遊戲,首先系統提示你需要創建一個角色。

新建角色對象:姓名(宇宙第一無敵厲害)誰會取這樣的名字哈哈哈、性別、門派、等級……

建完角色之後,來到新手村,對於一個心懷大俠夢的有志青年當然不能被困在這小小的新手村了,所以你需要去刷boss攢經驗升級。

boss對象:血量、等級、擁有技能……

就這樣,經過不懈努力,你終於達到了能出新手村的等級。這時候,夜深了,你媽氣急敗壞叫你關電腦睡覺。大家都知道,創建的對象是保存在記憶體中的,記憶體中的數據斷電即失。難道剛要出新手村,大俠夢就破滅了?怎麼辦呢?

二、大俠夢持續輸出——序列化

為了不讓無數青年的大俠夢關機即碎,序列化技術出現了,在遊戲中的體現就是存檔。即,在退出遊戲的時候,點擊存檔按鈕,這時候,你遊戲角色的等級,裝備等都會被保存在存儲媒體中(硬碟等)。這時候,就算你關機了,下次再次登錄遊戲,還是你關機時保存的角色狀態。

總結:序列化就是將記憶體中的對象數據存儲到存儲媒體中。

三、小爺我又回來了——反序列化

當天晚上,你在你媽的壓迫下無奈關機睡覺了。第二天起來你迫不及待打開遊戲準備再次大幹一場,可是遊戲角色已經被序列化到硬碟中,怎麼取出來呢?這時候,反序列化技術就出現了。就是把你存在硬碟上的數據還原成原來的遊戲對象,該是多少級還是多少級,該是啥裝備就是啥裝備。

總結:反序列化就是將存儲在媒體中的對象還原成記憶體中對象。

序列化與反序列化擴展:

對於序列化來講,不單單是作為將記憶體中的數據存儲到媒體中,其網路傳輸也是一大作用。

你想想,玩遊戲當然是大家一起玩才好了。每個遊戲玩家在遊戲世界中都可以進行溝通交流,當你刷boss的時候,爆到了一把一刀999的屠龍寶刀。這時候,你可以選擇高價賣給遊戲的其他土豪玩家。這時候,你不可能直接通過網線把這個給人家,而是通過序列化的方法,把這把屠龍刀傳輸給買家,買家在接收到這個數據之後,再通過反序列化將這把寶刀還原。

即,序列化的作用:

存儲記憶體中的對象、進行網路傳輸。

四、序列化機制探究

上文說到,序列化可以進行網路傳輸,說到網路傳輸,那麼I/O就不能陌生了,I/O傳輸支援的數據格式是位元組數組。即,要想進行網路傳輸,就是將傳輸的對象轉換成位元組數組。但是,我們不能盲目隨便轉換成位元組數組,因為你後面還需要將對象進行還原。也就是說,在進行轉換的時候你要指定一個規則(序列化),在將對象轉換成位元組數組的時候按照這個規則進行轉換。那個將對象進行還原的時候,按照規則(反序列化)進行還原。你不能將一個人偶拆了之後,裝的時候,把手裝成腳。

即,序列化機制:

將對象轉換成位元組數組的時候的轉換規則,這種轉換規則就是序列化機制。

五、序列化的種類

原生的JDK

JDK自帶了序列化方法,只需要實現了Serializable介面的類,就可以通過ObjectOutputStream類將對象變成byte[]位元組數組。

弊端:只能java自己玩。

程式碼示例:

/**
 * @ClassName Student
 * @Description Student類對象
 * @Author LH
 * @Date 2022/6/12 10:32
 */
@Data
public class Student implements Serializable {
    private String id;
    private String name;
    private int age;
}
/**
 * @ClassName JDKSerialize
 * @Description JDK序列化介面測試
 * @Author LH
 * @Date 2022/6/12 10:41
 */
public class JDKSerialize {

    /**
     *  對象序列化
     * @param  student 學生對象
     * @throws IOException 拋出io異常
     */
    public static void serializeStudent(Student student) throws IOException {
        ObjectOutputStream oo = new ObjectOutputStream(new ObjectOutputStream(Files.newOutputStream(new File("E:/java/workplace/study/student.txt").toPath())));
        oo.writeObject(student);
        oo.close();
    }

    /**
     * 對象反序列化
     * @param path 反序列化文件路徑
     * @return Student 還原的學生對象
     * @throws IOException 拋出io異常
     * @throws ClassNotFoundException 找不到文件異常
     */
    public static Student deSerializeStudent(String path) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new ObjectInputStream(Files.newInputStream(new File(path).toPath())));
        Student student = (Student) ois.readObject();
        ois.close();
        return student;
    }

    public static void main(String[] args) {
        // 創建一個student對象
        Student student = new Student();
        student.setId("1");
        student.setName("LH");
        student.setAge(17);

        try {
            // 序列化student對象
            JDKSerialize.serializeStudent(student);
            // student對象飯序列化
            String path = "E:/java/workplace/study/student.txt";
            Student deSerializeStudent = JDKSerialize.deSerializeStudent(path);
            System.out.println("student id:" + deSerializeStudent.getId() + "student age:" + deSerializeStudent.getAge() + "student name:" + deSerializeStudent.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ProtoBuf

Google推出,一種語言無關、平台無關、可擴展的序列化結構數據的方法,可用於通訊協議、數據存儲等。序列化後體積小,一般用於對傳輸性能有較高要求的系統。

了解不是很多,後面工作用到的話再來進行擴展。

JSON

在系統之間進行交互的時候很常用。

JSON 序列化方式生成的是一串有規則的字元串,可讀性強。是有規則的字元串,不跟任何程式語言綁定,天然上就具備了跨平台。

弊端:體積大。

常見的工具有:

fastJSONJacksonGson 等。工作中比較常用的就是阿里的fastJSON,使用起來也很簡單,直接調用轉換的api即可,了解更多可以去官網看看。

各種序列化手段都有不同的特性,看你的業務再去選型。

序列化如何選擇

主要考慮三點:

  • 選擇的序列化手段是否需要跨平台

  • 選擇的序列化手段的速度要求

  • 選擇的序列化手段的體積要求