首页 文章

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

提问于
浏览

11 回答

  • 24

    要添加到其他答案,通过实现 java.io.Serializable ,您可以获得类的对象的"automatic"序列化功能 . 不需要实现任何其他逻辑,它只会工作 . Java运行时将使用反射来确定如何编组和解组对象 .

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

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

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

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

    总之, Externalizable 是Java 1.1天的遗物 . 实在没有必要了 .

  • 7

    序列化提供了存储和稍后重新创建对象的默认功能 . 它使用详细格式来定义要存储的对象的整个图形,例如假设您有一个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;
    

    希望能帮助到你 . 如果我犯了任何错误,我道歉 . 谢谢 .

  • 34

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

    如果您只想序列化对象的一部分,只需将特定字段设置为 transient ,将它们标记为不持久化,并实现 Serializable .

  • 19

    SerializableExternalizable 之间的主要区别

    • Marker interfaceSerializable 是没有任何方法的标记接口 . Externalizable 接口包含两个方法: writeExternal()readExternal() .

    • Serialization process :对于实现 Serializable 接口的类,将启动默认序列化过程 . 程序员定义的序列化过程将为实现 Externalizable 接口的类启动 .

    • MaintenanceIncompatible changes可能会破坏序列化 .

    • Backward Compatibility and Control :如果您必须支持多个版本,则可以使用 Externalizable 接口完全控制 . 您可以支持对象的不同版本 . 如果您实现 Externalizable ,则您有责任序列化 super

    • public No-arg constructorSerializable 使用反射来构造对象,并且不需要arg构造函数 . 但是 Externalizable 要求公共的非参数构造函数 .

    请参阅blog by Hitesh Garg 更多细节 .

  • 258

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

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

  • 21

    Object Serialization uses the Serializable and Externalizable interfaces. Java对象只能序列化 . 如果一个类或它的任何超类实现java.io.Serializable接口或其子接口java.io.Externalizable . 大多数java类都是可序列化的 .

    • NotSerializableExceptionpackageName.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值与相应的发件人's class. then result' s不同,则冲突为 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();
            }
        }
    }
    

    @看到

  • 7

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

    这方面的示例是实现AMF(ActionScript消息格式)远程处理以通过网络传输本机动作脚本对象 .

  • 8

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

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

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

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

  • 2

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

  • 0

    Serializable和Externalizable之间存在很多差异但是当我们比较自定义Serializable(覆盖的writeObject()和readObject())和Externalizable之间的差异时,我们发现自定义实现与ObjectOutputStream类紧密绑定,在Externalizable情况下,我们自己提供ObjectOutput的实现,它可以是ObjectOutputStream类,也可以是org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream之类的其他实现 .

    In case of Externalizable interface

    @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但它有点难以使用 .

  • 2

    基本上, Serializable 是一个标记接口,意味着一个类对于序列化是安全的,并且JVM确定它是如何序列化的 . Externalizable 包含2个方法, readExternalwriteExternal . Externalizable 允许实现者决定如何序列化对象,其中 Serializable 以默认方式序列化对象 .

相关问题