什么是序列化?
序列化(Serialization):将对象信息(对象的类类型,对象的数据,数据的类型)转换成一个字节 (bytes) 序列。通常用于在网络间传输对象信息或存储对象信息在磁盘文件(.ser)中。类似于 XML 和 JSON,只不过 XML 和 JSON 是文本格式,Java serialization 是二进制格式。对应的,反序列化(Deserialization)就是将二进制字节序列转换成对象的过程。
【Java 序列化】序列化是平台独立的,在一个平台上序列化,可以在另一个平台上反序列化。注意:类的方法不会被序列化,因为序列化双方通常都会拥有类的信息,类方法没有序列化的必要。
为什么要序列化呢?
因为网络架构和硬盘只能理解 bits 和 bytes,所以需要将 Java 对象转换成对应格式才能进行传输。
JDK 自带的序列化
使用 JDK 序列化对象,操作为:
- 被序列化的类实现
java.io.Serializable
接口; - 创建
ObjectOutputStream
输出流,调用输出流的writeObject
方法输出序列化对象;
public class Employee implements Serializable {
private static final long serialVersionUID = 1905122041950251207L;
private String name;
...
}public class MySerialize {
public static void main(String[] args) {
Employee e = new Employee();
try {
FileOutputStream fileOut = new FileOutputStream("/tmp/employee.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(e);
// 序列化操作
out.close();
fileOut.close();
} catch (IOException i) {
i.printStackTrace();
}
}
}
使用 JDK 反序列化对象,操作为:
- 创建
ObjectInputStream
输入流,通过readObject
方法反序列化对象;
public class MyDeserialize {
public static void main(String[] args) {
Employee e = null;
try {
FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
// 默认的反序列化进程不会调用类的构造器
e = (Employee) in.readObject();
// 反序列化操作
in.close();
fileIn.close();
} catch (IOException i) {
i.printStackTrace();
} catch (ClassNotFoundException c) {
System.out.println("Employee class not found");
c.printStackTrace();
}
}
}
serialVersionUID 字段
用于版本控制,在反序列化期间使用,用来验证已序列化对象的发送方和接收方是否为该对象加载了与序列化相兼容的类。如果没有声明这一个字段,JVM 会在运行时动态生成一个,所以如果修改了类的结构,如修改 / 增加 / 删除字段,反序列化的时候 JVM 就会生成和原来不一样的
serialVersionUID
,这时 ObjectInputStream
的 readObject()
方法会抛出 InvalidClassException
异常。但如果我们声明了这一字段,如 private static final long serialVersionUID = 2L;
,那么我们修改类结构后 serialVersionUID
不会发生变化,所以就不会抛出这个异常。因此,强烈建议每个实现了 Serializable 接口的类声明这个字段。transient 关键字
标记有 transient 关键字的字段不能被序列化,同样地,由于 static 字段属于类而不是对象,所以 static 字段也不能被序列化。
为什么一个类实现了 Serializable 接口,它就可以被序列化呢? 查看
ObjectOutputStream
源码,查看 writeObject
方法,发现它调用了 writeObject0
方法:/**
* Underlying writeObject/writeUnshared implementation.
*/
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
...
// remaining cases
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
...
}
可以看到如果对象是「String,数组,Enum,Serializable」就可以进行序列化操作,否则将抛出
NotSerializableException
异常。参考资料:
- 什么是序列化?常见的序列化协议有哪些?
- java 序列化,看这篇就够了
- Java 序列化-菜鸟教程
推荐阅读
- SSM框架|[超详细]SSM框架整合教程--入门级(图书增删改查)
- 中间件|Redis从入门到精通
- java|Java小白从入门到精通,Java零基础入门看这一篇就够了
- 程序员|Redis从入门到精通,至少要看看这篇,几乎囊括了Java的所有知识点
- 深入理解Spring生态|Spring第三讲(SpringMVC 从入门到精通)
- 面试官(如何保证用户模块的数据安全(说说你的解决方案))
- Java/Spring/Dubbo三种SPI机制,到底谁更好()
- 人工智能|Pokémon AI,使用DALL-E生成神奇宝贝图鉴
- python|OpenAI 发布 DALL·E 进化版,这只蒸汽朋克时代的小熊有点酷~