安庆大理运城常德铜陵江西
投稿投诉
江西南阳
嘉兴昆明
铜陵滨州
广东西昌
常德梅州
兰州阳江
运城金华
广西萍乡
大理重庆
诸暨泉州
安庆南充
武汉辽宁

Java的序列化反序列化

5月1日 枯心人投稿
  一、介绍
  序列化和反序列化几乎是工程师们每天都需要面对的事情,尤其是当前流行的微服务开发。
  光看定义上,对于初学者来说,可能很难一下子理解序列化的意义,尤其是面对这种特别学术词语的时候,内心会不由自主的发问:它到底是啥,用来干嘛的?
  如果用通俗的方式来理解,你可以用变魔术的方式来理解它,就好比你想把一件铁器从一个地方运往到另一个地方,在出发的时候,通过魔术方式将这个东西融化成一桶铁水,当到达目的地之后,又通过变魔术的方式,将这桶铁水还原成一件铁器。当铁器变成铁水的过程,可以理解为序列化;从铁水变成铁器,可以理解为反序列化。
  站在程序世界的角度看,我们都知道计算机之间传递信息的最小单元是字节流,序列化其实就是将一个对象变成所有的计算机都能识别的字节流;反序列化就是将接受到的字节流还原成一个程序能识别的对象。
  简单的说,序列化最终的目的是为了对象可以更方面的进行跨平台存储和进行网络传输。
  基本上只要是涉及到跨平台存储或者进行网络传输的数据,都需要进行序列化。
  互联网早期的序列化方式主要有COM和CORBA。
  COM主要用于Windows平台,并没有真正实现跨平台,另外COM的序列化的原理利用了编译器中虚表,使得其学习成本巨大(想一下这个场景,工程师需要是简单的序列化协议,但却要先掌握语言编译器)。由于序列化的数据与编译器紧耦合,扩展属性非常麻烦。
  CORBA是早期比较好的实现了跨平台,跨语言的序列化协议。COBRA的主要问题是参与方过多带来的版本过多,版本之间兼容性较差,以及使用复杂晦涩。这些政治经济,技术实现以及早期设计不成熟的问题,最终导致COBRA的渐渐消亡。J2SE1。3之后的版本提供了基于CORBA协议的RMIIIOP技术,这使得Java开发者可以采用纯粹的Java语言进行CORBA的开发。
  随着软件技术的快速发展,之后逐渐出现了比较流行的序列化方式,例如:XML、JSON、Protobuf、Thrift和Avro等等。
  这些序列化方式各有千秋,不能简单的说哪一种序列化方式是最好的,只能从你的当时环境下去选择最适合你的序列化方式,如果你要为你的公司项目进行序列化技术的选型,主要可以从以下几个方面进行考虑:是否支持跨平台:尤其是多种语言混合开发的项目,是否支持跨平台直接决定了系统开发难度序列化的速度:速度快的方式会为你的系统性能提升不少序列化出来的大小:数据越小越好,小的数据传输快,也不占带宽,也能整体提升系统的性能
  BB了这么多,作为一名java程序员,我们应该如何使用序列化呢,以及序列化的过程中应该需要注意的问题。
  下面,我们一起来了解一下!二、代码实践2。1、序列化操作
  java实现序列化方式非常简单,只需要实现Serializable接口即可,例如下面这个类。publicclassStudentimplementsSerializable{用户名privateS年龄privateIpublicStudent(Stringname,Integerage){this。this。}OverridepublicStringtoString(){returnStudent1{namename,ageage};}}
  我们来测试一下,将Student对象进行二进制的数据存储后,并从文件中读取数据出来转成Student对象,这个过程其实就是一个序列化和反序列化的过程。publicclassObjectMainTest{publicstaticvoidmain(String〔〕args)throwsException{序列化serializeAnimal();反序列化deserializeAnimal();}privatestaticvoidserializeAnimal()throwsException{StudentblacknewStudent(张三,20);System。out。println(black。toString());System。out。println(开始序列化);ObjectOutputStreamoosnewObjectOutputStream(newFileOutputStream(object。log));oos。writeObject(black);oos。flush();oos。close();}privatestaticvoiddeserializeAnimal()throwsException{System。out。println(开始反序列化);ObjectInputStreamoisnewObjectInputStream(newFileInputStream(object。log));Studentblack(Student)ois。readObject();ois。close();System。out。println(black。toString());}}
  输出结果:Student{name张三,age20}开始序列化开始反序列化Student{name张三,age20}
  看起来是不是超级简单,但是请你别大意,这里面的坑还真不少,请看下面的问题汇总!三、序列化问题汇总3。1、static属性不能被序列化
  实际在序列化的时候,被static修饰的属性字段是不能被序列化进去的,因为静态变量属于类的状态,序列化并不保存静态变量!2。3、Transient属性不会被序列化
  被Transient修饰的属性无法被序列化,眼见为实,我们给Student类的name字段加一个transient修饰符。publicclassStudentimplementsSerializable{用户名privatetransientS。。。省略}
  运行测试方法,输出结果如下:Student{name张三,age20}开始序列化开始反序列化Student{namenull,age20}
  很明显,被transient修饰的name属性,反序列化后的结果为null。2。4、序列化版本号serialVersionUID问题
  只要是实现了Serializable接口的类都会有一个版本号,如果我们没有定义,JDK工具会按照我们对象的属性生成一个对应的版本号,当然我们还可以自定义,例如给Student类自定义一个序列化版本号,操作如下。publicclassStudentimplementsSerializable{自定义序列化版本号privatestaticfinallongserialVersionUID1l;。。。省略}
  如何验证这一点呢?
  首先,我们先序列化一个Student对象,里面没有自定义版本号,然后在反序列化的时候,我们给这个对象自定义一个版本号,运行测试程序,看能不能反序列化成功?Exceptioninthreadmainjava。io。InvalidClassException:com。example。java。serializable。test1。entity。Slocalclassincompatible:streamclassdescserialVersionUID821478144412499207,localclassserialVersionUID1atjava。io。ObjectStreamClass。initNonProxy(ObjectStreamClass。java:699)atjava。io。ObjectInputStream。readNonProxyDesc(ObjectInputStream。java:1885)atjava。io。ObjectInputStream。readClassDesc(ObjectInputStream。java:1751)atjava。io。ObjectInputStream。readOrdinaryObject(ObjectInputStream。java:2042)atjava。io。ObjectInputStream。readObject0(ObjectInputStream。java:1573)atjava。io。ObjectInputStream。readObject(ObjectInputStream。java:431)
  答案很明显,反序列化失败!
  分析原因:Student对象序列化时的版本号是821478144412499207,反序列化时的版本号是1,两者不一致,导致无法反序列化成功!
  当我们没有显式的自定义序列化版本号时,JDK会根据当前对象的属性自动生成一个对象的版本号,只要对象的属性不会发生变化,这个版本号也基本上不会发生变化,但是当对象的属性发生了变化,对应的反序列化对象没有跟着一起变化,大概率会出现反序列化失败!
  为了眼见为实,我们继续以实际案例给大家演示一下。
  还是以上面那个为主,我们先序列化一个Student对象,里面没有自定义版本号,然后在反序列化操作的时候,我们给Student对象新增一个属性email,同时也不自定义版本号。publicclassStudentimplementsSerializable{用户名privateS年龄privateI邮箱privateS省略set、get。。。}
  看看运行效果:Exceptioninthreadmainjava。io。InvalidClassException:com。example。java。serializable。test1。entity。Slocalclassincompatible:streamclassdescserialVersionUID821478144412499207,localclassserialVersionUID5996907635197467174atjava。io。ObjectStreamClass。initNonProxy(ObjectStreamClass。java:699)atjava。io。ObjectInputStream。readNonProxyDesc(ObjectInputStream。java:1885)atjava。io。ObjectInputStream。readClassDesc(ObjectInputStream。java:1751)atjava。io。ObjectInputStream。readOrdinaryObject(ObjectInputStream。java:2042)atjava。io。ObjectInputStream。readObject0(ObjectInputStream。java:1573)atjava。io。ObjectInputStream。readObject(ObjectInputStream。java:431)
  答案很显然,反序列化报错了!两者的版本号不一致!
  在平时开发的过程中,实体类的属性难免会发生改动,我们有些同学啊,在写代码的时候只是把序列化的接口实现了,但是没有自定义版本号,在这点上,我强烈建议大家一定要给每个实现了Serializable接口的类,自定义一个版本号,即使对象的属性发生了变化,也不会影响到数据的序列化和反序列化操作!
  操作很简单,直接在实体类里面加上这个静态变量即可!自定义序列化版本号privatestaticfinallongserialVersionUID1l;2。5、父类、子类序列化问题
  在实际的开发过程中,尤其是实体类,为了对象属性的复用,我们往往会采用继承的方式来处理。
  使用了继承之后,父类属性是否可以正常被序列化呢?下面我们一起来看看!父类没有实现序列化,子类实现序列化
  首先我们创建两个类Parent和Child,Child继承自Parent。publicclassParent{privateSpublicStringgetName(){}publicParentsetName(Stringname){this。}}publicclassChildextendsParentimplementsSerializable{privatestaticfinallongserialVersionUID1l;privateSpublicStringgetId(){}publicChildsetId(Stringid){this。}}
  编写测试类,先序列化,然后再反序列化!publicclassObjectMainTest{publicstaticvoidmain(String〔〕args)throwsException{serializeAnimal();deserializeAnimal();}privatestaticvoidserializeAnimal()throwsException{ChildblacknewChild();black。setId(123);black。setName(张三);System。out。println(id:black。getId(),name:black。getName());System。out。println(开始序列化);ObjectOutputStreamoosnewObjectOutputStream(newFileOutputStream(object。log));oos。writeObject(black);oos。flush();oos。close();}privatestaticvoiddeserializeAnimal()throwsException{System。out。println(开始反序列化);ObjectInputStreamoisnewObjectInputStream(newFileInputStream(object。log));Childblack(Child)ois。readObject();ois。close();System。out。println(id:black。getId(),name:black。getName());}}
  运行结果如下:id:123,name:张三开始序列化开始反序列化id:123,name:null
  结果很明显,父类的属性没有被序列化进去!
  我们在来试试,另一种常见父类实现序列化,子类不实现序列化publicclassParentimplementsSerializable{privatestaticfinallongserialVersionUID1L;privateSpublicStringgetName(){}publicParentsetName(Stringname){this。}}publicclassChildextendsParent{privateSpublicStringgetId(){}publicChildsetId(Stringid){this。}}
  接着运行一次程序,结果如下!id:123,name:张三开始序列化开始反序列化id:123,name:张三
  结果很明显,父类的属性被序列化进去!
  假如,子类和父类,都实现了序列化,并且序列化版本号都不一样,会不会出现问题呢?父类实现序列化,子类实现序列化publicclassParentimplementsSerializable{privatestaticfinallongserialVersionUID1L;privateSpublicStringgetName(){}publicParentsetName(Stringname){this。}}publicclassChildextendsParentimplementsSerializable{privatestaticfinallongserialVersionUID2l;privateSpublicStringgetId(){}publicChildsetId(Stringid){this。}}
  运行一次程序,结果如下!id:123,name:张三开始序列化开始反序列化id:123,name:张三
  父类的属性序列化依然成功,当父、子类都实现了序列化,并且定义了不同的版本号,这种情况下,版本号是跟着子类的版本号走的!
  总结起来,当父类实现序列化时,子类所有的属性也会全部被序列化;但是当父类没有实现序列化,子类在序列化时,父类属性并不会被序列化!2。6、自定义序列化过程
  Serializable接口内部序列化是JVM自动实现的,但是在某些少数的场景下,你可能想自定义序列化和反序列化的内容,但是又不想改实体类属性,这个时候你可以采用自定义序列化的实现方式。
  自定义序列化方式,其实也很简单,只需要实现JDK自身提供的Externalizable接口就行,里面有两个核心方法,一个是数据写入,另一个是数据的读取。publicinterfaceExternalizableextendsjava。io。Serializable{voidwriteExternal(ObjectOutputout)throwsIOEvoidreadExternal(ObjectInputin)throwsIOException,ClassNotFoundE}
  Externalizable接口的实现过程也很简单,我们创建一个Person,实现自Externalizable的两个方法。publicclassPersonimplementsExternalizable{privatestaticfinallongserialVersionUID1l;privateS实现了Externalizable这个接口时需要提供无参构造,在反序列化时会检测publicPerson(){System。out。println(Person:empty);}publicPerson(Stringname,intage){this。this。}OverridepublicvoidwriteExternal(ObjectOutputout)throwsIOException{System。out。println(personwriteExternal。。。);out。writeObject(name);out。writeInt(age);}OverridepublicvoidreadExternal(ObjectInputin)throwsClassNotFoundException,IOException{System。out。println(personreadExternal。。。);name(String)in。readObject();agein。readInt();}OverridepublicStringtoString(){returnPerson{namename,ageage};}}
  测试Person对象的序列化和反序列化。publicclassExternalizableMain{publicstaticvoidmain(String〔〕args)throwsIOException,ClassNotFoundException{serializable();deserializable();}privatestaticvoidserializable()throwsIOException{PersonpersonnewPerson(张三,15);System。out。println(person。toString());System。out。println(开始序列化);FileOutputStreamboasnewFileOutputStream(person。log);ObjectOutputStreamoosnewObjectOutputStream(boas);oos。writeObject(person);oos。close();boas。close();}privatestaticvoiddeserializable()throwsIOException,ClassNotFoundException{System。out。println(反序列化);ObjectInputStreambisnewObjectInputStream(newFileInputStream(person。log));Personperson(Person)bis。readObject();System。out。println(person。toString());}}
  运行结果如下:Person{name张三,age15}开始序列化personwriteExternal。。。反序列化Person:emptypersonreadExternal。。。Person{name张三,age15}四、小结
  对象的序列化,在实际的开发过程中,使用的非常频繁,尤其是微服务开发,如果你用的是SpringBootDubbo组合的框架,那么在通过rpc调用的时候,如果传输的对象没有实现序列化,会直接报错!
  在使用序列化的时候,坑点还不少,尤其是版本号的问题,这个很容易被忽略,大家在实际开发的时候,强烈推荐自定义版本号,这样可以避免传输的对象属性发生变化的时候,接口反序列化出错的概率!
