来源:https://blog.csdn.net/litianx...前言最近公司在做服务,模型包中的所有类都需要实现Serializable接口,同时显示值指定的serialVersionUID。听到这个需求,脑子里突然冒出几个问题,比如:什么是序列化和反序列化?为什么要实现Serializable接口来实现序列化和反序列化呢?实现Serializable接口就可以了,为什么需要它呢?显示指定serialVersionUID的值?我应该为serialVersionUID指定什么值?让我们一一回答这些问题。字面序列化和反序列化Serialization:将对象转换为字节序列的过程称为对象序列反序列化。反序列化:将字节序列恢复为对象的过程称为对象的反序列化。什么时候需要使用序列化和反序列化?当我们只在本地JVM中运行Java实例时,这个时候不需要进行序列化和反序列化,但是当我们需要将内存中的对象持久化到磁盘或数据库中,当我们需要与浏览器进行交互时,当我们需要实现RPC,这个时候需要序列化和反序列化。前两个需要序列化和反序列化的场景是不是让我们产生了很大的疑问?我们在和浏览器交互的时候,还是要把对象存储在内存中,持久化到数据库的时候,好像没有进行序列化和反序列化,因为我们没有实现Serializable接口,但是一直在正常运行。先给出如下结论:只要我们持久化或者网络对象在内存中传输,这个时候就需要序列化和反序列化。原因:服务端与浏览器交互时真的没有用到Serializable接口吗?JSON格式其实就是把一个对象转成一个字符串,所以当服务端和浏览器交互的时候数据格式其实是一个字符串,我们看String类型的源码:publicfinalclassStringimplementsjava.io.Serializable,Comparable,CharSequence{/**该值用于字符存储。*/私人决赛字符值[];/**缓存字符串的哈希码*/privateinthash;//默认为0/**使用JDK1.0.2的serialVersionUID实现互操作性*/privatestaticfinallongserialVersionUID=-6849794470754667710L;......}String类型实现Serializable接口,显示指定serialVersionUID的值。然后我们看对象持久化到数据库时的情况。Mybatis数据库映射文件中插入代码:INSERTINTOt_user(name,age)VALUES(#{name},#{age})其实我们并不是将整个对象持久化到数据库中,而是将对象中的属性持久化到数据库中,而这些属性就是实现Serializable接口的基本属性。为什么要实现序列化和反序列化的Serializable接口?Java实现Serializable接口后,JVM会帮我们在底层实现序列化和反序列化。如果我们不实现Serializable接口,那么我们可以自己写一套序列化和反序列化的代码。至于怎么写,你可以谷歌一下就算实现了Serializable接口,为什么还要显示指定serialVersionUID的值呢?如果没有显示指定的serialVersionUID,JVM在序列化时会根据该属性自动生成一个serialVersionUID,然后在持久化或网络传输前用该属性序列化。反序列化时,JVM会根据属性自动生成新版本的serialVersionUID,然后将这个新版本的serialVersionUID与序列化时生成的旧版本的serialVersionUID进行比较。如果相同则反序列化成功,否则会报错。如果指定了serialVersionUID,JVM在序列化和反序列化的时候还是会生成一个serialVersionUID,但是value是我们显示指定值的值,这样反序列化的时候新旧版本的serialVersionUID会保持一致。在实际开发中,不显示指定serialVersionUID的情况会导致什么问题?如果我们的类写完之后不修改,当然不会有问题,但是在实际开发中这是不可能的。我们的类会不断迭代。一旦类被修改,旧对象反序列化就会报错。所以在实际开发中,我们会指定一个serialVersionUID。值多少并不重要,只要它保持不变即可。写一个例子测试:(1)User类不显示指定的serialVersionUID。publicclassUserimplementsSerializable{privateStringname;私人整数年龄;publicStringgetName(){返回名称;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){返回年龄;}publicvoidsetAge(Integerage){这个。年龄=年龄;}@OverridepublicStringtoString(){返回"User{"+"name='"+name+'\''+",age="+age+'}';}}(2)测试类先序列化,再反序列化。公共类SerializableTest{privatestaticvoidserialize(Useruser)throwsException{ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream(newFile("D:\\111.txt")));oos.writeObject(用户);oos.close();}privatestaticUserdeserialize()throwsException{ObjectInputStreamois=newObjectInputStream(newFileInputStream(newFile("D:\\111.txt"))));返回(用户)ois.readObject();}publicstaticvoidmain(String[]args)throwsException{Useruser=newUser();user.setName("tyshawn");user.setAge(18);System.out.println("序列化前的结果:"+user);序列化(用户);用户dUser=反序列化();System.out.println("反序列化结果:"+dUser);}}(3)结果,先注释掉反序列化代码,执行序列化代码,然后添加一个属性sexpublicclassUserimplementsSerializable{privateStringname;私人整数年龄;私人字符串性别;publicStringgetName(){返回名称;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){返回年龄;}publicvoidsetAge(Integerage){this.age=age;}publicStringgetSex(){返回性别;}publicvoidsetSex(Stringsex){this.sex=sex;}@OverridepublicStringtoString(){return"User{"+"name='"+name+'\''+",age="+age+",sex='"+sex+'\''+'}';}}注释掉序列化代码,执行反序列化代码,最终结果如下:序列化前的结果:User{name='tyshawn',age=18}Exceptioninthread"main"java.io.InvalidClassException:org.tyshawn.SerializeAndDeserialize.User;本地类不兼容:stramclassdescserialVersionUID=1035612825366363028,localclassserialVersionUID=-1830850955895931978报错结果是序列化和反序列化生成的serialVersionUID不一致。接下来我们在上面的User类的基础上指定一个serialVersionUIDprivatestaticfinalLlong然后执行上面的serialVer1步骤和测试结果如下:序列化前的结果:User{name='tyshawn',age=18}反序列化后的结果:显示User{name='tyshawn',age=18,sex='null'}指定serialVersionUID后,可以解决序列化和反序列化时serialVersionUID不一致的问题。Java序列化的其他特性将首先总结。transient关键字修饰的属性不会被序列化,静态属性也不会被序列化。.让我们来检验这个结论:(1)UserclasspublicclassUserimplementsSerializable{privatestaticfinallongserialVersionUID=1L;私有字符串名称;私人整数年龄;私有瞬态字符串性;privatestaticStringsignature="你眼中的世界就是你自己";publicStringgetName(){返回名称;}publicvoidsetName(Stringname){this.name=name;}publicIntegergetAge(){返回年龄;}publicvoidsetAge(Integerage){this.age=age;}民众StringgetSex(){返回性别;}publicvoidsetSex(Stringsex){this.sex=sex;}publicstaticStringgetSignature(){返回签名;}publicstaticvoidsetSignature(Stringsignature){User.signature=signature;}@OverridepublicStringtoString(){return"User{"+"name='"+name+'\''+",age="+age+",sex='"+sex+'\''+",signature='"+signature+'\''+'}';}}(2)测试类publicclassSerializableTest{privatestaticvoidserialize(Useruser)throwsException{ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream(newFile("D:\\111.txt")));oos.writeObject(用户);oos.close();}privatestaticUserdeserialize()throwsException{ObjectInputStreamois=newObjectInputStream(newFileInputStream(newFile("D:\\111.txt")));返回(用户)ois.readObject();}publicstaticvoidmain(String[]args)throwsException{Useruser=newUser();user.setName("tyshawn");user.setAge(18);user.setSex("男人");System.out.println("序列化前的结果:"+user);序列化(用户);用户dUser=反序列化();System.out.println("反序列化结果:"+dUser);}}(3)结果,先注释掉反序列化代码,执行序列化代码,然后修改User类签名=“我只看到你”,然后注释掉序列化代码,执行反序列化代码,最后结果如下:序列化前的结果:User{name='tyshawn',age=18,sex='man',signature='你眼中的世界就是你的样子'}反序列化结果:User{name='tyshawn',age=18,sex='null',signature='我只看到你'}static属性为什么没有序列化?因为序列化是针对对象的,而静态属性优先于对象,并且是用类加载的,所以不会被序列化。看到这个结论,会有人问,serialVersionUID也是静态修饰的,为什么serialVersionUID要序列化呢?其实serialVersionUID属性并没有被序列化,JVM在序列化对象的时候会自动生成一个serialVersionUID,然后将我们指定的serialVersionUID属性值赋值给自动生成的serialVersionUID.任何技术问题?欢迎在评论区留言~推荐近期热点文章:1.1000+Java面试题及答案(2022最新版)2.精彩!Java协程来了。.3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!