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

Java 序列化與反序列化實戰:原生 JDK、XML (XStream) 及避坑指南

     編輯:Java 相關知識

為什麼需要序列化?

  • 持久化:對象 → 字節序列 → 存磁盤/數據庫/Redis。
  • 網絡傳輸:RPC、分布式 Session、消息隊列體內對象跨 JVM 傳遞。
  • 深拷貝:序列化再反序列化獲得完全獨立的對象圖副本。

方式一:原生 JDK 序列化 (java.io.Serializable)

@SuppressWarnings("serial")
class Person implements Serializable {
    private String name; private int age; private transient String pwd; // transient 不序列化
    // 構造器、getter/setter、toString
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        // 可加密敏感字段再寫入
    }
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        // 解密還原
    }
}

public class JdkSerialDemo {
    public static void main(String[] args) throws Exception {
        Person p = new Person("Jim", 28, "secret123");
        
        // 序列化到文件
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(p);
        }
        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person p1 = (Person) ois.readObject();
            System.out.println(p1); // pwd 為 null (transient)
        }
    }
}

關鍵注意點

  • serialVersionUID:強烈建議顯式聲明,否則類結構變更導致反序列化報 InvalidClassException。
  • transient:標記不序列化字段(密碼、臨時狀態、非串行化第三方對象)。
  • 自定義序列化:writeObject/readObject、writeReplace/readResolve(單例保護)、Externalizable 完全手動控制。
  • 安全漏洞:反序列化任意類可導致 RCE(Apache Commons Collections 鏈),**嚴禁反序列化不可信數據**,或使用 ObjectInputFilter (JDK 9+) 白名單過濾。
  • 性能/體積:JDK 序列化元數據多、體積大、速度慢,**高性能場景請用 Protobuf、Kryo、Hessian、FST**。

方式二:XML 序列化 (XStream)

人類可讀、跨語言、版本兼容性好,適合配置文件、對外接口、日誌審計。

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.20</version>
</dependency>

public class XStreamDemo {
    public static void main(String[] args) throws Exception {
        // 1. 配置安全框架 (防 CVE-2021-21347 等)
        XStream xs = new XStream(new DomDriver());
        xs.allowTypes(new Class[]{RoadInfo.class, TableList.class}); // 白名單
        // xs.allowTypesByWildcard(new String[]{"com.myapp.**"}); // 通配符
        
        // 2. 別名簡化 XML
        xs.alias("road", RoadInfo.class);
        xs.alias("roads", TableList.class);
        
        // 3. 反序列化 XML 文件
        String xml = Files.readString(Path.of("roads.xml"));
        TableList list = (TableList) xs.fromXML(xml);
        System.out.println(list);
    }
}

@XStreamAlias("road")
class RoadInfo { int id; long mdn; String name; double lng, lat; String icon; }
@XStreamAlias("roads")
class TableList { List<RoadInfo> roads = new ArrayList<>(); }

XStream 避坑

  • 安全第一:必須設置白名單 allowTypes,禁止反序列化任意類。
  • 字段映射:默認字段名做元素名,用 @XStreamAlias、@XStreamAsAttribute、@XStreamImplicit 控制結構。
  • 日期/集合:註冊轉換器 xs.registerConverter(new JodaDateTimeConverter())。
  • 性能:DOM 解析全量載入內存,大文件用 StAX/SAX 或切換 JSON。

方式三:JSON 序列化 (Jackson/Gson) —— 現代首選

ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String json = mapper.writeValueAsString(obj);
User u = mapper.readValue(json, User.class);

優勢:體積小、速度快、生態完善、支持流式處理、Spring Boot 默認集成。

選型決策表

場景推薦方案理由
Java 內部 RPC、Session 複製Kryo / FST / Protobuf極致性能、體積小、支持循環引用
異構系統接口、配置文件、審計日誌JSON (Jackson)通用、可讀、工具鏈豐富
老系統對接、需 XML 格式XStream (白名單)XML 標準、注解驅動
簡單對象持久化、教學演示JDK Serializable (UID)零依賴、語言內建

總結

序列化看似簡單,實則暗藏版本兼容、安全漏洞、性能陷阱。原則:**首選 JSON/Protobuf,慎用 JDK 原生,禁用不可信反序列化,顯式 serialVersionUID,敏感字段 transient 加密**。

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