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

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


答案 1

若要添加到其他答案中,通过实现 ,可以获得类对象的“自动”序列化功能。无需实现任何其他逻辑,它就可以工作了。Java 运行时将使用反射来弄清楚如何封送和取消封送对象。java.io.Serializable

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

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

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

一个很大的缺点是你必须自己保持这个逻辑 - 如果你添加,删除或更改类中的字段,你必须改变你的/方法来解释它。ExternalizablewriteExternalreadExternal

综上所述,是Java的遗物1.1天。真的不再需要它了。Externalizable


答案 2

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

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

但是,如果您希望限制序列化或不希望序列化对象的某些部分,请使用 Externalizable。可外部化接口扩展了可序列化接口,并添加了两个方法:writeExternal() 和 readExternal()。这些在序列化或反序列化时自动调用。在使用 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) 方法中序列化它,也没有在 readExternal(ObjectInput oi) 中设置相同的值。这就是 Externalizable 提供的灵活性。

以上代码的输出如下:

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

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

也可以通过将密码字段声明为瞬态来实现相同的操作。

private transient String passWord;

希望它有帮助。如果我犯了任何错误,我很抱歉。谢谢。