投诉 评论

Java的序列化反序列化一、介绍序列化和反序列化几乎是工程师们每天都需要面对的事情,尤其是当前流行的微服务开发。光看定义上,对于初学者来说,可能很难一下子理解序列化的意义,尤其是面对这种特……经济基础决定上层建筑,那么请问是什么在决定经济基础呢?1、在经济基础和上层建筑的相互关系中,首先是经济基础决定上层建筑。上层建筑是经济基础的反映,它的产生、发展和变革,都不能从它本身来解释,而只能由它的经济基础来说明。2、经……特斯拉员工猝死在生产线上,官方机构介入调查鞭牛士1月21日消息,据IT之家援引外媒消息,美国当地时间周三早上,特斯拉一名员工猝死在加州弗里蒙特工厂的动力总成系统生产线上,相关机构已经介入调查。加州职业安全与健康管……使用快一个月,再分享鸿蒙的3个小技巧鸿蒙小技巧又要跟小伙伴们见面了,今天更新3个小技巧,希望能帮到大家。第一,我是一个摄影爱好者,首先从相机说起,拍照最重要的就是手稳,而现在相机的快门键都固定在相机的一侧,……假如嫦娥N号不停地采样回来,每次2kg,直到整个月球都被取样假如你一直吃饭,直到地球被吃完会发生什么!量变到质变假如嫦娥N号不停地采样回来,每次2kg,直到整个月球都被取样回地球会发生什么?会发生的情况多了,比如以下几种:……王传福腾势汽车将迎来优质产品集中投放期黄灵灵中国证券报中证网中证网讯(记者黄灵灵)5月16日晚间,腾势汽车发布首款高端MPV产品腾势D9。比亚迪董事长兼总裁王传福在发布会上表示,公司对腾势汽车的未来充满信心。……业界首例!华为又破冰新技术如今,随着科技的发展和社会的进步,我们日常生活的很多商品基本上发展都十分成熟。各大厂商之间的竞争也渐渐变为高科技的竞争。就比如我们所熟悉的手机,还有笔记本电脑。说起笔记本……如何解决华为手机杀后台严重的情况呢?华为手机经常出现杀后台的现象,有时候切换后台应用就需要重新打开,发个微信回到游戏又没了,确实很烦人。如何解决华为手机杀后台严重的现象呢?在手机设置中,我们先找到应用……今日头条申请认证,与不认证的区别是什么?你好,我是全职自媒体人,很荣幸回答你的问题。以下观点和建议,供参考:头条有几个认证,不同的认证,作用不同。不知道你说的哪个,一一给你说下吧。几个认证分别是:实名认证,兴趣……行情最新5大智能手机品牌用户换机数据小米忠诚度最高研究机构QuestMobile最近发布的一份2021年6月中国智能终端半年洞察报告数据蛮有意思的,公布了今年6月份不同品牌用户的换机数据。如上图所见,该研究机构罗列了在6……MySQL数据库表被锁解锁,删除事务背景在程序员的职业生涯中,总会遇到数据库表被锁的情况,前些天就又撞见一次。由于业务突发需求,各个部门都在批量操作、导出数据,而数据库又未做读写分离,结果就是:数据库的某张……消息称英伟达要求厂商暂停RTX3090Ti显卡生产IT之家1月15日消息,英伟达于1月5日正式发布了全新的RTX3090Ti旗舰显卡。这款产品拥有10752个CUDA核心,依旧配备24GBGDDR6X显存,但是显存颗粒的带宽快……
日本在哪一场战争后开始疯狂的扩张?1939年,北京老照片被邓小平通报,被刘伯承批评的上甘岭战役的总指挥秦大胆音容犹在,丰碑永存!缅怀周恩来总理齐桓公去世后,诸子争夺君位,时长竟达四十年清末罕见照片清军士兵挎腰刀充门面,男子戴着肚兜一身赘肉毛主席巧解字毛与蒋惟妙惟肖,王与王各不相同也谈刑不上大夫消失的明太宗谁动了朱棣的庙号?为何要改成祖呢?1979年,刚入伍52天的新兵就获一等功,后来升至什么军衔?王翦为秦始皇灭楚,逼死项羽祖父项燕,秦朝灭亡后其家族命运如何乾隆收复新疆后,为解决男多女少问题,采用一方法人口增十倍小朋友防晒妙招多知道这几个就够了广州公租房收费标准是什么家长必读岁前不看电视孩子将一生受益迪卡侬的护臂好吗柳永少年游新东方年底前终止K9学科类培训,俞敏洪否认照搬李佳琦玩摄影和音乐,哪个更烧钱?饭不做,要造反啊,女人熬了20年,不想再熬了《白夜行》中桐原弥生子人物形象分析用情深伤感的短句在所有你想珍惜的关系里,问心无愧就好詹姆斯韦伯望远镜即将在深空中对准其镜面管理故事:为什么让第一个石匠下岗

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找七猫云易事利