如何处理Findbugs"可序列化类中的非瞬态非可序列化实例字段"?

Koo*_*inn 53 java serialization findbugs

考虑下面的课程.如果我针对它运行Findbugs,它将在第5行给出一个错误("可序列化类中的非瞬态非可序列化实例字段")但不在第7行.

1 public class TestClass implements Serializable {
2
3  private static final long serialVersionUID = 1905162041950251407L;
4
5  private Set<Integer> mySet;      // Findbugs error
6
7  private HashSet<Integer> myOtherSet;
8
9 }
Run Code Online (Sandbox Code Playgroud)

这是正确的,因为java.util.Set从不在其层次结构中实现Serializable,而java.util.HashSet也是如此.但是,最佳做法是针对接口而不是具体实现进行编码.

我怎样才能最好地处理这件事?

我可以在第3行添加@Suppresswarnings(justification ="No bug",values ="SE_BAD_FIELD").我的实际代码中有很多集合和列表,我担心它会丢失我的代码太多.

还有更好的方法吗?

Mic*_*rdt 28

但是,最佳做法是针对接口而不是具体实现进行编码.

我提出不,在这种情况下不是.Findbugs非常正确地告诉您,只要您在该字段中NotSerializableException具有非可序列化的Set实现,就会冒险进入.这是你应该处理的事情.怎么样,这取决于你的课程的设计.

  • 如果这些集合在类中初始化并且从不在外部设置,那么我认为声明字段的具体类型绝对没有错,因为字段无论如何都是实现细节.请在公共接口中使用接口类型.
  • 如果集合通过公共接口传递到类中,则必须确保它们实际上是通过公共接口传递的Serializable.为此,请创建一个界面SerializableSet extends Set, Serializable并将其用于您的领域.然后,要么:
    • 使用SerializableSet公共接口,并提供实现它的实现类.
    • 检查传递给类的集合instanceof Serializable,如果不是,则将它们复制到类似的东西中.

  • @Michael - 具体类型并不比界面更安全,除非你处理的是"最终"类型.我可以轻松地创建一个自定义的HashSet子类,它是_not_ serializable,所以编译检查不会让你获得任何东西.如果你真的是偏执狂,那么你永远不应该直接从外部实例设置集合,但总是制作防御性副本.无论哪种方式,我对使用界面的评论仍然有效:因此警告没有帮助. (9认同)
  • @Michael内部使用具体类型可能不是一个"问题",但我认为这是一个不好的做法.即使它是在外部设置的,你也只需要担心,如果你可能正在处理你无法控制的代码.我觉得在这种情况下,设计的清洁度(使用界面)超过了这个警告的(理论上)有用性. (8认同)
  • EW.即使在这种情况下,我也不喜欢使用混凝土类型.我认为这是一个安全可忽视的警告.您可能需要担心的唯一部分是,如果您真的有任意代码设置此集合,可能将其设置为不可序列化的集合实例. (4认同)
  • 我同意@jtahlborn.当你真的需要一个集合时,你不能让每个方法只接受HashSet.不应要求调用者传递HashSet,任何Serializable Set实现都可以.这是你现在无法用Java表达的东西,这是你必须处理的语言设计限制.我认为使用界面更安全并忽略此警告(或者只是检查你没问题,而不是100%肯定). (4认同)
  • @ymajoros:对于你的陈述是真实的"安全"这个词没有合理的定义.事实上,安全性正是使用混凝土类型比忽略警告更好的解决方案的原因. (3认同)

小智 12

我知道这是一个已经回答的旧问题,但其他人知道,Set<Integer>如果您对序列化特定字段没有兴趣可以修复FindBugs错误,则可以将字段设置为瞬态.

public class TestClass implements Serializable {

    private static final long serialVersionUID = 1905162041950251407L;
    private transient Set<Integer> mySet;

}
Run Code Online (Sandbox Code Playgroud)

我更喜欢这种方法,而不是强迫你的API用户强制转换为你的具体类型,除非它只是内部的,然后Michael Borgwardt的回答更有意义.

  • 这只是将错误更改为"字段mySet是瞬态的,但未通过反序列化设置" (6认同)

Vla*_*cea 9

您可以Critical通过向您的类添加以下方法来消除这些警告消息:

private void writeObject(ObjectOutputStream stream)
        throws IOException {
    stream.defaultWriteObject();
}

private void readObject(ObjectInputStream stream)
        throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
}
Run Code Online (Sandbox Code Playgroud)

  • 添加过时的代码以使审计工具满意(显然是利用该工具中的错误),而程序行为保持不变,只是变得效率稍低。伟大的事情…… (4认同)
  • 很棒的发现!这解决了一个非常模糊的findbugs错误.罗德里戈指出,也避免了标记瞬态的需要,这甚至没有帮助. (2认同)

jon*_*ejj 8

您可以使用捕获帮助程序来确保传入的Set支持两个接口:

private static class SerializableTestClass<T extends Set<?> & Serializable> implements Serializable
{
    private static final long serialVersionUID = 1L;
    private final T serializableSet;

    private SerializableTestClass(T serializableSet)
    {
        this.serializableSet = serializableSet;
    }
}

public static class PublicApiTestClass
{
    public static <T extends Set<?> & Serializable> Serializable forSerializableSet(T set)
    {
        return new SerializableTestClass<T>(set);
    }
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以拥有一个强制Serializable的公共API,而无需检查/需要特定的实现细节.


bra*_*etz 7

我使用findbugs-exclude过滤器来收集字段:

<Match>
    <Field type="java.util.Map" />
    <Bug pattern="SE_BAD_FIELD" />
</Match>
<Match>
    <Field type="java.util.Set" />
    <Bug pattern="SE_BAD_FIELD" />
</Match>
<Match>
    <Field type="java.util.List" />
    <Bug pattern="SE_BAD_FIELD" />
</Match>
Run Code Online (Sandbox Code Playgroud)

http://findbugs.sourceforge.net/manual/filter.html

  • 或闭上眼睛,错误将消失 (3认同)