5

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_myDatasuper()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

4 回答 4

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.

于 2009-07-06T10:18:28.790 回答
1

从构造函数调用非最终方法通常是一个坏主意(正是您提出的原因)。

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

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

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

更新:

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

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

于 2009-07-06T07:42:56.233 回答
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
    }
}
于 2009-07-06T08:07:35.467 回答
0

使用组合而不是继承。

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

仅当您准备好时才实例化 ObjectOutputStream。您需要在接口中实现的其余方法可以只调用相同的方法objOutStream

于 2009-07-06T07:42:21.710 回答