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


当前回答

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

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

其他回答

对象序列化使用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和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的博客。

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

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

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

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,但使用起来有点困难。