java序列化什么是序列化?对象和二进制转换。转换的目的是什么?将对象转换为二进制,然后将二进制还原为对象。具体应用场景是将对象写入磁盘文件,或者更常见的是将对象传输到远程机器(例如dubborpc框架)。什么是java序列化?Java序列化有点特殊,就是对象和字节数组(byte[])的转换。但是,字节数组本质上也是二进制的。序列化有什么作用?因此,无论是其他语言还是Java语言的序列化,其本质功能都是从磁盘文件或远程机器中恢复对象。官方文档介绍Serialization用于轻量级持久化以及通过套接字或JavaRemoteMethodInvocation(JavaRMI)进行通信。https://docs.oracle.com/javas...如何实现序列化?实现序列化接口publicclassPersonimplementsSerializable{privatestaticfinallongserialVersionUID=2709425275741743919L;demopojo类主要是实现序列化接口。packagetest2;importjava.io.Serializable;publicclassPersonimplementsSerializable{//实现序列化接口privatestaticfinallongserialVersionUID=1L;私有字符串名称;私人整数年龄;私有字符串地址;publicPerson(){}publicPerson(Stringname,Integerage,Stringaddress){this.name=name;这个。年龄=年龄;this.address=地址;}@OverridepublicStringtoString(){return"test2.Person{"+"name='"+name+'''+",age="+age+",address='"+address+'''+'}';}}测试类,核心步骤序列化将对象转换为二进制(即Section数组),然后写入磁盘文件2,反序列化从磁盘文件中恢复对象,其实就是将二进制转换为对象包test2;导入java.io.File;导入java.io.FileInputStream;导入java.io.FileOutputStream;导入java.io.ObjectInputStream;导入java.io.ObjectOutputStream;/***@authorgzh*@createTime2021/12/68:08PM*/publicclassTest{publicstaticvoidmain(String[]args)抛出异常{testversion1L();}publicstaticvoidtestversion1L()抛出异常{Filefile=newFile("Person.out");//序列化ObjectOutputStreamoout=newObjectOutputStream(newFileOutputStream(file));PersonPerson=newPerson("号子",22,"上海");oout.writeObject(人);//将对象转换为二进制(即字节数组),然后写入磁盘文件oout.close();//反序列化ObjectInputStreamoin=newObjectInputStream(newFileInputStream(file));ObjectnewPerson2=oin.readObject();//从磁盘文件中恢复对象,将二进制转换为对象oin.close();System.out.println(newPerson2);}}java序列化究竟做了什么?协议格式是什么?最重要和必不可少的是将对象转换为二进制格式。java中独有的一套东西主要包括:java基本数据类型java非基本数据类型所以,简而言之,只有java语言本身才能序列化java中的二进制识别,也就是只有java语言本身才能还原二进制和将其转换为对象。源代码分析参考:https://juejin.cn/post/703916...序列化id的作用到底是什么?jdkapi官方文档Serializable如果一个可序列化的类没有显式声明一个serialVersionUID,那么序列化运行时会计算一个默认的该类的serialVersionUID值基于类的各个方面,如Java(TM)对象序列化规范中所述。但是,强烈建议所有可序列化类显式声明serialVersionUID值,因为默认的serialVersionUID计算对类高度敏感细节可能因编译器实现而异,因此可能在反序列化过程中导致意外的InvalidClassExceptions。因此,为了保证在不同的Java编译器实现中具有一致的serialVersionUID值,可序列化类必须声明一个显式的serialVersionUID值。还强烈建议显式serialVersionUID声明尽可能使用private修饰符,因为此类声明仅适用于立即声明的类——serialVersionUID字段作为继承成员没有用处。总结一下serializationid,本质就是pojo类的版本号。如果没有显式写序列化id,jvm会默认生成一个随机值。如果写的话,一般情况下写1??L作为值就可以了。也可以用idea生成一个很大的随机值。官方推荐,最好显示一个值(一般情况下,1L就够了)——因为可能jvm的实现不同,导致consumer和provider的jvm生成的版本号不一样。如果提供者新增字段或修改字段类型,则需要升级序列化id版本。这种情况下provider在反序列化的时候会出现异常:consumer和provider序列化后的id版本不一致。这正是我们想要的结果——因为如果提供者增加了一个新的字段或者修改了字段类型,但是提供者没有升级序列化的id版本,而是仍然和提供者的版本一样,那么这个时候提供者的反序列化,不会有异常,但此时provider新增字段的值为null(provider新增了字段)或者类型转换异常(provider修改了字段类型)。demo沿用上面的demo代码示例,这里再做一个演示。首先添加一个字段——emailtoprovider的pojo类。包测试;导入java.io.Serializable;公共类Person实现Serializable{privatestaticfinallongserialVersionUID=2L;私有字符串名称;私人整数年龄;私有字符串地址;私人字符串电子邮件;//新字段publicPerson(){}publicPerson(Stringname,Integerage,Stringaddress){this.name=name;这个。年龄=年龄;this.address=地址;}publicPerson(Stringname,Integerage,Stringaddress,Stringemail){this.name=name;这个。年龄=年龄;this.address=地址;this.email=电子邮件;}@OverridepublicStringtoString(){return"Person{"+"name='"+name+'''+",age="+age+",address='"+address+'''+",电子邮件='"+电子邮件+'''+'}';}}然后,直接从上面demo生成的文件反序列化得到对象。注意序列化代码要注释掉。包测试;导入java.io.File;导入java.io.FileInputStream;导入java.io.FileOutputStream;导入java.io.ObjectInputStream;导入java.io.ObjectOutputStream;导入test2.Person;/***@authorgzh*@createTime2021/12/68:08PM*/publicclassTest{publicstaticvoidmain(String[]args)throwsException{testversion1L();}publicstaticvoidtestversion1L()throwsException{Filefile=newFile("Person.out");//序列化//ObjectOutputStreamoout=newObjectOutputStream(newFileOutputStream(file));//PersonPerson2=newPerson("Haozi",22,"Shanghai");//oout.writeObject(Person2);//out.close();//反序列化ObjectInputStreamoin=newObjectInputStream(newFileInputStream(file));对象newPerson2=oin.readObject();//获取对象oin.close();System.out.println(newPerson2);}}打印结果:Person{name='Haozi',age=22,address='Shanghai',email='null'}说明:provider也必须有对应的pojo类从打印结果可以得到对象通过直接从磁盘文件反序列化。前提是provider也必须有同包同类名的pojo类,provider才能创建对象。如果provider没有对应的pojo类,会报错:classexceptionnotfound。线程“main”中的异常java.lang.ClassNotFoundException:Personatjava.net.URLClassLoader.findClass(URLClassLoader.java:381)atjava.lang.ClassLoader.loadClass(ClassLoader.java:424)atsun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)在java.lang.ClassLoader.loadClass(ClassLoader.java:357)在java.lang.Class.forName0(NativeMethod)在java.lang.Class.forName(Class.java:348)在java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:686)在java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1868)在java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)在java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)在java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)在java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)在Test.testversion1L(Test.java:27)atTest.main(Test.java:14)如何模拟这个异常?直接把pojo类删掉或更换一个名字即可。为什么提供者的新字段的值为空?如果provider有对应的pojo类,provider可以从磁盘文件的二进制数据中创建对象,就像上面的打印结果一样。但是由于提供者增加了一个新字段,而消费者在向磁盘文件写入二进制数据时没有这个字段,所以新字段的值为null。但是,在实际工作中,我们不应该允许这种情况发生。如果provider增加了新的字段,需要升级pojo类,即将序列化id的值改为2L。继续分析,如上例,消费者写入磁盘的二进制数据版本为1L。这时候provider在反序列化的时候会报错:版本号不匹配,具体是stream(即consumer)版本号为1,本地(即provider)版本为2。线程“main”中的异常java.io.InvalidClassException:Person;本地类不兼容:流classdescserialVersionUID=1,本地类serialVersionUID=2在java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)在java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)在java.io。ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)在java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)在java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)在java.io.ObjectInputStream.readObject(ObjectInputStream).java:431)atTest.testversion1L(Test.java:27)atTest.main(Test.java:14)与目标VM断开连接,地址:'127.0.0.1:0',传输:'socket'Processfinishedwithexitcode1虽然此时是异常的,但这正是我们想要的结果。因为一眼就能看出consumer和provider的pojo版本不匹配,背后的本质一定是provider新增了字段或者修改了字段的类型。这时候消费者也应该增加新的字段或者修改字段类型与提供者保持一致。如果提供者不升级版本号,虽然提供者没有报错,但是提供者new字段的值为null(因为消费者缺少new字段),这个时候其实就有问题了,因为一般情况下provider添加了一个新字段。如果必须使用这个字段的值,那么这个值从哪里来呢?绝对是消费者。消费者是怎么来的?同样新增一个字段,并与provider版本保持一致即可解决问题。参考https://www.liaoxuefeng.com/w...JavaserialVersionUID的作用是什么?https://www.jianshu.com/p/91f...
