如何重写ObjectOutputStream.writeStreamHeader()?

发布于 2024-07-25 18:25:58 字数 1361 浏览 5 评论 0原文

可以重写方法 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;
}

它不起作用,因为在初始化 m_myData 之前调用 super() ,并且 >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>();
}

这似乎可行,但是有更好的(不那么笨拙的)方法吗?

The method ObjectOutputStream.writeStreamHeader() can be overridden to prepend or append data to the header. However, if that data is based on an argument passed to the derived class's constructor like:

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;
}

it doesn't work because super() is called before m_myData is initialized and super() calls writeStreamHeader(). The only way I can think to work around this is by using ThreadLocal like:

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>();
}

This seems to work, but is there a better (less clunky) way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

橙味迷妹 2024-08-01 18:26:04

使用组合而不是继承。

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

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

Use composition instead of inheritance.

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

    private ObjectOutputStream objOutStream;

    //implement all the methods below
}

Instance the ObjectOutputStream only when you are ready to do so. The remaining methods you need to implement in the interfaces can just call the same methods on objOutStream

口干舌燥 2024-08-01 18:26:03

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

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

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

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

更新:

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

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

It is generally a bad idea to call non-final methods from the constructor (for exactly the reason you presented).

Can you achieve your custom serialization without extending ObjectOutputStream? I am thinking about stream composition. For example, you could prepend your header by writing it to the underlying OutputStream before ObjectOutputStream does. This can obviously not be done in a subclass of ObjectOutputStream, but it can easily be done from the outside.

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

If you want, you can wrap this all up nicely behind the ObjectOutput interface as Stu Thompson suggested in his answer, so that it can look to the outside almost like an ObjectOutputStream.

Update:

Looking at the JavaDocs and source for ObjectOutputStream, there is a second (protected) constructor, that does not call writeStreamHeader().

However, this constructor also does not initialize other internal structures. To quote the docs,
it is intended "for subclasses that are completely reimplementing ObjectOutputStream
to not have to allocate private data just used by this implementation of ObjectOutputStream". In this case it also calls some other methods, such as "writeObjectOverride". Messy...

剑心龙吟 2024-08-01 18:26:02

你就不能这样做吗? 当您初始化所需字段时,忽略超级构造函数中的 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
    }
}

Couldn't you do it like this. Ignore the writeStreamHeader call from the super constructor and do one yourself, when you have initialized the needed field:

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();
}
}

EDIT:

Or, as suggested by Thilo, it could be written like:

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
    }
}
乄_柒ぐ汐 2024-08-01 18:26:01

有一个通用的方法可以解决此类问题。 创建类和内部类,并在外部作用域中引用变量。 (请注意,这仅适用于 -target 1.4 或 grter,这是当前版本的 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 之前将数据写出可能会更容易。

There is a general way to solve this sort of problem. Make the class and inner class and reference a variable in the outer scope. (Note, this only works with -target 1.4 or greter, which is the default in current versions of javac. With -target 1.3 you will get an 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();
        }
    };
}

But, it's probably easier just to write the data out before constructing the ObjectOuputStream.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文