Java:writeObject与writeExternal的效率比较

19
据说Java的默认序列化机制不太高效,因为a)它通过反射发现要写入/读取的字段,这通常很慢;b)它向流中写入额外的数据。
使其更加高效的一种方法是实现Externalizable接口及其writeExternal/readExternal方法。
问题在于:如果我提供了writeObject/readObject方法,并且在其中没有调用defaultWriteObject/defaultReadObject,那么该机制将不使用反射来确定要写入/读取的字段,并且它也不会向流中写入额外数据(或者会吗?不确定)。因此,从效率的角度来看,实现上述writeObject/readObject是否与实现Externalizable相同?或者后者给出了前者没有的一些实际好处吗?
编辑: 当可序列化类实现readObject/writeObject时被子类化时,如果子类有自己的readObject/writeObject,则它们不需要调用父类的readObject/writeObject。如果超类/子类代替实现Externalizable,则情况并非如此。在这种情况下,必须显式调用超类的writeExternal/readExternal。然而,从效率的角度来看,这种区别是无关紧要的。

请问您是如何得出在可序列化类中不需要使用super.readObject()的结论的?这是否意味着对于每个读/写对象方法,编译器都会在第一行添加对defaultread/writeObject()方法的调用? - amarnath harish
@amarnath harish,我相信编译器会添加一个调用super的read/write对象。你可以写一个小代码片段来验证这一点。如果你发现不同,请告诉我。 - shrini1000
3个回答

9

在选择下一个class/writeObject/readObject时仍然存在一些额外工作量,但是这个工作量已经明显减少。

这可以根据你的需求以及是否使用它提供的额外选项来执行与Externalizable相同的操作。例如,readObject假定每次都需要创建一个新对象,而Externalizable具有readResolve,这意味着可以重用对象。

http://docs.oracle.com/javase/1.5.0/docs/guide/serialization/spec/input.html

在许多情况下,回收对象是加速反序列化的“下一步”(假定这对您来说是一种选择)。

http://vanillajava.blogspot.co.uk/2011/10/recycling-objects-to-improve.html


请问您能否再解释一下:“在选择下一个要调用的类/ writeObject / readObject 时仍然存在一些开销。” 另外,“readResolve”不是“Externalizable”合同的一部分,对吗? 所以我有点困惑。 - shrini1000
它必须确定调用哪个类的readObject或readExternal,除非您自己也这样做。 readResolve是一种可选的方法,您可以与Serailizable一起使用。有关更多详细信息,请参见第一个链接。 - Peter Lawrey
1
谢谢。我已经使用了readResolve,但是因为你在与Externalizable相关的地方提到了它,所以感到有些困惑。顺便说一下,我读了你的文章,写得很好。我在那里问了一个问题。如果您能回复一下就太好了。 - shrini1000

2

在尝试和查看序列化机制代码时,发现了几个问题:

1)如果对象被发现是Externalizable,则将其强制转换为Externalizable,并在其上调用相应的方法;而对于Serializable对象,则通过反射检查它是否具有readObject/writeObject。因此,这可能使其稍微慢一些。

2)对于使用readObject/writeObject的Serializable对象,写入的数据量比Externalizable略多(当我写入B对象时,发现以下代码存在1字节的差异)。

对于Externalizable:

static class A implements Externalizable
{
    @Override
    public void writeExternal(ObjectOutput out) throws IOException 
    {
        System.out.println("A write called");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException 
    {
        System.out.println("A read called");
    }       
}

static class B extends A implements Externalizable
{       
    @Override
    public void writeExternal(ObjectOutput out) throws IOException 
    {
        super.writeExternal(out);
        System.out.println("B write called");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException 
    {
        super.readExternal(in);
        System.out.println("B read called");
    }       
}

对于可序列化:

static class A implements Serializable
{
    private void writeObject(ObjectOutputStream out) throws IOException 
    {
        System.out.println("A write called");
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException 
    {
        System.out.println("A read called");
    }       
}

static class B extends A implements Serializable
{       
    private void writeObject(ObjectOutputStream out) throws IOException 
    {           
        System.out.println("B write called");
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException 
    {           
        System.out.println("B read called");
    }       
}

1

在类设计方面,主要的区别是 Serializable 可以用于任何类,而 Externalizable 只适用于具有公共默认(无参)构造函数的可变类。


它们真的需要是可变的吗?我的意思是,我不能像这样做吗?http://www.byteslounge.com/tutorials/java-externalizable-example - shrini1000
@shrini1000,你可以这样做,但是User类不是线程安全的。然而,将所有字段都设置为volatile应该可以解决这个问题,所以最终,你是正确的,严格来说,这个类不一定要是可变的。 - nilskp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接