我在一些代码上运行findbugs,它说readObject(…)方法必须是私有的才能调用序列化/反序列化?为什么?如果公开会有什么问题?
关于readObject()/writeObject()
是私有的,情况是这样的:如果你的类Bar扩展了一些类Foo;Foo也实现了readObject()/writeObject()
, Bar也实现了readObject()/writeObject()
。
现在,当Bar对象被序列化或反序列化时,JVM需要自动为Foo和Bar调用readObject()/writeObject()
(即不需要显式调用这些超类方法)。然而,如果这些方法不是私有的,它就变成了方法覆盖,JVM就不能再在子类对象上调用超类方法。
因此它们必须是私有的!
在现代Java实现中(至少JDK 6到10),ObjectInputStream
和ObjectOutputStream
类将只识别readObject
, readObjectNoData
和writeObject
方法,如果它们被声明为private
而不是static
。
(我在任何文档中都找不到明确的说明,但代码中明确实现了该限制。)
所以,不管这是不是一个好主意,FindBugs指出非私有readObject
方法是一个错误是正确的。它不会被使用。
我想把这个方法设为public的唯一原因是把它设为final,这样继承的对象就不能篡改它了。
我认为你不应该尝试那样做。在类级别的javadoc中注明您认为子类应该做什么和不应该做什么。如果有人选择实现一个类而忽略了这个建议,那么处理后果是他们的问题。
试图强迫其他人以特定方式实现子类的问题是,他们可能有一个用例,要求他们做不同的事情…因为你无法理解的原因。让未来的开发人员自由地做他们想做的事情,并让他们对结果负责,这是一个更好的主意。我不确定为什么findbugs认为这是一个bug,但我可以猜测两个可能的原因。将readObject设为公共会破坏封装,因为调用代码可以看到类的内部结构。此外,通过将其设为公共,可以强制所有派生类将readObject声明为公共。所以除非这个类是final的,否则你是在改变序列化的契约。
我认为findbugs可以为它的大多数消息提供基本原理。这上面有什么要说的吗?
您没有理由自己调用诸如readObject
之类的序列化方法,更不用说从另一个类调用了。你应该尽可能地减少可见性。
编辑:如果你想子类能够改变行为,使方法protected
…这是可以接受的。
为了让你的方法被objectInputStream.readObject()调用,你必须将它声明为private:
private void readObject(ObjectInputStream objectInputStream)
如果你不这样做,你的方法将不会被调用(在那里放置一个断点来证明这一点)。您的代码可能看起来可以工作,但那是因为正在使用默认的序列化。
您可能想要使此保护以允许子类化,但这不是必需的。序列化进程在调用具体类的readObject之前自动调用基类的readObject。即使具体类没有调用:
,也会发生这种情况。objectInputStream.defaultReadObject();
…与我在网上看到的其他帖子相反。这同样适用于writeObject方法