Java中的Serializable和Externalizable有什么区别?

时间:2009-05-03 19:58:19

标签: java serialization serializable externalizable

Java中SerializableExternalizable之间有什么区别?

12 个答案:

答案 0 :(得分:259)

要添加其他答案,通过实施java.io.Serializable,您可以获得班级对象的“自动”序列化功能。不需要实现任何其他逻辑,它只会工作。 Java运行时将使用反射来弄清楚如何编组和解组对象。

在早期版本的Java中,反射非常慢,因此序列化大对象图(例如在客户端 - 服务器RMI应用程序中)是一个性能问题。为了处理这种情况,提供了java.io.Externalizable接口,类似于java.io.Serializable,但使用自定义编写的机制来执行编组和解组功能(您需要实现readExternal和{{1你班上的方法)。这为您提供了解决反射性能瓶颈的方法。

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

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

Externalizable的一大缺点是你必须自己维护这个逻辑 - 如果你在课堂上添加,删除或更改某个字段,你必须更改Externalizable / {{1}方法来解释它。

总之,writeExternal是Java 1.1天的遗留物。实际上已经没有必要了。

答案 1 :(得分:35)

序列化提供了存储和稍后重新创建对象的默认功能。它使用详细格式来定义要存储的对象的整个图形,例如假设您有一个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()和readExternal()。这些在序列化或反序列化时自动调用。在使用Externalizable时我们应该记住默认构造函数应该是public,否则代码将抛出异常。请按照以下代码:

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)方法中对其进行序列化,也没有在readExternal(ObjectInput oi)中设置它的值。这是Externalizable提供的灵活性。

上述代码的输出如下:

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

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

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

private transient String passWord;

希望它有所帮助。如果我犯了任何错误,我道歉。感谢。

答案 2 :(得分:24)

为了完整起见,transient关键字也缩小了两者之间的差距。

如果您只想序列化部分对象,只需将特定字段设置为transient,将其标记为不持久,并实施Serializable

答案 3 :(得分:21)

SerializableExternalizable

之间的主要差异
  1. 标记界面Serializable是没有任何方法的标记界面。 Externalizable界面包含两种方法:writeExternal()readExternal()
  2. 序列化过程:对于实现Serializable接口的类,将启动默认序列化过程。程序员定义的序列化过程将被用于实现Externalizable接口的类。
  3. 维护Incompatible changes可能会破坏序列化。
  4. 向后兼容性和控制:如果您必须支持多个版本,则可以使用Externalizable界面完全控制。您可以支持对象的不同版本。如果您实施Externalizable,则您有责任序列化super
  5. public No-arg构造函数Serializable使用反射构造对象,不需要arg构造函数。但是Externalizable要求公开的无法构造函数。
  6. 有关详情,请参阅Hitesh Garg {{1}}。

答案 4 :(得分:20)

序列化使用某些默认行为来存储并稍后重新创建对象。您可以指定处理引用和复杂数据结构的顺序或方式,但最终归结为使用每个原始数据字段的默认行为。

外部化在极少数情况下用于您真正想要以完全不同的方式存储和重建对象,而不使用数据字段的默认序列化机制。例如,假设您拥有自己独特的编码和压缩方案。

答案 5 :(得分:9)

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

  • NotSerializableException packageName.ClassName «要在序列化过程中参与类对象,该类必须实现Serializable或Externalizable接口。

enter image description here

<强> Serializable Interface

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

package java.io;

public interface Serializable {};
  • 序列化接口没有方法或字段,仅用于标识可序列化的语义。对于序列化/反序列化类,要么我们可以使用默认的writeObject和readObject方法(或),我们可以从类中重写writeObject和readObject方法。
  • JVM将完全控制序列化对象。使用 transient keyword 来阻止数据成员被序列化。
  • 此处可直接从流中重建可序列化对象,而不执行
  • InvalidClassException«在反序列化过程中,如果本地类 serialVersionUID 值与相应的发件人类不同。然后结果是冲突的 java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
  • 类的非瞬态和非静态字段的值被序列化。

<强> Externalizable Interface

对于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;
}
  • Externalizable接口有两个方法,一个可外化的对象必须实现一个writeExternal和readExternal方法来保存/恢复一个对象的状态。
  • 程序员必须处理要序列化的对象。作为程序员处理序列化所以,这里的transient关键字不会限制序列化过程中的任何对象。
  • 重建Externalizable对象时,使用public no-arg构造函数创建实例,然后调用readExternal方法。通过从ObjectInputStream中读取可序列化对象来恢复它们。
  • OptionalDataException«字段必须与我们写出来时的顺序和类型相同。如果流中存在任何类型不匹配,则抛出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();
    }
    
  • (公开)写入ObjectOutput的类的实例字段序列化。

示例«实现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

答案 6 :(得分:7)

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

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

外化使对象流的生产者能够完全控制精确的类元数据(如果有的话),超出了类所需的最小标识(例如,其名称)。这在某些情况下显然是可取的,例如封闭环境,其中对象流的生产者及其消费者(从流中对对象进行统一)是匹配的,并且关于该类的其他元数据没有用处并且降低了性能。

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

答案 7 :(得分:7)

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

示例是实现AMF(ActionScript消息格式)远程处理以通过网络传输本机操作脚本对象。

答案 8 :(得分:2)

在考虑提高性能的选项时,不要忘记自定义序列化。你可以让Java做它做得很好,或者至少足够好,免费,并为它做得很糟糕提供定制支持。这通常比完全的Externalizable支持少得多。

答案 9 :(得分:2)

Serializable和Externalizable之间存在很多差异,但是当我们比较自定义Serializable(重写的writeObject()&amp; readObject())和Externalizable之间的差异时,我们发现自定义实现与ObjectOutputStream类紧密绑定,其中在Externalizable中case,我们自己提供ObjectOutput的实现,它可以是ObjectOutputStream类,也可以是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 / Instream与类紧密绑定的位置。我们可以扩展ObjectOutputStream / ObjectInputStream,但它有点难以使用。

答案 10 :(得分:0)

基本上,Serializable是标记接口,表示类可安全进行序列化,并且JVM确定如何对其进行序列化。 Externalizable包含2种方法,readExternalwriteExternalExternalizable允许实现者决定如何序列化对象,而Serializable则以默认方式序列化对象。

答案 11 :(得分:0)

一些区别:

  1. 对于序列化,不需要该类的默认构造函数,因为Object是因为JVM在Reflection API的帮助下构造了相同的对象。在没有arg的外部化构造器的情况下,因为控制权掌握在程序中,后来又通过设置器将反序列化的数据分配给对象。

  2. 在序列化中,如果用户要跳过某些要序列化的属性,则必须将这些属性标记为瞬态,反之则不需要外部化。

  3. 如果期望任何类都具有向后兼容性支持,则建议使用Externalizable。序列化支持defaultObject持久化,如果对象结构损坏,则在反序列化时会导致问题。