Java中可序列化和可外部化的区别是什么?


序列化使用某些默认行为来存储对象并稍后重新创建对象。您可以指定以何种顺序或如何处理引用和复杂的数据结构,但最终还是要为每个基本数据字段使用默认行为。

在极少数情况下使用外部化,您确实希望以完全不同的方式存储和重新构建对象,并且不使用数据字段的默认序列化机制。例如,假设您有自己独特的编码和压缩方案。


https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html

默认序列化有点冗长,并且假定序列化对象的使用场景尽可能广泛,因此默认格式(Serializable)用关于序列化对象的类的信息注释结果流。

外部化使对象流的生产者能够完全控制精确的类元数据(如果有的话),而不仅仅是类所需的最小标识(例如它的名称)。这在某些情况下显然是可取的,比如在封闭环境中,对象流的生产者和消费者(从流中具体化对象)是匹配的,关于类的额外元数据没有任何作用,而且会降低性能。

此外(正如Uri指出的那样)外部化还提供了对与Java类型对应的流中数据编码的完全控制。对于(一个人为的)例子,您可能希望将布尔值true记录为“Y”,将false记录为“N”。外部化可以让你做到这一点。


要添加到其他答案,请实现java.io。Serializable,您可以获得类对象的“自动”序列化功能。不需要实现任何其他逻辑,它就可以工作了。Java运行时将使用反射来确定如何封送和解封送对象。

In earlier version of Java, reflection was very slow, and so serializaing large object graphs (e.g. in client-server RMI applications) was a bit of a performance problem. To handle this situation, the java.io.Externalizable interface was provided, which is like java.io.Serializable but with custom-written mechanisms to perform the marshalling and unmarshalling functions (you need to implement readExternal and writeExternal methods on your class). This gives you the means to get around the reflection performance bottleneck.

在Java的最新版本(当然是1.3以后)中,反射的性能比以前好得多,所以这不是一个大问题。我怀疑在现代JVM中,您很难从Externalizable中获得有意义的好处。

此外,内置的Java序列化机制并不是唯一的,您可以获得第三方替代机制,例如JBoss serialization,它要快得多,并且是默认的替代机制。

Externalizable一个很大的缺点是你必须自己维护这个逻辑——如果你在你的类中添加、删除或改变一个字段,你必须改变你的writeExternal/ readeexternal方法来解释它。

总之,Externalizable是Java 1.1时代的遗留物。真的没有必要了。


在考虑提高性能的选项时,不要忘记自定义序列化。您可以免费让Java做它擅长的事情,或者至少做得足够好,并为它做得不好的事情提供自定义支持。这通常比完全的外部化支持少得多。


实际上并没有提供Externalizable接口来优化序列化进程的性能!而是提供实现您自己的自定义处理的方法,并为对象及其超类型提供对流的格式和内容的完全控制!

这方面的例子是AMF (ActionScript Message Format)远程处理的实现,通过网络传输本机操作脚本对象。


序列化提供了存储对象和稍后重新创建对象的默认功能。它使用详细格式来定义要存储的对象的整个图,例如,假设你有一个linkedList,你像下面这样编码,那么默认的序列化将发现所有被链接的对象并将序列化。在默认的序列化中,对象完全由其存储的位构造,没有构造函数调用。

  ObjectOutputStream oos = new ObjectOutputStream(
      new FileOutputStream("/Users/Desktop/files/temp.txt"));
  oos.writeObject(linkedListHead); //writing head of linked list
  oos.close();

但是如果你想要限制序列化或者不希望对象的某些部分被序列化,那么就使用Externalizable。Externalizable接口扩展了Serializable接口,并添加了两个方法writeExternal()和readeexternal()。在序列化或反序列化时自动调用这些函数。在使用Externalizable时,我们应该记住默认构造函数应该是公共的,否则代码将抛出异常。请遵循以下代码:

public class MyExternalizable implements Externalizable
{

private String userName;
private String passWord;
private Integer roll;

public MyExternalizable()
{
}

public MyExternalizable(String userName, String passWord, Integer roll)
{
    this.userName = userName;
    this.passWord = passWord;
    this.roll = roll;
}

@Override
public void writeExternal(ObjectOutput oo) throws IOException 
{
    oo.writeObject(userName);
    oo.writeObject(roll);
}

@Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException 
{
    userName = (String)oi.readObject();
    roll = (Integer)oi.readObject();
}

public String toString()
{
    StringBuilder b = new StringBuilder();
    b.append("userName: ");
    b.append(userName);
    b.append("  passWord: ");
    b.append(passWord);
    b.append("  roll: ");
    b.append(roll);
   
    return b.toString();
}
public static void main(String[] args)
{
    try
    {
        MyExternalizable m  = new MyExternalizable("nikki", "student001", 20);
        System.out.println(m.toString());
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
        oos.writeObject(m);
        oos.close();
        
        System.out.println("***********************************************************************");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
        MyExternalizable mm = (MyExternalizable)ois.readObject();
        mm.toString();
        System.out.println(mm.toString());
    } 
    catch (ClassNotFoundException ex) 
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
    catch(IOException ex)
    {
        Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
    }
}
}

