大厂面试系列-如何回答面试序列化和反序列化相关问题

大厂面试系列-如何回答面试序列化和反序列化相关问题我们知道,在对象第一次被创建之后都是在Java堆内存中进行生成的,也就是说只有在JVM运行过程中,这些对象才是存在的,当JVM生命周期结束的时候

欢迎大家来到IT世界,在知识的湖畔探索吧!

大厂面试系列-如何回答面试序列化和反序列化相关问题

什么是序列化与反序列化

在Java中所谓的序列化操作就是将Java中的对象以一连串的字节码保存到磁盘文件中的过程,也就是说可以是可以保存Java对象的某个状态。通常完成序列化的数据将在磁盘上永久进行保存。

而反序列化的操作就是将保存在磁盘上的Java字节码重新转换成Java对象的过程。

Java中如何实现序列化和反序列化操作

在Java中创建对象的方式有很多中,并且在学习垃圾回收机制的时候我们知道只要是没有被回收的对象我们都可以对该对象进行重复的利用。我们知道,在对象第一次被创建之后都是在Java堆内存中进行生成的,也就是说只有在JVM运行过程中,这些对象才是存在的,当JVM生命周期结束的时候,这些对象都将不复存在。

在一些实际开发过程中,我们需要将一些存在于JVM运行期间的对象永久的保存下来,这样方便在需要的时候把这些对象重新读取出来并且加载到JVM中运行使用。这个时候我们就需要采用序列化的方式对该对象进行持久化的存储。

对于对象的序列化操作,在Java中是提供了原生的支持,也就是说在Java语言内部就提供了关于序列化的有关操作。通过对象序列化的方式可以把对象的某个状态保存到字节数组中,在需要的时候将这些字节数组中的内容读取出来并使用。

这就涉及到了如何将字节数组中的对象内容与JVM中的活动对象进行转化的操作。

在很多RPC框架中就是通过序列化和反序列化的方式将对象通过网络以及其他传输方式进行一个远程调用RMI的操作。而这些操作无非就是利用了上面所说的要让JVM运行的对象进行持久化操作的想法。

JDK提供的序列化操作接口

在JDK中提供了相关的序列化的接口来实现对于Java对象的序列化存储的操作。

  • java.io.Serializable
  • java.io.Externalizable
  • ObjectOutput
  • ObjectInput
  • ObjectOutputStream
  • ObjectInputStream

下面我们就来依次介绍一下这些接口相关的内容。

Serializable

Serializable接口是Java提供用来标记使用序列化功能的接口,会看到这个接口没有任何的方法,也就是说它所完成的主要的功能就是用来标记某个类是否可以进行序列化操作,只有实现了这个接口的类才可以被序列化,而没有实现这个接口的类则不能进行序列化操作。如果一个类是可以被序列化的类,那么它的子类也将会变成可被序列化的类。

public interface Serializable {
}

欢迎大家来到IT世界,在知识的湖畔探索吧!

如果在进行对象序列化的时候,对应的类不能被序列化的时候,这个时候就会抛出一个NotSerializableException异常。下面我们就来演示一个继承了Serializable接口实现的接口类的操作。

第一步、先来定一个继承了Serialzable接口的测试类

欢迎大家来到IT世界,在知识的湖畔探索吧!public class SerializableTest implements Serializable {
    private String id;
    private String name;
    private String param;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getParam() {
        return param;
    }

    public void setParam(String param) {
        this.param = param;
    }
}

第二步、编写测试主类来测试相关内容

public class SerializableDemo1 {
    public static void main(String[] args) {
        //Initializes The Object
        SerializableTest serializableTest = new SerializableTest();
        serializableTest.setId("1231231");
        serializableTest.setName("Test");
        serializableTest.setParam("HelloWorld!");
        System.out.println(serializableTest);

        //Write Obj to File
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
            oos.writeObject(serializableTest);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(oos);
        }

