您好,欢迎访问三七文档
当前位置:首页 > 临时分类 > java 序列化原理机制浅谈
java序列化原理机制浅谈什么是序列化java中的序列化(serialization)机制能够将一个实例对象的状态信息写入到一个字节流中,使其可以通过socket进行传输、或者持久化存储到数据库或文件系统中;然后在需要的时候,可以读取字节流中的信息来重构一个相同的对象。序列化机制在java中有着广泛的应用,EJB、RMI、hessian等技术都是以此为基础的。so,序列化一般用于以下场景:1:永久性保存对象,保存对象的字节序列到本地文件或者数据库中2:通过序列化以字节流的形式使对象在网络中进行传递和接收;3:通过序列化在进程间传递对象。接下来我们将从序列化机制原理等方面进行剖析如何序列化一个对象类通过实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。1:我们先来看看一个例子将对象序列化为一个字节流我们将对象序列化并输出。ObjectOutputStream能把Object输出成Byte流。---上图显示的20行-26行我们将Byte流暂时存储到temp.out文件里。而在33行-39行,我们利用反序列化,根据字节流重建对象。从上面代码中,我们不难看出,序列化和反序列化的俩个主要类:ObjectOutputStream、ObjectInputStream。序列化后的内容通过上面的例子我们知道,对象被序列化存在至文件temp.out中,我们可以通过工具UltraEdit以16进制方式打开该文件,看看它里面是以什么样的形式存在组织我们的对象的。每一行分号后面的内容都是前面16进制码的注释。对于这些我们先随意YY下,这些内容的组织方式应该是:1:对象类型描述2:对象属性类型描述3:对象属性值现在我们将以一个全面的例子来说明该问题:代码-1.1[javascript]viewplaincopy1.classparentimplementsSerializable{2.intparentVersion=10;3.}4.classcontainimplementsSerializable{5.intcontainVersion=11;6.}7.publicclassSerialTestextendsparentimplementsSerializable{8.intversion=66;9.containcon=newcontain();10.publicintgetVersion(){11.returnversion;12.}13.publicstaticvoidmain(Stringargs[])throwsIOException{14.FileOutputStreamfos=newFileOutputStream(temp_1.out);15.ObjectOutputStreamoos=newObjectOutputStream(fos);16.SerialTestst=newSerialTest();17.oos.writeObject(st);18.oos.flush();19.oos.close();20.}21.}temp_1.out文件内容如下:先对上面图简单注释下:0000000h-000000c0h表示行号(我们一次可以称为第一行,第二行…………),0~f表示列;行后面的文字表示对这行16进制的解释。我们来仔细看看这些字节都是些说明东西。第一行的:0列-4列1.ACED:STREAM_MAGIC.声明使用了序列化协议.可以理解为实现了Serializable类2.0005:STREAM_VERSION.序列化协议版本.3.0x73:TC_OBJECT.声明这是一个新的对象.因此第一步存储的就是序列化的描述。二:输出SerialTest类的描述:第一行的5列到三行的7列1:4.0x72:TC_CLASSDESC.声明这里开始一个新Class。2.0015:Class名字的长度(包括package名称:如本例就是:testSerial.SerialTest;正好21的长度,16进制表示就为15了).3.7465737453657269616C2E53657269616c54657374表示testSerial/SerialTest类的全限定名4.第二行的D列到3行的4列表示:SerialVersionUID,序列化ID,如果没有指定,则会由算法随机生成一个8byte的ID.5.0x02:标记号.该值声明该对象支持序列化。6.0002:该类所包含的域个数。三:接下来,输出其中的一个域(一次从上到下父类到子类),intversion=66;3行8列-4行1列1.0x49:域类型.49代表I,也就是Int.2.0007:域名字的长度.3.76657273696F6E:version,域名字描述.四:另外一个域,containcon=newcontain();这个有点特殊,是个对象。?描述对象类型引用时需要使用JVM的标准对象签名表示法,4行2列-5行f列1:0x4C:域的类型.2:0003:域名字长度.3:636F6E:域名字描述,con4:0x74:TC_STRING.代表一个newString.用String来引用对象。5:0014:该String长度.既:LtestSerial/contain的长度6:4行b-5行e列:LtestSerial/contain;,JVM的标准对象签名表示法.6:0x78:TC_ENDBLOCKDATA,对象数据块结束的标志五:Parent类描述了6行的0列到7行的e列1:0x72:TC_CLASSDESC.声明这个是个新类.2:0011:类名长度.3:6行3列-7行3列:testSerial.parent,类名描述。4:7行4列-7行b列:SerialVersionUID,序列化ID.5:0x02:标记号.该值声明该对象支持序列化.6:0001:类中域的个数.六:parent类的域描述,intparentVersion=1007行f列-9行0列1:0x49:域类型.49代表I,也就是Int.2:000D:域名字长度.3:8行2列-8行e列:parentVersion,域名字描述。4:0x78:TC_ENDBLOCKDATA,对象块结束的标志。5:0x70:TC_NULL,说明没有其他超类的标志。七:到此为止,算法已经对所有的类的描述都做了输出。下一步就是把实例对象的实际值输出了。这时候是从parentClass的域开始的,9行一列-9行4列0000000A:10,parentVersion域的值.八:还有SerialTest类的域:9行5列-9行8列00000042:66,version域的值.九:再往后的bytes比较有意思,算法需要描述contain类的信息1:0x73:TC_OBJECT,声明这是一个新的对象.2:0x72:TC_CLASSDESC声明这里开始一个新Class.3:0012:类名的长度.4:9行d列-10行e列:TestSerial.contain,类名描述.5:10行f列-11行6列:SerialVersionUID,序列化ID.6:0x02:Variousflags.标记号.该值声明该对象支持序列化7:0001:类内的域个数。十:.输出contain的唯一的域描述,intcontainVersion=111:0x49:域类型.49代表I,也就是Int..2:000E:域名字长度.3:636F6E7461696E56657273696F6E:containVersion,域名字描述.4:0x78:TC_ENDBLOCKDATA对象块结束的标志.十一:这时,序列化算法会检查contain是否有超类,如果有的话会接着输出。0x70:TC_NULL,没有超类了。十二:最后,将contain类实际域值输出。0000000B:11,containVersion的值.总结下序列化后二进制流中按哪些顺序存储哪些内容:◆当前类描述◆当前类属性描述◆超类描述◆超类属性描述(如果超类还有超类,则依次递归)◆超类属性值描述◆子类属性值描述既是:类描述是从下到上,类属性描述是从上到下。俩个是反着来的。transient和static的类变量的序列化我们现在回过去看看代码1.1。然后修成下:如下---代码1.2[java]viewplaincopy1.省略前面:2.3.publicclassSerialTestextendsparentimplementsSerializable{4.5.intversion=66;6.publicstaticinttestStatic=1;7.publictransientinttestTransient=2;8.9.containcon=newcontain();10.11.publicintgetVersion(){12.13.returnversion;14.15.}16.…………………………………………17.}类SerialTest新增了俩个变量publicstaticinttestStatic=1;publictransientinttestTransient=2;然后再看看重新生成的temp_1.out文件内容,我们惊奇发现文件内容和没有新增变量之前是一模一样的。so,我们推理static和transient变量被序列化的时候,并不会序列化。现在哥们我用别人的总结来总结自己的序列化时,类的所有数据成员应可序列化除了声明为transient或static的成员。将变量声明为transient告诉JVM我们会负责将变元序列化。将数据成员声明为transient后,序列化过程就无法将其加进对象字节流中,没有从transient数据成员发送的数据。后面数据反序列化时,要重建数据成员(因为它是类定义的一部分),但不包含任何数据,因为这个数据成员不向流中写入任何数据。记住,对象流不序列化static或transient。我们的类要用writeObject()与readObject()方法以处理这些数据成员。使用writeObject()与readObject()方法时,还要注意按写入的顺序读取这些数据成员那对于这些问题,我们该如何进行序列化和反序列化呢?简单,也就是说我们要对这俩个类型的变量单独处理,怎么办?就是在出现这类变量的所属类中增加俩个方法[java]viewplaincopy1.privatevoidwriteObject(java.io.ObjectOutputStreamout)throwsIOException2.privatevoidreadObject(java.io.ObjectInputStreamin)throwsIOException,ClassNotFoundException;而对应于我们的类中添加的方法就是[java]viewplaincopy1.publicclassSerialTestextendsparentimplementsSerializable{2.//省略3.privatevoidwriteObject(ObjectOutputStreamout)throwsIOException{4.out.defaultWriteObject();5.out.writeInt(this.testStatic);6.out.writeInt(this.testTransient);7.}8.privatevoidreadObject(ObjectInputStreamin)throwsIOException,ClassNotFoundException{9.in.defaultReadObject();10.this.testStatic=in.readInt();11.this.testTransient=in.readInt();12.}13
本文标题:java 序列化原理机制浅谈
链接地址:https://www.777doc.com/doc-7845674 .html