在这里,如果你注释了默认构造函数,那么代码将抛出以下异常:

 java.io.InvalidClassException: javaserialization.MyExternalizable;     
 javaserialization.MyExternalizable; no valid constructor.

我们可以观察到,由于密码是敏感信息,所以我没有在writeExternal(ObjectOutput oo)方法中序列化它,也没有在readexterexternal (ObjectInput oi)中设置相同的值。这就是Externalizable提供的灵活性。

上述代码的输出如下所示:

userName: nikki  passWord: student001  roll: 20
***********************************************************************
userName: nikki  passWord: null  roll: 20

我们可以观察到,因为我们没有设置passWord的值,所以它是空的。

通过将密码字段声明为瞬态,也可以实现同样的效果。

private transient String passWord;

希望能有所帮助。如果我犯了错误,我向你道歉。谢谢。


Serializable和Externalizable之间的主要区别

Marker interface: Serializable is marker interface without any methods. Externalizable interface contains two methods: writeExternal() and readExternal(). Serialization process: Default Serialization process will be kicked-in for classes implementing Serializable interface. Programmer defined Serialization process will be kicked-in for classes implementing Externalizable interface. Maintenance: Incompatible changes may break serialisation. Backward Compatibility and Control: If you have to support multiple versions, you can have full control with Externalizable interface. You can support different versions of your object. If you implement Externalizable, it's your responsibility to serialize super class public No-arg constructor: Serializable uses reflection to construct object and does not require no arg constructor. But Externalizable demands public no-arg constructor.

更多细节请参考Hitesh Garg的博客。


There are so many difference exist between Serializable and Externalizable but when we compare difference between custom Serializable(overrided writeObject() & readObject()) and Externalizable then we find that custom implementation is tightly bind with ObjectOutputStream class where as in Externalizable case , we ourself provide an implementation of ObjectOutput which may be ObjectOutputStream class or it could be some other like org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream

如果是Externalizable接口

@Override
public void writeExternal(ObjectOutput out) throws IOException {
    out.writeUTF(key);
    out.writeUTF(value);
    out.writeObject(emp);
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    this.key = in.readUTF();
    this.value = in.readUTF();
    this.emp = (Employee) in.readObject();
}





**In case of Serializable interface**


        /* 
             We can comment below two method and use default serialization process as well
             Sequence of class attributes in read and write methods MUST BE same.
        // below will not work it will not work . 
        // Exception = java.io.StreamCorruptedException: invalid type code: 00\
              private void writeObject(java.io.ObjectOutput stream) 
              */
            private void writeObject(java.io.ObjectOutputStream Outstream)
                    throws IOException {

                System.out.println("from writeObject()");
                /*     We can define custom validation or business rules inside read/write methods.
 This way our validation methods will be automatically 
    called by JVM, immediately after default serialization 
    and deserialization process 
    happens.
                 checkTestInfo();
                */

                stream.writeUTF(name);
                stream.writeInt(age);
                stream.writeObject(salary);
                stream.writeObject(address);
            }

            private void readObject(java.io.ObjectInputStream Instream)
                    throws IOException, ClassNotFoundException {
                System.out.println("from readObject()");
                name = (String) stream.readUTF();
                age = stream.readInt();
                salary = (BigDecimal) stream.readObject();
                address = (Address) stream.readObject();
                // validateTestInfo();
            }

为了更好地解释,我添加了示例代码。请检查Externalizable的入/出对象情况。它们不直接绑定到任何实现。 其中Outstream/ stream与类紧密绑定。我们可以扩展ObjectOutputStream/ObjectInputStream,但使用起来有点困难。


对象序列化使用Serializable和Externalizable接口。 Java对象只能序列化。如果一个类或它的任何超类实现了java.io.Serializable接口或它的子接口java.io.Externalizable。大多数java类都是可序列化的。

NotSerializableException: packageName。要使类对象参与序列化过程,类必须实现Serializable或Externalizable接口。


序列化接口

对象序列化生成一个流,其中包含保存的对象的Java类信息。对于可序列化的对象,即使存在不同的(但兼容的)类实现版本,也会保留足够的信息来恢复这些对象。Serializable接口被定义为识别实现了Serializable协议的类:

package java.io;

public interface Serializable {};

