如何覆盖ObjectOutputStream.writeStreamHeader()?

时间:2009-07-06 07:29:55

标签: java constructor thread-local objectoutputstream

可以重写方法ObjectOutputStream.writeStreamHeader()以在数据头前添加或附加数据。但是,如果该数据基于传递给派生类的构造函数的参数,如:

public class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
        super( out );
        m_myData = myData;
    }

    protected void writeStreamHeader() throws IOException {
        write( m_myData );            // WRONG: m_myData not initialized yet
        super.writeStreamHeader();
    }

    private final int m_myData;
}

它无效,因为在super()初始化m_myData调用super()之前调用了writeStreamHeader()。我能想到解决这个问题的唯一方法是使用ThreadLocal之类的:

public class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream( int myData, OutputStream out ) throws IOException {
        super( thunk( myData, out ) );
    }

    protected void writeStreamHeader() throws IOException {
        write( m_myData.get().intValue() );
        super.writeStreamHeader();
    }

    private static OutputStream thunk( int myData, OutputStream out ) {
        m_myData.set( myData );
        return out;
    }

    private static final ThreadLocal<Integer> m_myData = new ThreadLocal<Integer>();
}

这似乎有效,但有更好(不那么笨重)的方式吗?

4 个答案:

答案 0 :(得分:3)

有一种解决此类问题的通用方法。创建类和内部类,并在外部作用域中引用变量。 (注意,这仅适用于-target 1.4或greter,这是当前版本的javac中的默认设置。使用-target 1.3,您将获得NPE。)

public static ObjectOutputStream newInstance(
    final int myData, final OutputStream out
) throws IOException {
    return new ObjectOutputStream(out) {
        @Override
        protected void writeStreamHeader() throws IOException {
            write(myData);
            super.writeStreamHeader();
        }
    };
}

但是,在构建ObjectOuputStream之前将数据写出来可能更容易。

答案 1 :(得分:1)

从构造函数中调用非final方法通常是个坏主意(出于你提出的原因)。

您是否可以在不扩展ObjectOutputStream的情况下实现自定义序列化?我正在考虑流组成。例如,您可以通过在ObjectOutputStream执行之前将标头写入底层OutputStream来添加标头。这显然不能在ObjectOutputStream的子类中完成,但可以从外部轻松完成。

   out.write(myExtraHeader);
   ObjectOutputStream oos = new ObjectOutputStream(out);

如果你愿意,你可以将这一切完美地包装在ObjectOutput接口后面,就像Stu Thompson在他的回答中所建议的那样,这样它就可以像对待ObjectOutputStream一样向外看。

更新

查看ObjectDutputStream的JavaDocs和源代码,有第二个(受保护的)构造函数,它不会调用writeStreamHeader()

但是,此构造函数也不会初始化其他内部结构。引用文档, 它旨在“用于完全重新实现ObjectOutputStream的子类 不必分配这个ObjectOutputStream实现所使用的私有数据“。在这种情况下,它还会调用其他一些方法,例如”writeObjectOverride“.Messy ......

答案 2 :(得分:1)

你不能这样做吗?当你初始化了所需的字段时,忽略来自超级构造函数的writeStreamHeader调用并自己做一个:

public class MyObjectOutputStream extends ObjectOutputStream {

private boolean initalized = false;
private final int m_myData;

protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
    super(out);
    m_myData = myData;
    initalized = true;
    writeStreamHeader();
}

protected void writeStreamHeader() throws IOException {

    if(!initalized){
        return;
    }

    write( m_myData );
    super.writeStreamHeader();
}
}

修改

或者,正如Thilo所建议的那样,它可以写成:

public class MyObjectOutputStream extends ObjectOutputStream {

    private final int m_myData;

    protected MyObjectOutputStream(int myData, OutputStream out) throws IOException, SecurityException {
        super(out);
        m_myData = myData;
        write( m_myData );
        super.writeStreamHeader();
    }

    protected void writeStreamHeader() throws IOException {
        // work is done in the constructor
    }
}

答案 3 :(得分:0)

使用合成而不是继承。

public class MyObjOutStream implements DataOutput, ObjectOutput, ObjectStreamConstants {
    //same interfaces that ObjectOutputStream implements

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

只有在您准备好的情况下才会实例化ObjectOutputStream。您需要在接口中实现的其余方法只能在objOutStream

上调用相同的方法