发布于2026-05-20 阅读(0)
扫一扫,手机访问
在分布式系统、缓存以及跨应用数据交换的场景中,我们经常需要将对象“打包”发送或“封存”起来。这背后依赖的核心技术,就是序列化与反序列化。

简单来说,序列化就是把一个活生生的对象,转换成一串连续的字节流。这个过程就好比把一件复杂的家具拆解、打包成一个规整的包裹,目的是为了方便存储到文件或数据库,或者通过网络“寄送”到另一个系统。
反序列化自然是序列化的逆过程。当系统收到这个“包裹”或者从存储介质中读取到这串字节流时,需要按照原来的图纸,把零件重新组装成完整的家具对象。这个过程就是反序列化,它让字节流重新获得生命,变回可操作的程序对象。
整个流程可以清晰地分为三步走:
第一步:对象状态转换为字节流
第二步:传输或存储字节流
第三步:将字节流转为对象(反序列化)
正是这套机制,实现了对象跨越时间(持久化)和空间(网络传输)的转移。虽然这里以Ja va为例,但序列化的思想在众多编程语言和框架中都有体现,只是实现方式各有千秋。
Ja va语言内置了一套完整的序列化机制,让对象转换变得非常直接。其核心是Serializable接口和一对流类:ObjectOutputStream与ObjectInputStream。
想让一个类的对象能被序列化,第一步就是让它实现Serializable接口。这个接口没有任何方法,是一个纯粹的“标记接口”,它的作用就是告诉JVM:“我这个类的对象可以被序列化。”
示例:
import ja va.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 省略getters和setters
}
解释: 上面的Person类实现了Serializable接口。这意味着,这个类的所有实例字段(除非被特殊标记)在序列化时都会被自动转换成字节流。
ObjectOutputStream:负责将对象“写出去”,即执行序列化操作。ObjectInputStream:负责将字节流“读进来”,即执行反序列化操作。示例:序列化对象
import ja va.io.FileOutputStream;
import ja va.io.ObjectOutputStream;
public class SerializeExample {
public static void main(String[] args) {
Person person = new Person("Alice", 30);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person); // 将Person对象写入文件
System.out.println("对象已序列化");
} catch (Exception e) {
e.printStackTrace();
}
}
}
解释:
ObjectOutputStream,并将其链接到一个文件输出流。writeObject方法,person对象的状态便被序列化并写入person.ser文件中。示例:反序列化对象
import ja va.io.FileInputStream;
import ja va.io.ObjectInputStream;
public class DeserializeExample {
public static void main(String[] args) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
Person person = (Person) ois.readObject(); // 从文件中读取对象
System.out.println("对象已反序列化: " + person.getName() + ", " + person.getAge());
} catch (Exception e) {
e.printStackTrace();
}
}
}
解释:
ObjectInputStream来读取之前保存的文件。readObject方法,字节流被重新解析,还原成一个Person对象。序列化会包含所有非静态字段。但如果有些字段(比如密码、密钥)不希望被持久化或传输,该怎么办?这时就需要transient关键字出场了。用它修饰的字段,会在序列化过程中被自动忽略。
示例:
public class User implements Serializable {
private String username;
private transient String password; // 不会被序列化
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
在这个例子中,即使用户对象被序列化,password字段也不会被写入字节流,从而在存储或传输时保护了敏感信息。
对于更复杂的控制需求,Ja va提供了Externalizable接口。它继承自Serializable,但要求开发者自己动手,完全掌控序列化和反序列化的每一个细节。实现它需要重写两个方法:
writeExternal(ObjectOutput out):定义如何将对象写入字节流。readExternal(ObjectInput in):定义如何从字节流中读取并重建对象。示例:
import ja va.io.Externalizable;
import ja va.io.ObjectOutput;
import ja va.io.ObjectInput;
public class Book implements Externalizable {
private String title;
private double price;
public Book() {} // 必须有无参构造方法
public Book(String title, double price) {
this.title = title;
this.price = price;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(title);
out.writeDouble(price);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
title = in.readUTF();
price = in.readDouble();
}
}
解释:
writeExternal方法中,我们明确指定了将title和price字段以何种顺序、何种格式写入流中。readExternal方法中,我们按照写入的顺序和格式,再将数据读取出来,赋值给对象的字段。总的来说,Ja va内置的序列化机制为开发者提供了从便捷到灵活的不同选择。Serializable搭配ObjectOutputStream/ObjectInputStream足以应对大多数常规需求。而当需要对过程进行精细控制时,Externalizable接口则提供了强大的自定义能力。
尽管Ja va原生序列化用起来方便,但在构建高性能、跨平台、高安全的现代分布式系统时,它的几个固有缺陷就会变得非常突出。
性能较差:
不跨语言:
存在安全漏洞:
缺乏版本控制:
serialVersionUID来缓解,但在复杂的项目迭代中,维护版本兼容性依然是个头疼的问题。正因为这些局限性,在实际生产环境中,尤其是对性能、安全性和跨语言能力有要求的场景下,开发者往往会转向更优秀的替代方案。下面就来聊聊两个主流选择:FastJson和Protobuf。
FastJson是阿里巴巴开源的高性能JSON处理库。它的核心是将Ja va对象与JSON格式相互转换,而JSON是一种几乎无处不在的、人类可读的数据交换格式。
FastJson的优势:
跨平台:
速度快,性能优异:
支持灵活的数据结构:
安全性更高:
FastJson使用示例:
import com.alibaba.fastjson.JSON;
public class FastJsonExample {
public static void main(String[] args) {
// 序列化
Person person = new Person("Alice", 25);
String jsonString = JSON.toJSONString(person);
System.out.println("序列化为JSON字符串:" + jsonString);
// 反序列化
Person deserializedPerson = JSON.parseObject(jsonString, Person.class);
System.out.println("反序列化对象:" + deserializedPerson.getName());
}
}
Protobuf(Protocol Buffers)是Google推出的一种语言中立、平台无关、可扩展的序列化机制。它追求的是极致的效率和紧凑性。
Protobuf的优势:
高效的二进制格式:
跨语言:
.proto文件,Protobuf编译器可以生成Ja va、C++、Python、Go等多种语言的代码。不同语言编写的服务可以使用同一份数据定义进行无缝通信。支持严格的版本控制:
安全性强:
Protobuf使用示例: 使用Protobuf首先需要定义一个.proto文件来描述数据结构:
// person.proto
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
}
然后,使用Protobuf编译器生成对应的Ja va类,之后就可以像下面这样使用:
Person person = Person.newBuilder().setName("Alice").setAge(25).build();
byte[] data = person.toByteArray(); // 序列化
Person deserializedPerson = Person.parseFrom(data); // 反序列化
简单来说,FastJson以其通用性和可读性见长,非常适合前后端交互和需要日志调试的场景。而Protobuf则以绝对的性能和跨语言能力取胜,是微服务、分布式存储等对效率和带宽敏感场景的首选。两者都有效地弥补了Ja va原生序列化的短板。
序列化与反序列化是构建可交互、可扩展软件系统的基石技术。它们让对象得以跨越进程、网络甚至语言的边界,是实现数据持久化和远程通信的关键。
Ja va通过Serializable接口和ObjectOutputStream/ObjectInputStream这一套组合拳,提供了开箱即用的序列化支持。其流程直观:对象变字节流,存储或传输;字节流再变回对象,恢复状态。这套方案在单一的Ja va环境中简单快捷。
然而,一旦进入生产环境,面对性能、安全、跨语言的需求,原生序列化的局限性便暴露无遗:
因此,它更适用于简单的、临时的、信任边界内的数据存储场景。
为了应对更复杂的挑战,业界广泛采用了更先进的序列化方案:
技术选型没有银弹,关键在于理解不同序列化工具的特性,并根据实际应用场景在性能、可读性、安全性和开发成本之间做出平衡。选择合适的序列化方案,能显著提升系统的整体健壮性和效率。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8