在我们实际使用中,我们经常会遇到对象需要序列化的问题。我将通过下面几个问题,理解序列化的相关问题
当我们需要把Java的内存对象持久化到文件/数据库,或者进行网络传输时,就需要对对象进行序列化。
在Java中,如果一个类实现了Serializable接口,JVM底层就会帮我们实现序列化和反序列化。如果我们不实现Serializable接口,那么就需要我们自己实现序列化和反序列化
在Java中,如果一个实现了Serializable接口的类没有指定serialVersionUID,那么JVM在序列化时,会根据属性自动生成一个serialVersionUID,然后和属性一起序列化,再进行网络传输或者持久化。在反序列化时,JVM会再根据属性生成一个新版本的serialVersionUID,然后再用这个新版本和serialVersionUID和序列化时生成的旧版本的serialVersionUID进行计较。如果二者一样就可以序列化成功。反之,报错。
如果我们显示指定了serialVersionUID,JVM在序列化和反序列化时,就会使用我们指定的serialVersionUID。这样我们就可以确保在反序列化时,serialVersionUID和之前的相同。
在实际的开发中,不指定serialVersionUID的值。在我们修改一个类的属性,或者使用不同版本的jdk时,都有可能会导致自动生成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,就可以避免这个问题。
因为序列化序列的时内存对象,static属性先于对象加载。随着类的加载,static就加载好了。所以不会被序列化。
serialVersionUID被static修饰,为什么会被序列化?
serialVersionUID属性并没有被序列化,JVM在序列化时会自动给对象生成一个serialVersionUID。我们手动指定的话,serialVersionUID属性的话,这个值就会赋给serialVersionUID