首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 與 C# 二進制序列化/反序列化實戰:跨語言數據持久化與傳輸

     編輯:Java 相關知識

前言

二進制序列化將對象轉換為字節流,用於文件持久化、網絡傳輸、進程間通信。Java 和 C# 均提供原生二進制序列化機制。本文通過完整代碼示例,演示雙語言的序列化/反序列化操作,並探討跨語言互操作的關鍵點與替代方案。

一、Java 原生二進制序列化

核心 API

  • java.io.Serializable 標記接口:類實現該接口(無方法),聲明可序列化。
  • ObjectOutputStream.writeObject(obj):將對象圖寫入字節流。
  • ObjectInputStream.readObject():從字節流重建對象圖。
  • serialVersionUID:顯式聲明版本號,防止類結構變更導致 InvalidClassException。

實戰代碼:文件複製(字節流)與對象序列化

import java.io.*;

public class JavaBinarySerializationDemo {

    // 1. 簡單文件字節流複製(非對象序列化,僅演示流操作)
    public static void copyFile(String src, String dest) throws IOException {
        try (FileInputStream in = new FileInputStream(src);
             FileOutputStream out = new FileOutputStream(dest);
             BufferedInputStream bin = new BufferedInputStream(in);
             BufferedOutputStream bout = new BufferedOutputStream(out)) {

            byte[] buffer = new byte[8192];
            int len;
            while ((len = bin.read(buffer)) != -1) {
                bout.write(buffer, 0, len);
            }
            bout.flush();
            System.out.println("文件複製完成:" + dest);
        }
    }

    // 2. 對象序列化到文件
    public static void serializeObject(Serializable obj, String filePath) throws IOException {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {
            oos.writeObject(obj);
            System.out.println("對象序列化成功:" + filePath);
        }
    }

    // 3. 文件反序列化為對象
    public static Object deserializeObject(String filePath) throws IOException, ClassNotFoundException {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {
            Object obj = ois.readObject();
            System.out.println("對象反序列化成功");
            return obj;
        }
    }

    // 測試實體類
    static class User implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;
        private int age;
        private transient String password; // transient 不參與序列化

        public User(String name, int age, String password) {
            this.name = name; this.age = age; this.password = password;
        }
        @Override public String toString() {
            return "User{name='" + name + "', age=" + age + ", password='" + password + "'}";
        }
    }

    public static void main(String[] args) throws Exception {
        // 文件複製測試
        copyFile("src/main/resources/logo.gif", "target/logo_copy.gif");

        // 對象序列化測試
        User user = new User("Alice", 25, "secret123");
        String userFile = "target/user.dat";
        serializeObject(user, userFile);

        User restored = (User) deserializeObject(userFile);
        System.out.println("還原對象:" + restored); // password 為 null(transient)
    }
}

二、C# 原生二進制序列化

核心 API

  • [Serializable] 特性:標記類可序列化。
  • System.Runtime.Serialization.Formatters.Binary.BinaryFormatter:核心序列化器(注意:.NET Core 3.0+ 標記為過時,不推薦新項目使用)。
  • Serialize(Stream, object) / Deserialize(Stream):流式讀寫。

實戰代碼

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class PlayManager
{
    public string PlayerName { get; set; }
    public int Score { get; set; }
    // [NonSerialized] private string _token; // 不序列化字段

    public void Save(string filePath)
    {
        try
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Create))
            {
                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(fs, this);
            }
            Console.WriteLine("C# 序列化保存成功:" + filePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine("序列化失敗:" + ex.Message);
        }
    }

    public static PlayManager Load(string filePath)
    {
        try
        {
            using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
            {
                BinaryFormatter bf = new BinaryFormatter();
                return (PlayManager)bf.Deserialize(fs);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("反序列化失敗:" + ex.Message);
            return null;
        }
    }
}

// 測試
class Program
{
    static void Main()
    {
        var mgr = new PlayManager { PlayerName = "Bob", Score = 999 };
        string path = "playmanager.dat";
        mgr.Save(path);

        var loaded = PlayManager.Load(path);
        Console.WriteLine($"載入:Name={loaded.PlayerName}, Score={loaded.Score}");
    }
}

三、跨語言互操作:Java ↔ C# 二進制序列化的陷阱與替代方案

問題說明解決方案
格式不兼容 Java ObjectOutputStream 與 C# BinaryFormatter 使用各自專有二進制格式,無法直接互讀。 雙方協議統一採用跨語言序列化標準。
類型系統差異 基本類型大小、字符編碼、日期時間表示、集合結構均不同。 使用Schema 驅動的序列化框架,由 IDL 定義數據結構,生成雙語言代碼。
版本演進 字段增減、類型變更需向前/向後兼容。 選擇支持 Schema 演進的格式(Protobuf、Avro、Thrift、MessagePack)。
安全性 Java 反序列化漏洞(Gadget Chain)、C# BinaryFormatter 遠程代碼執行風險。 嚴禁反序列化不可信數據;遷移至安全框架,啟用類過濾器。

推薦跨語言序列化方案對比

方案特點適用場景Java 生態C# 生態
Protocol Buffers (Protobuf) Google 出品,二進制、Schema 優先、高性能、體積小、支持 gRPC 微服務 RPC、高吞吐內部通信、持久化 protobuf-java, protobuf-gradle-plugin Google.Protobuf, Grpc.Tools
Apache Avro JSON Schema、動態類型、壓縮、Hadoop 生態標準 大數據管道、Kafka 消息、數據湖存儲 avro, avro-maven-plugin Apache.Avro, Microsoft.Hadoop.Avro
MessagePack 「二進制 JSON」、無 Schema、自描述、極快 緩存、日誌、配置、輕量級 RPC msgpack-java MessagePack-CSharp
JSON (Jackson / System.Text.Json) 文本、可讀、通用、Web API 標準 對外 REST API、配置文件、異構系統集成 Jackson, Gson System.Text.Json, Newtonsoft.Json

四、最佳實踐總結

  1. 單語言內部持久化/緩存:Java 用 Serializable + serialVersionUID;C# 新項目避開 BinaryFormatter,改用 System.Text.Json、MessagePack 或 Protobuf。
  2. 跨語言/微服務通信:首選 Protobuf + gRPC,契約優先、代碼生成、強類型、高性能。
  3. 大數據/日誌/異步消息:Avro(Schema Registry 配合 Kafka)或 MessagePack。
  4. 安全第一:任何反序列化入口(HTTP、RPC、文件上傳)必須驗證來源、限制可反序列化類白名單、禁用危險 Gadget。
  5. 版本管理:序列化格式變更遵循「只增不減、字段編號不變、提供默認值」原則。

結語

Java 與 C# 原生二進制序列化雖然簡單易用,但僅限單語言內部使用。現代分佈式系統中,請拥抱跨語言、Schema 驅動、高性能、安全的序列化標準(Protobuf/Avro/MessagePack),從架構層面解決互操作與演進難題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved