📜  Java序列化中Serializable和Externalizable的区别

📅  最后修改于: 2021-09-10 03:05:24             🧑  作者: Mango

将对象的状态写入文件的过程称为序列化,但严格来说,是将对象从Java支持的形式转换为文件支持的形式或网络支持的形式的过程。通过使用 fileOutputStream 和 objectOutputStream 类,我们可以实现序列化。

但是我们只能序列化可序列化的对象。当且仅当相应的类实现了 Serializable 接口时,才说对象是可序列化的。 Java.io 包中存在的可序列化接口,它不包含任何方法,因此它是一个标记接口。如果我们试图序列化一个不可序列化的对象,那么我们会得到运行时异常,提示 notSerializableException。

例子:

Java
// Java Program to illustrate Serializable
 
// Importing utility classes
// Importing input output classes
import java.io.*;
import java.util.*;
 
// Main Class
// Class implementing serializable interface
class serializableDemo implements Serializable {
    // Member variables of this class
    String name;
    int age;
    int jobId;
 
    // Default constructor
    public serializableDemo(String name, int age, int jobId)
    {
        // This keyword is used to refer
        // current object instance
        this.name = name;
        this.age = age;
        this.jobId = jobId;
    }
 
    // Method 2
    // Main driver method
    public static void main(String[] args) throws Exception
    {
 
        // Creating an object of class in main() method
        serializableDemo t1
            = new serializableDemo("Ram", 34, 2364);
 
        // Serialization
 
        // Saving of object in a file
        FileOutputStream fos
            = new FileOutputStream("abc1.ser");
        ObjectOutputStream oos
            = new ObjectOutputStream(fos);
 
        // Method for serialization of object
        oos.writeObject(t1);
 
        System.out.println("Object has been serialized");
        // Deserialization
 
        // Reading the object from a file
        FileInputStream fis
            = new FileInputStream("abc1.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
 
        // Method for deserialization of object
        serializableDemo t2
            = (serializableDemo)ois.readObject();
 
        // Display message only
        System.out.println("Object has been deserialized ");
 
        // Print and display the name and age
        // to illustrate Serializable
        System.out.println("Name:" + t2.name + "\n"
                           + "Age:" + t2.age + "\n"
                           + t2.jobId);
    }
}


Java
// Java Program to illustrate Externalizable
 
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.*;
 
// Main Class
// Class implementing externalizable class
public class ExternalizableDemo implements Externalizable {
 
    // Member variables of this class
    String name;
    int age;
    int jobId;
 
    // Constructors of this class
 
    // Constructor 1
    // No-argument constructor
    public ExternalizableDemo()
    {
 
        // Display message
        System.out.println(
            "Public no-argument constructor");
    }
 
    // Constructor 2
    // Default constructor
    public ExternalizableDemo(String name, int age,
                              int jobId)
    {
 
        // This keyword refers to current object itself
        this.name = name;
        this.age = age;
        this.jobId = jobId;
    }
 
    // Implementing write external method
    public void writeExternal(ObjectOutput out)
        throws IOException
    {
        // Writing name and age to file
        out.writeObject(name);
        out.writeInt(age);
    }
 
    // Implementing readExternal method
    public void readExternal(ObjectInput in)
        throws IOException, ClassNotFoundException
    {
        // Reading name from file
        name = (String)in.readObject();
 
        // Reading age from file
        age = in.readInt();
    }
 
    // Main method
    public static void main(String[] args) throws Exception
    {
 
        // Creating an object of type ExternalizableDemo
        ExternalizableDemo t1
            = new ExternalizableDemo("Ram", 35, 23675);
 
        // Serialization of object
        FileOutputStream fos
            = new FileOutputStream("abc.ser");
 
        ObjectOutputStream oos
            = new ObjectOutputStream(fos);
 
        oos.writeObject(t1);
 
        // Deserlization
        FileInputStream fis
            = new FileInputStream("abc.ser");
 
        ObjectInputStream ois = new ObjectInputStream(fis);
 
        ExternalizableDemo t2
            = (ExternalizableDemo)ois.readObject();
 
        // Display message
        System.out.println("Name :"
                           + " " + t2.name + " "
                           + "Age :"
                           + " " + t2.age);
    }
}


输出:

Object has been serialized
Object has been deserialized  
Name:Ram
Age:34  
2364  

可外化

在序列化中,一切都由 JVM 处理,程序员没有任何控制权。在序列化中,总是可以将整个对象解决到文件中,而无法保存可能会产生性能问题的部分对象。为了克服这个问题,我们应该去外化。

序列化的主要优点是一切都由程序员负责,JVM 没有任何控制权。根据我们的要求,我们可以保存整个对象或部分对象,从而提高系统的性能。要为任何强制Java对象提供可泛化能力,相应的类应该实现可泛化接口。

该接口定义了如下两个方法:

方法一

public void  writeExternal( ObjectIOutput obj ) throws IOException

这个方法会在序列化的时候自动执行,这个方法内我们要写代码把需要的变量保存到文件中。

方法二

public void  readExternal(ObjectInput  in )throws IOException , ClassNotFoundException

这个方法会在反序列化的时候自动执行。在这个方法中,我们必须编写代码从文件中读取所需的变量并将其分配给当前对象。但严格来说,在反序列化时,JVM 将通过执行公共无参数构造函数来创建一个单独的新对象,然后在该对象上,JVM 将调用readExternal方法。

因此,每个 Externalizable 实现的类都应该强制包含一个公共的无参数构造函数,否则我们会得到运行时异常,说 InvalidClassException。

例子:

Java

// Java Program to illustrate Externalizable
 
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.*;
 
// Main Class
// Class implementing externalizable class
public class ExternalizableDemo implements Externalizable {
 
    // Member variables of this class
    String name;
    int age;
    int jobId;
 
    // Constructors of this class
 
    // Constructor 1
    // No-argument constructor
    public ExternalizableDemo()
    {
 
        // Display message
        System.out.println(
            "Public no-argument constructor");
    }
 
    // Constructor 2
    // Default constructor
    public ExternalizableDemo(String name, int age,
                              int jobId)
    {
 
        // This keyword refers to current object itself
        this.name = name;
        this.age = age;
        this.jobId = jobId;
    }
 
    // Implementing write external method
    public void writeExternal(ObjectOutput out)
        throws IOException
    {
        // Writing name and age to file
        out.writeObject(name);
        out.writeInt(age);
    }
 
    // Implementing readExternal method
    public void readExternal(ObjectInput in)
        throws IOException, ClassNotFoundException
    {
        // Reading name from file
        name = (String)in.readObject();
 
        // Reading age from file
        age = in.readInt();
    }
 
    // Main method
    public static void main(String[] args) throws Exception
    {
 
        // Creating an object of type ExternalizableDemo
        ExternalizableDemo t1
            = new ExternalizableDemo("Ram", 35, 23675);
 
        // Serialization of object
        FileOutputStream fos
            = new FileOutputStream("abc.ser");
 
        ObjectOutputStream oos
            = new ObjectOutputStream(fos);
 
        oos.writeObject(t1);
 
        // Deserlization
        FileInputStream fis
            = new FileInputStream("abc.ser");
 
        ObjectInputStream ois = new ObjectInputStream(fis);
 
        ExternalizableDemo t2
            = (ExternalizableDemo)ois.readObject();
 
        // Display message
        System.out.println("Name :"
                           + " " + t2.name + " "
                           + "Age :"
                           + " " + t2.age);
    }
}

输出:

Public no-argument constructor
Name : Ram Age : 35

现在,我们已经完成了对它们的讨论,让我们总结一下表格中的所有差异,以便更好地理解。

                          Serializable                                        Externalizable
A serializable interface is used to implement serialization.  An externalizable interface used to implement Externalization
Serializable is a marker interface i.e. it does not contain any method. The externalizable interface is not a marker interface and thus it defines two methods writeExternal() and readExternal().
Serializable interface passes the responsibility of serialization to JVM and the programmer has no control over serialization, and it is a default algorithm. The externalizable interface provides all serialization responsibilities to a programmer and hence JVM has no control over serialization.
Serialization using a serializable interface has bad performance. Serialization using an externalizable interface has better performance.
Default serialization does not require any no-arg constructor.  A public no-arg constructor is required while using an Externalizable interface.
It is hard to analyze and modify class structure because any change in structure may break serialization. It is relatively easy to analyze and modify class structure because of complete control over serialization logic.
Using a serializable interface we save the total object to a file, and it is not possible to save part of the object. Base on our requirements we can save either the total object or part of the object.
Transient keyword play an important role here. Transient keyword won’t play any role.