Java序列化和反序列化
在我们实际使用中,我们经常会遇到对象需要序列化的问题。我将通过下面几个问题,理解序列化的相关问题
- 什么是序列化和反序列化
- 什么时候需要进行序列化和反序列化
- 实现序列化和反序列化为什么要实现Serializable接口
- 在实现Serializable接口时,为什么要显示指定serialVersionUID的值
- Java的序列化的特性
- static为什么不需要被序列化
什么是序列化和反序列化
- 序列化:把Java内存对象转换为字节序列的过程称为对象的序列化
- 反序列化:把字节序列恢复为Java内存对象的过程称为对象的反序列化
什么时候需要序列化和反序列化
当我们需要把Java的内存对象持久化到文件/数据库,或者进行网络传输时,就需要对对象进行序列化。
实现序列化和反序列化为什么要实现Serializable接口
在Java中,如果一个类实现了Serializable接口,JVM底层就会帮我们实现序列化和反序列化。如果我们不实现Serializable接口,那么就需要我们自己实现序列化和反序列化
实现Serializable接口时,为什么需要指定serialVersionUID的值
在Java中,如果一个实现了Serializable接口的类没有指定serialVersionUID,那么JVM在序列化时,会根据属性自动生成一个serialVersionUID,然后和属性一起序列化,再进行网络传输或者持久化。在反序列化时,JVM会再根据属性生成一个新版本的serialVersionUID,然后再用这个新版本和serialVersionUID和序列化时生成的旧版本的serialVersionUID进行计较。如果二者一样就可以序列化成功。反之,报错。
如果我们显示指定了serialVersionUID,JVM在序列化和反序列化时,就会使用我们指定的serialVersionUID。这样我们就可以确保在反序列化时,serialVersionUID和之前的相同。
在实际的开发中,不指定serialVersionUID的值。在我们修改一个类的属性,或者使用不同版本的jdk时,都有可能会导致自动生成serialVersionUID发生改变。从而会导致这个类在进行反序列化时失败。
下面我们写一个例子:
- 测试实体类,Stu,实现了Serializable接口,但是不指定serialVersionUID
public class Stu implements Serializable {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Stu{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- 测试类
public class SerializableTest {
private static void serialize(Stu stu) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("./1.txt")));
oos.writeObject(stu);
oos.close();
}
private static Stu deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("./1.txt")));
return (Stu)ois.readObject();
}
@Test
public void testSerializer() throws Exception {
Stu stu = new Stu();
stu.setId(18);
stu.setName("hardy");
System.out.println("before serializer" + stu);
serialize(stu);
Stu deserializeStu = deserialize();
System.out.println("after deserializer:" + deserializeStu);
}
}
此时,我们注释代码中序列化的的部分,给Stu再增加一个属性,再进行反序列化时就会报错:
java.io.InvalidClassException: com.hardy.pojo.Stu; local class incompatible: stream classdesc serialVersionUID = -5638707890362050979, local class serialVersionUID = 6871763750857709435
at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:715)
at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2021)
at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1890)
at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2183)
at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1707)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:517)
at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:475)
at com.hardy.SerializableTest.deserialize(SerializableTest.java:23)
at com.hardy.SerializableTest.testSerializer(SerializableTest.java:34)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
这是因为,序列化和反序列化的serialVersionUID不一致。我们在序列化时,增加serverVersionUID,就可以避免这个问题。
Java序列化的特性
- 被transient关键字修饰的属性不会被序列化
- static属性也不会被序列化
static属性为什么不会被实例化
因为序列化序列的时内存对象,static属性先于对象加载。随着类的加载,static就加载好了。所以不会被序列化。
serialVersionUID被static修饰,为什么会被序列化?
serialVersionUID属性并没有被序列化,JVM在序列化时会自动给对象生成一个serialVersionUID。我们手动指定的话,serialVersionUID属性的话,这个值就会赋给serialVersionUID
欢迎大家阅读,有问题和不足的地方欢迎大家指出。作者:永和九年,转载请注明原文链接:https://www.cnblogs.com/hardyzhou/p/16629484.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话