The serialization interface has no methods or fields and serves only to identify the semantics of being serializable. For serializing/deserializing a class, either we can use default writeObject and readObject methods (or) we can overriding writeObject and readObject methods from a class. JVM will have complete control in serializing the object. use transient keyword to prevent the data member from being serialized. Here serializable objects is reconstructed directly from the stream without executing InvalidClassException « In deserialization process, if local class serialVersionUID value is different from the corresponding sender's class. then result's in conflict as java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771 The values of the non-transient and non-static fields of the class get serialized.

外部化接口

对于Externalizable对象,容器只保存对象类的标识;类必须保存并恢复内容。Externalizable接口定义如下:

package java.io;

public interface Externalizable extends Serializable
{
    public void writeExternal(ObjectOutput out)
        throws IOException;

    public void readExternal(ObjectInput in)
        throws IOException, java.lang.ClassNotFoundException;
}

The Externalizable interface has two methods, an externalizable object must implement a writeExternal and readExternal methods to save/restore the state of an object. Programmer has to take care of which objects to be serialized. As a programmer take care of Serialization So, here transient keyword will not restrict any object in Serialization process. When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor, then the readExternal method called. Serializable objects are restored by reading them from an ObjectInputStream. OptionalDataException « The fields MUST BE IN THE SAME ORDER AND TYPE as we wrote them out. If there is any mismatch of type from the stream it throws OptionalDataException. @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt( id ); out.writeUTF( role ); out.writeObject(address); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.id = in.readInt(); this.address = (Address) in.readObject(); this.role = in.readUTF(); } The instance fields of the class which written (exposed) to ObjectOutput get serialized.


示例«实现Serializable

class Role {
    String role;
}
class User extends Role implements Serializable {

    private static final long serialVersionUID = 5081877L;
    Integer id;
    Address address;

    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }
}

class Address implements Serializable {

    private static final long serialVersionUID = 5081877L;
    String country;
}

示例«实现了Externalizable

class User extends Role implements Externalizable {

    Integer id;
    Address address;
    // mandatory public no-arg constructor
    public User() {
        System.out.println("Default Constructor get executed.");
    }
    public User( String role ) {
        this.role = role;
        System.out.println("Parametarised Constructor.");
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt( id );
        out.writeUTF( role );
        out.writeObject(address);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.id = in.readInt();
        this.address = (Address) in.readObject();
        this.role = in.readUTF();
    }
}

例子

public class CustomClass_Serialization {
    static String serFilename = "D:/serializable_CustomClass.ser";

    public static void main(String[] args) throws IOException {
        Address add = new Address();
        add.country = "IND";

        User obj = new User("SE");
        obj.id = 7;
        obj.address = add;

        // Serialization
        objects_serialize(obj, serFilename);
        objects_deserialize(obj, serFilename);

        // Externalization
        objects_WriteRead_External(obj, serFilename);
    }

    public static void objects_serialize( User obj, String serFilename ) throws IOException{
        FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        // java.io.NotSerializableException: com.github.objects.Address
        objectOut.writeObject( obj );
        objectOut.flush();
        objectOut.close();
        fos.close();

        System.out.println("Data Stored in to a file");
    }
    public static void objects_deserialize( User obj, String serFilename ) throws IOException{
        try {
            FileInputStream fis = new FileInputStream( new File( serFilename ) );
            ObjectInputStream ois = new ObjectInputStream( fis );
            Object readObject;
            readObject = ois.readObject();
            String calssName = readObject.getClass().getName();
            System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException

            User user = (User) readObject;
            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void objects_WriteRead_External( User obj, String serFilename ) throws IOException {
        FileOutputStream fos = new FileOutputStream(new File( serFilename ));
        ObjectOutputStream objectOut = new ObjectOutputStream( fos );

        obj.writeExternal( objectOut );
        objectOut.flush();

        fos.close();

        System.out.println("Data Stored in to a file");

        try {
            // create a new instance and read the assign the contents from stream.
            User user = new User();

            FileInputStream fis = new FileInputStream(new File( serFilename ));
            ObjectInputStream ois = new ObjectInputStream( fis );

            user.readExternal(ois);

            System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);

            Address add = (Address) user.address;
            System.out.println("Inner Obj : "+ add.country );
            ois.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

@see

什么是对象序列化 对象序列化:常见问题


基本上,Serializable是一个标记接口,它暗示一个类对于序列化是安全的,并且JVM决定它如何序列化。Externalizable包含两个方法,readExternal和writeExternal。Externalizable允许实现者决定如何序列化一个对象,而Serializable序列化对象是默认的方式。


一些差异:

For Serialization there is no need of default constructor of that class because Object because JVM construct the same with help of Reflection API. In case of Externalization contructor with no arg is required, because the control is in hand of programmar and later on assign the deserialized data to object via setters. In serialization if user want to skip certain properties to be serialized then has to mark that properties as transient, vice versa is not required for Externalization. When backward compatiblity support is expected for any class then it is recommended to go with Externalizable. Serialization supports defaultObject persisting and if object structure is broken then it will cause issue while deserializing.