        //Read Obj from File
        File file = new File("tempFile");
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(file));
            SerializableTest newObject = (SerializableTest) ois.readObject();
            System.out.println(newObject);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(ois);
            try {
                FileUtils.forceDelete(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

运行程序会看到在控制台有相关对象内容输出。到这里我们的序列化和反序列的操作就完成了,接下来我们来看一下关于Externalizable 接口的相关内容

Externalizable接口详解

Externalizable 是Java提供的另外一种序列化接口,其源码如下。会看到Externalizable接口也是继承了Serializable接口只不过在接口中提供了两个扩展方法。

欢迎大家来到IT世界,在知识的湖畔探索吧!public interface Externalizable extends java.io.Serializable {
    /**
     * The object implements the writeExternal method to save its contents
     * by calling the methods of DataOutput for its primitive values or
     * calling the writeObject method of ObjectOutput for objects, strings,
     * and arrays.
     *
     * @serialData Overriding methods should use this tag to describe
     *             the data layout of this Externalizable object.
     *             List the sequence of element types and, if possible,
     *             relate the element to a public/protected field and/or
     *             method of this Externalizable class.
     *
     * @param out the stream to write the object to
     * @exception IOException Includes any I/O exceptions that may occur
     */
    void writeExternal(ObjectOutput out) throws IOException;

    /**
     * The object implements the readExternal method to restore its
     * contents by calling the methods of DataInput for primitive
     * types and readObject for objects, strings and arrays.  The
     * readExternal method must read the values in the same sequence
     * and with the same types as were written by writeExternal.
     *
     * @param in the stream to read data from in order to restore the object
     * @exception IOException if I/O errors occur
     * @exception ClassNotFoundException If the class for an object being
     *              restored cannot be found.
     */
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

使用Externalizable 接口来进行序列化对象的时候,开发人员需要重写这两个方法,而这两个方法分别是读取了写入操作的扩展。

public class SerializableTest implements Externalizable {
    private String id;
    private String name;
    private String param;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getParam() {
        return param;
    }

    public void setParam(String param) {
        this.param = param;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws
    IOException {
        
    }

    @Override
    public void readExternal(ObjectInput in) throws 
                             IOException, ClassNotFoundException {

    }
}

实现对应操作内容,当然这里只是一个演示示例在实际开发过程中,要对流操作,对象存储操作等做好合理的安排。

public class ExternalizableDemo1 {
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //Write Obj to file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        SerializableTest serializableTest = new SerializableTest();
        serializableTest.setId("1231231");
        serializableTest.setName("Test");
        serializableTest.setParam("HelloWorld!");
        oos.writeObject(serializableTest);
        //Read Obj from file
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        SerializableTest newInstance = (SerializableTest) ois.readObject();
        //output
        System.out.println(newInstance);
    }
}

执行完上面的操作之后,会看到输出结果中并没有将我们设置的属性值进行存储,那是因为,在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。并且必须要实现writeExternal和readExternal方法。如下,我们可以再次运行代码就会看到属性值也会被保存下来。

public void writeExternal(ObjectOutput out) throws IOException {
    out.writeObject(name);
    out.writeInt(age);
}

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    name = (String) in.readObject();
    age = in.readInt();
}

Serializable 和 Externalizable 有什么不同

通过查看源码我们知道其实Externalizable接口也是继承了Serializable接口来实现序列化操作,而前面内容中我们也介绍了Serializable接口实际上就是用来标记这个类是否可以被序列化或者是反序列化。

而Externalizable在继承了Serializable接口的基础上又定义了两个方法writeExternal和readExternal,当我们继承Externalizable接口进行序列化的时候,我们需要重新编写这两个方法中的内容,否则就会的到一个空对象。而在Externalizable接口的时候需要序列化的对象又必须去提供一个无参构造。并且在writeExternal和readExternal方法中对指定的属性进行序列化指定。

总结

整个的Java序列化的过程其实是为我们提供了一种新的Java扩展机制,是为了更好的整合各种资源使得开发效率得到更好的提升。并且现在的很多框架中对序列化和反序列化的使用也是非常广泛的。同时开发人员还定义了自己的序列化和反序列化的规则。极大的提升了代码安全以及开发效率。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/43151.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信