一.为什么要序列化?
序列化为数据持久化、远程通信、数据交换和对象复制提供了便捷的解决方案。它是在不同系统间共享和传递对象的一种常用方法。
几个常见原因:
持久化数据: 将对象序列化后存储在文件系统或数据库中,使数据在应用关闭或重启后仍然存在。这种持久化允许数据在不同的时间和地点之间持久保存。
网络传输: 序列化的字节流可在网络中传输。在分布式系统中,对象序列化后可以在不同的节点间传递,支持远程通信和数据交换。
跨平台通信: 通过序列化,可以使数据跨不同的平台和编程语言进行交互。例如,在使用JSON、XML或Protocol Buffers时,不同系统能够解析和操作序列化的数据。
对象克隆: 通过序列化和反序列化,可以快速克隆一个对象,特别是在需要对象副本而不想手动复制对象时非常有用。
缓存和缓存共享: 序列化允许对象缓存到持久存储或共享缓存中,以便将来快速获取,减少重复对象创建和初始化的开销。
二.有哪些实现方式?各自的使用场景是哪些?
1.Serializable接口:
通过实现java.io.Serializable接口,对象可以被序列化。这是Java内置的序列化机制,可以使用ObjectOutputStream进行对象序列化,ObjectInputStream进行反序列化。
示例代码:
import java.io.*;
//类实现Serializable接口
public class MyClass implements Serializable {
public static final long serialVersionUID =1L;
private int id;
private String name;
// get,set,构造方法略
}
// 序列化示例
MyClass obj = new MyClass();
try {
FileOutputStream fileOut = new FileOutputStream("file.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(obj);
out.close();
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化示例
MyClass obj;
try {
FileInputStream fileIn = new FileInputStream("file.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
obj = (MyClass) in.readObject();
in.close();
fileIn.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
2.Externalizable接口:
另一个接口java.io.Externalizable允许你自定义序列化过程。这需要实现writeExternal()和readExternal()方法来手动管理对象的序列化和反序列化过程。
import java.io.*;
public class MyClass implements Externalizable {
private int id;
private String name;
// get,set,构造方法略
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(id);
out.writeObject(name);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
id = in.readInt();
name = (String) in.readObject();
}
}
// 序列化和反序列类似Serializable
3.JSON序列化:
使用第三方库如Jackson、Gson、Fastjson等,将对象转换为JSON格式进行序列化和反序列化。这种方式不依赖Java的内置序列化机制,提供了更灵活的序列化和反序列化过程。
import com.fasterxml.jackson.databind.ObjectMapper;
public class MyClass {
private int id;
private String name;
// get,set,构造方法略
}
// 序列化
MyClass obj = new MyClass();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(obj);
// 反序列化
MyClass obj = mapper.readValue(json, MyClass.class);
4.XML序列化:
同样可以使用第三方库,比如JAXB(Java Architecture for XML Binding)来实现将对象序列化为XML格式,并进行反序列化。
import javax.xml.bind.*;
class MyClass {
private int id;
private String name;
// get,set,构造方法略
}
// 反序列化
MyClass obj = new MyClass();
JAXBContext context = JAXBContext.newInstance(MyClass.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(obj, new File("output.xml"));
// 序列化
MyClass obj = (MyClass) context.createUnmarshaller().unmarshal(new File("output.xml"));
5.Protocol Buffers:
由Google开发的一种序列化框架,使用.proto文件定义数据结构,提供了高效的序列化和反序列化。
需要通过.proto文件定义数据结构,并使用相应的生成工具生成Java类。例子较为复杂,需要编写.proto文件,并通过Protocol Buffers工具生成Java类。
6.Kryo和Hessian:
第三方库如Kryo和Hessian提供了替代Java默认序列化机制的选择,它们通常更快速、更紧凑。
Kryo和Hessian:这些库的使用方式类似于Serializable接口,不同之处在于引入相应的库类,例如使用Kryo库。
三.各自的优势和不足
Serializable接口:
优势:简单易用,Java内置支持。 不足:性能较低,不适用于跨语言。
JSON序列化:
优势:易于阅读、跨语言、通用性强。 不足:相对较慢,可能产生大量文本数据。
XML序列化:
优势:可读性强、结构化明确。 不足:相对冗长,性能较JSON差。
Protocol Buffers:
优势:高性能、紧凑、适用于跨系统通信。 不足:对数据结构定义要求严格,不够直观。
Kryo和Hessian:
优势:高性能、紧凑。 不足:可能不适用于所有数据类型,不如JSON、XML通用。
实现难度:
Serializable接口较为简单,JSON和XML相对易于理解。Protocol Buffers需要定义数据结构,较为复杂。Kryo和Hessian使用相对复杂但更高效的序列化方式。
跨系统和跨语言:
Serializable接口在Java环境下通用,但不适用于跨语言。JSON、XML和Protocol Buffers能够实现跨系统、跨语言交互。Kryo和Hessian则取决于具体的实现和库。
性能:
Serializable接口性能较低,JSON和XML相对较慢。Protocol Buffers、Kryo和Hessian具有更高的性能,尤其适用于高性能需求和大规模数据传输。
四.通常使用哪一个呢?。
在java的本系统中
没有特殊的要求下Serializable就可以了,因为简单。
注意事项:
1.序列化ID(Serial Version UID):确保显式声明serialVersionUID,这是序列化的版本标识符。它保证了反序列化时的兼容性。如果不显式指定,每次编译类时都会自动生成,可能导致兼容性问题。
serialVersionUID,该字段必须是静态的、final的和 long 类型;例如:访问修饰符 static final long serialVersionUID = 42L;
2.静态变量不能序列化(static修饰的属性)
3.不需要序列化使用transient关键字
4.序列化的类serialVersionUID要保持一致,避免序列化失败抛出InvalidClassExceptio异常
涉及到不同系统或与移动端交互
一般Json比较合适,因为可读性强,开发中遇到数据交互问题容易发现。
注意:本文归作者所有,未经作者允许,不得转载