当前位置: 首页 > 科技观察

面试官:能谈谈序列化和反序列化吗?它是如何实现的?在什么场景下需要它?

时间:2023-03-20 00:11:38 科技观察

序列化和反序列化是Java最基础的知识点,很容易被大家遗忘。虽然它们每天都在使用,但它们可能并没有被清楚地解释。相信很多朋友只有几个概念和关键词(Serializable)。如果你问序列化和反序列化是如何实现的,使用场景等等,你可能会一头雾水。每次作为面试官考察Java基础,一般都会问序列化和反序列化的知识点,衡量Java基础。当问到什么是Java序列化?什么是反序列化?会在什么场景下使用?如果不用,会出现什么问题等等,一般大家的回答只是一些简单的概念,有的已经工作了好几年。大多数应聘者甚至无法清楚地解释这个概念,他们看起来很无聊。本文对序列化和反序列化进行了深入的讨论。当别人问起时,你不会感到无聊或尴尬。它可能会为您未来的工作面试增加一点影响力。一、基本概念1、什么是序列化和反序列化序列化是指将Java对象转换为字节序列的过程,而反序列化是将字节序列转换为Java对象的过程。Java对象序列化是将一个实现了Serializable接口的对象转化为一个字节序列,可以通过网络传输、文件存储等方式进行传输。在传输过程中,无需担心数据在不同机器之间发生变化,环境,并且不需要关心字节的顺序或任何其他细节,并且能够在以后将这个字节序列完全还原为原始对象(这个还原过程称为反序列化)。对象的序列化非常有趣,因为它可以用来实现轻量级的持久化。“持久化”是指一个对象的生命周期不仅仅取决于程序是否在运行,而是在程序调用之间能够存活。对象持久化是通过将一个序列化的对象写入磁盘,然后在程序被调用时恢复该对象来实现的。本质上,序列化是将实体对象的状态按照一定的格式写入有序的字节流中,反序列化是从有序的字节流中重建对象,恢复对象状态。2、为什么需要使用序列化和反序列化我们知道,不同的进程/程序在进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等,而这些数据会被以二进制序列形式在网络上传输。那么当两个Java进程进行通信时,能否实现进程间的对象传递呢?当然有可能!怎么做?这需要使用Java序列化和反序列化。发送方需要将这个Java对象转换成字节序列,然后在网络上传输,接收方需要从字节序列中还原出Java对象。在我们明白了为什么要使用Java序列化和反序列化之后,自然而然的就会想到Java序列化的好处:实现数据的持久化,通过序列化可以将数据永久保存到硬盘中(如:存储在文件中),实现对象的永久存储。利用序列化进行远程通信,即通过网络传输对象的能力。2.如何实现Java序列化和反序列化只要对象实现了Serializable和Externalizable接口(接口只是一个标记接口,不包含任何方法),对象就被序列化了。1、具体是如何实现的?序列化,首先创建一些OutputStream对象,然后封装在一个ObjectOutputStream对象中,然后调用writeObject()方法将对象序列化发送给OutputStream(对象序列化是基于字节的,所以InputStream和OutputStream继承的类是用过的)。反序列化,即反向序列化的过程,需要将一个InputStream封装在一个ObjectInputStream对象中,然后调用readObject()方法获取一个对象引用(它指向一个向上转化的Object),然后进行类型强制转换得到物体。假设一个User类,它的对象需要序列化,有以下三种方法:(1)如果User类只实现了Serializable接口,可以通过以下方式进行序列化和反序列化。ObjectOutputStream使用默认的序列化方法序列化User对象的非瞬态实例变量。ObjcetInputStream使用默认的反序列化方法反序列化User对象的非瞬态实例变量。(2)如果User类只实现了Serializable接口,还定义了readObject(ObjectInputStreamin)和writeObject(ObjectOutputSteamout),按以下方式进行序列化和反序列化。ObjectOutputStream调用User对象的writeObject(ObjectOutputStreamout)方法进行序列化。ObjectInputStream会调用User对象的readObject(ObjectInputStreamin)方法进行反序列化。(3)如果User类实现了Externalizable接口,并且User类必须实现readExternal(ObjectInputin)和writeExternal(ObjectOutputout)方法,按如下方式进行序列化和反序列化。ObjectOutputStream调用User对象的writeExternal(ObjectOutputout)方法进行序列化。ObjectInputStream会调用User对象的readExternal(ObjectInputin)方法进行反序列化。java.io.ObjectOutputStream:对象输出流,其writeObject(Objectobj)方法可以序列化指定的obj对象,并将得到的字节序列写入一个目标输出流。java.io.ObjectInputStream:对象输入流,它的readObject()方法可以从输入流中读取字节序列,反序列化为一个对象,并返回。2.序列化和反序列化的例子为了更好的理解序列化和反序列化的过程,举个例子如下:");ObjectOutputStreamoos=newObjectOutputStream(fos);Useruser1=newUser("xcbeyond","123456789");oos.writeObject(user1);oos.flush();oos.close();//反序列化FileInputStreamfis=newFileInputStream("object.txt");ObjectInputStreamois=newObjectInputStream(fis);Useruser2=(User)ois.readObject();System.out.println(user2.getUsername()+","+user2.getPassword());}}//对象User实现了Serializable接口publicclassUserimplementsSerializable{privateStringusername;privateStringpassword;……}3、哪些场景需要序列化?当你想将内存中的对象状态保存到文件或数据库中时。当您想使用套接字通过网络传输对象时。当你想通过RMI传输对象时。三、注意事项1、当父类实现序列化时,子类会自动实现序列化,无需显式实现Serializable接口。2.当一个对象的实例变量引用其他对象时,在对象被序列化的同时,被引用的对象也被序列化。3.并不是所有的对象都可以序列化,比如:安全原因,比如一个对象有private、public等成员变量,对于一个对象要传递,比如写入文件,或者进行RMI传输等,在序列化和传输的过程中,这个对象的private和其他字段没有被保护;由于资源分配的原因,比如socket、thread类,如果可以序列化、传输或保存,就不能再进行资源分配,而且,也不是必须要实现的。4.声明为static和transient的成员变量不能被序列化。因为static代表类的状态,transient代表对象的临时数据。5.序列化运行时将使用一个称为serialVersionUID的版本号,它与每个可序列化类相关联。这个序列号用来验证序列化对象的发送方和接收方是否在反序列化过程中。为此对象加载了一个与序列化兼容的类。如果接收方使用与相应发送方的类版本号不同的serialVersionUID加载此对象的类,则反序列化将导致InvalidClassException。可序列化类可以通过声明名为“serialVersionUID”的字段(必须是静态的最终长字段)来显式声明自己的serialVersionUID。如果序列化类未显式声明serialVersionUID,则序列化运行时会根据类的各个方面计算该类的默认serialVersionUID值,如“Java(TM)对象序列化规范”中所述。但是,强烈建议所有可序列化的类显式声明serialVersionUID值,因为默认serialVersionUID的计算对类的细节高度敏感,并且可能因编译器实现的不同而有很大差异,因此在反序列化过程中可能导致意外的InvalidClassException。因此,为了保证serialVersionUID值在不同java编译器实现之间的一致性,序列化器类必须声明一个显式的serialVersionUID值。还强烈建议(如果可能)使用private修饰符显式声明serialVersionUID,因为此类声明仅适用于直接声明类——serialVersionUID字段作为继承成员没有用处。数组类不能声明显式的serialVersionUID,因此它们总是有一个默认的计算值,但数组类没有匹配serialVersionUID值的要求。6、Java中有很多基础类已经实现了serializable接口,比如String,Vector等,但是也有一些没有实现serializable接口。7、如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存!这也是为什么可以用序列化来解决深拷贝的一个重要原因。有了上面关于序列化和反序列化的详细介绍,大家平时使用的序列化和反序列化是怎么实现的,又会在什么场景下使用,是不是比较深刻呢?