当前位置: 首页 > 后端技术 > Java

IO流的序列化和反序列化

时间:2023-04-01 18:39:29 Java

什么是序列化和反序列化?详见:IO流的序列化与反序列化-小健博客(ideaopen.cn)序列化:指堆内存对象的Java对象数据存储在磁盘文件中或传递给其他网络节点(传输到网络)以某种方式。这个过程称为序列化。通俗地说,反序列化就是将数据结构或对象转换为二进制字符串的过程:将磁盘文件中的对象数据或网络节点上的对象数据还原为Java对象模型的过程。也就是将序列化过程中产生的二进制字符串转换成数据结构或对象的过程好像有些不明确,所以我们简略一下。序列化:将对象转换为字节序列的过程称为对象的序列化。(常见的是保存文件)反序列化:将一个字节序列还原为一个对象的过程称为对象反序列化。不管它有多受欢迎。序列化:将对象写入IO流反序列化:从IO流中恢复对象为什么而生?我们要想学好一样东西,就必须了解它的作用和道理。为了节省时间,在博主的博客里找到了清晰明了的解释。由JavaBean和Serializeable程序创建的每个JavaBean类都实现了Serializeable接口。不知道大家看到这句话有没有感到疑惑呢?呵呵,没错,这个你可能没看懂。那么我们现在来说说这两件事。不过我这篇文章是连载的,肯定不会自己写解释的。这个太费时间了,所以我就截图了解下网站上的解释或者其他网络博主的解释。但是,我绝对不会选择我看不懂的文章。来看看知乎老大的描述吧!这就是我们所说的JavaBean的由来。也许你可以把它理解为一个概念而不是一个具体的东西。而我们想要一个能够实现JavaBean概念的类,我们需要具备以下条件。1.所有属性都是私有的2.提供默认构造方法3.提供getter和setter4.实现serializable接口这里我们发现了第二个问题,serializable,这是什么?这个东西是一个接口,这个接口有什么用呢?Serializable接口是一个没有实现任何方法的标记接口。一旦实现了这个接口,这个类的对象就可以序列化了。也就是说,它就像一个标记,里面没有你需要实现的东西。如果继承这个接口,就会给你一个标记,有这个标记的类就可以序列化了。但是,以上四个条件必须同时满足!!!我们写一段试试。演示这是一个满足序列化的类。我在这个类中定义了两个字段,最后rewrite返回一个String值。它具有与普通类相同的功能,但需要满足一些条件。packageIoDemo.Demo;importjava.io.Serializable;publicclassIoObjimplementsSerializable{privateStringstr;私人整数;//默认构造publicIoObj(){}publicIoObj(Stringstr,intnum){this.str=str;这个.num=num;}publicStringgetStr(){返回海峡;}publicvoidsetStr(Stringstr){this.str=str;}publicintgetNum(){返回数字;}publicvoidsetNum(intnum){this.num=num;}@OverridepublicStringtoString(){return"IoObj[str="+str+",num="+num+"]";如何序列化好,这里有一个支持序列化的类,我们现在来试试如何序列化这个类的对象。第一步:创建一个ObjectOutputStream输出流;第二步:调用ObjectOutputStream对象的writeObject输出一个可序列化的对象。对象是对象的意思。我们这里可以做的序列化和反序列化也可以叫做对象流。先看代码:packageIoDemo.Demo;importjava.io.*;publicclassIoDemoTest{publicstaticvoidmain(String[]args){IoObjioObj=newIoObj("SerializationTest",1);//创建对象流对象ObjectOutputStreamoos=null;try{//创建文件对象Filefile=newFile("D:\\test.txt");//创建文件输出流对象FileOutputStreamfos=newFileOutputStream(file);//创建对象流对象oos=newObjectOutputStream(fos);//写入对象oos.writeObject(ioObj);}catch(FileNotFoundExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}}}我在这里用了多少步?创建一个新对象IoObjioObj=newIoObj("序列化测试",1);写入文件的位置我们要写入对象到D:\\test.txt,所以需要用到File对象来保存地址。然后我们还需要将我们序列化后的内容写入到文件中,所以我们也必须创建文件的输出流。//创建文件对象Filefile=newFile("D:\\test.txt");//创建文件输出流对象FileOutputStreamfos=newFileOutputStream(file);serialize(createanobjectstreamobject)//创建一个对象流对象oos=newObjectOutputStream(fos);这里我们把上面的输出流对象交给object流对象。那么对象流对象有个方法,writeObject()方法,就是用来写对象的。//写入对象oos.writeObject(ioObj);这样,我们就把IoObj对象写入了文件,我们来看一下。注意:将对象序列化为文件时,标准Java惯例是为文件提供.ser扩展名。至于,你可能想知道,这篇文章的内容是什么?无法阅读。那就看看下面的解释就明白了。反序列化包IoDemo.Demo;importjava.io.*;publicclassIoDemoTest{publicstaticvoidmain(String[]args){IoObjioObj=newIoObj("序列化测试",1);//创建对象流ObjectObjectOutputStreamoos=null;try{//创建文件对象Filefile=newFile("D:\\test.txt");//读取文件ObjectInputStreamois=newObjectInputStream(newFileInputStream(file));//读取对象IoObjioObj1=(IoObj)ois.readObject();System.out.println(ioObj1.toString());}catch(FileNotFoundExceptione){e.printStackTrace();}抓住(IOExceptione){e。打印堆栈跟踪();}catch(ClassNotFoundExceptione){e.printStackTrace();}}}反序列化也是一样,就不多说了。ObjectInputStream是对象的写入流。//读取对象IoObjioObj1=(IoObj)ois.readObject();这里是将读取的值赋给对象,readObject()方法用于读取对象流文件的内容。至于(IoObj),可以理解为强制转移。外延这里有个大博主,我也借了他的文章,你可以看看。大佬的博文中有很多扩展的知识点,为了阅读方便我就搬到这里了,上面给出了版权链接。反序列化不调用构造函数。反序列化的对象是JVM自己生成的对象,不是通过构造方法生成的。成员按引用序列化如果可序列化类的成员不是基本类型或String类型,那么引用类型也必须是可序列化的;否则,此类将不可序列化。如果有这样一个成员:privatePersonperson多次序列化同一个对象的机制如果多次序列化同一个对象,那么这个对象会被多次序列化吗?答案是否定的。Java序列化同一个对象,不会多次序列化这个对象得到多个对象。Java序列化算法所有保存到磁盘的对象都有一个序列化编码号当程序试图序列化一个对象时,它会首先检查该对象是否已经被序列化,只有该对象从未被序列化过(在这个虚拟机中)才会这样对象被序列化为字节序列以供输出。如果对象已经序列化,直接输出数字即可。Java序列化算法的潜在问题由于Java序列化算法不会重复序列化同一个对象,它只记录序列化对象的编号。如果序列化一个可变对象(对象内部的内容是可以改变的)后,改变对象的内容,再次序列化,对象不会再次转换成字节序列,只会保存序列号。可选的自定义序列化有时候,我们有这样的需求,有些属性不需要序列化。使用transient关键字来选择不需要序列化的字段。publicclassPersonimplementsSerializable{//不需要序列化名称与年序privatetransientStringname;私有瞬态整数;私人高度;私有瞬态布尔单一性;publicPerson(Stringname,intage){this.name=name;this.age=年龄;}//省略get,set方法}publicclassTransientTest{publicstaticvoidmain(String[]args)throwsException{try(ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream("person.txt"));ObjectInputStreamiosObjectInput=new(newFileInputStream("person.txt"))){Person=newPerson("9龙",23);person.setHeight(185);System.out.println(人);oos.writeObject(人);Personp1=(Person)ios.readObject();System.out.println(p1);}}}//输出结果//Person{name='9龙',age=23',singlehood=true',height=185cm}//Person{name='null',age=0',singlehood=false',height=185cm}从输出中我们可以看出,java序列化时忽略了transient修饰的属性,所以反序列化后的对象,transient修饰的属性是默认值。对于引用类型,值为null;基本类型,值为0;boolean类型,值为false。两种序列化比较实现Serializable接口实现Externalizable接口系统自动存储必要的信息程序员决定存储哪些信息Java内置支持,易于实现,只需要实现接口,无需任何代码支持必须在接口中实现这两个方法性能稍差,性能稍好。Externalizable接口虽然带来了一定的性能提升,但是也增加了复杂度,所以序列化一般都是通过实现Serializable接口来实现的。综上所述,所有需要网络传输的对象都需要实现序列化接口,建议所有javaBean都实现Serializable接口。对象的类名和实例变量(包括基本类型、数组和对其他对象的引用)将被序列化;方法、类变量和瞬态实例变量不会被序列化。如果你想让一个变量不被序列化,使用瞬态修改。序列化对象的引用类型成员变量也必须是可序列化的,否则会报错。反序列化的时候,必须要有被序列化对象的类文件。通过文件或网络读取序列化对象时,必须按照实际写入的顺序读取。对于单例类的序列化,需要重写readResolve()方法;否则,单例原则将被破坏。同一个对象被多次序列化,只有第一次被序列化成二进制流,以后只保存序列号,不会重复序列化。建议所有可序列化的类都加上serialVersionUID版本号,方便项目升级。