Java 如何处理Findbugs“;serializable类“中的非瞬态非序列化实例字段”;?

Java 如何处理Findbugs“;serializable类“中的非瞬态非序列化实例字段”;?,java,serialization,findbugs,Java,Serialization,Findbugs,考虑下面的类。如果我对它运行Findbugs,它将在第5行(而不是第7行)给出一个错误(“serializable类中的非瞬态非序列化实例字段”) 1 public class TestClass implements Serializable { 2 3 private static final long serialVersionUID = 1905162041950251407L; 4 5 private Set<Integer> mySet; // Findbu

考虑下面的类。如果我对它运行Findbugs,它将在第5行(而不是第7行)给出一个错误(“serializable类中的非瞬态非序列化实例字段”)

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 }
1公共类TestClass实现可序列化{
2.
3私有静态最终长serialVersionUID=1905162041950251407L;
4.
5私有集mySet;//Findbugs错误
6.
7私有HashSet-myOtherSet;
8.
9 }
这是正确的,因为java.util.Set从未在其层次结构中实现Serializable,而java.util.HashSet则实现了。 然而,最好的做法是根据接口而不是具体的实现进行编码

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

我可以在第3行添加@Suppresswarnings(justify=“No bug”,values=“SE_BAD_FIELD”)。在我的实际代码中有相当多的集合和列表,我担心它会太多地乱扔我的代码

有更好的方法吗

然而,编写代码是最佳实践 针对接口而不是混凝土 实现

我认为不,在这种情况下不是。Findbugs非常正确地告诉您,一旦在该字段中设置了不可序列化的
实现,您就有可能遇到
NotSerializableException
。这是你应该处理的事情。如何,这取决于类的设计

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

我知道这是一个已经回答过的老问题,但其他人知道的是,如果您对序列化特定字段不感兴趣,可以将
set
字段设置为transient,这将修复FindBugs错误

public class TestClass implements Serializable {

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

}
公共类TestClass实现可序列化{
私有静态最终长serialVersionUID=1905162041950251407L;
私有瞬态集mySet;
}

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

您可以使用捕获助手来确保传入集支持两个接口:

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);
    }
}
private静态类SerializableTestClass&Serializable>Serializable for SerializableSet(T集)
{
返回新的SerializableTestClass(集合);
}
}

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

我对集合字段使用findbugs排除过滤器:

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


请参见

通过向类中添加以下方法,您可以消除那些
严重的警告消息:

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

private void readObject(ObjectInputStream stream)
        throws IOException, ClassNotFoundException {
    stream.defaultReadObject();
}

对内部表示使用一个具体的可序列化集,但使任何公共接口都使用该集接口

public class TestClass implements Serializable {
    private static final long serialVersionUID = 1905162041950251407L;

    private HashSet<Integer> mySet;

    public TestClass(Set<Integer> s) {
        super();
        setMySet(s);
    }

    public void setMySet(Set<Integer> s) {
        mySet = (s == null) ? new HashSet<>() : new HashSet<>(s);
    }
}
公共类TestClass实现可序列化{
私有静态最终长serialVersionUID=1905162041950251407L;
私有HashSet-mySet;
公共测试类(集合s){
超级();
setMySet(s);;
}
公共无效集合集(集合s){
mySet=(s==null)?new HashSet():new HashSet(s);
}
}

如果您正在使用findbugs maven插件,并且必须保留一个字段,而该字段是一个未实现可序列化接口的类,例如,一个在第三方中定义了类的字段。您可以手动配置findbugs的排除文件

如果这是唯一的情况,请将其添加到排除文件中: pom:


org.codehaus.mojo
&


关于SE_BAD_字段:serializable类中的非瞬态非序列化实例字段,我认为它不应该检查实体。因为,
javax.persistence.AttributeConverter
提供了将字段外部序列化的方法(implements Serializable是要序列化的内部方法)。

我对可序列化类中的受保护字段发出了高警告。为解决我的问题的字段添加瞬态:

 protected transient Object objectName;

电子战。即使在这种情况下,我也不喜欢使用具体的字体。我认为这是一个完全可以忽略的警告。您可能需要担心的唯一一点是,您是否真的有任意代码设置此集合,这可能会将其设置为不可序列化的集合实例。@Michael在内部使用具体类型可能不是“问题”,但我认为这是一种不好的做法。即使它是在外部设置的,您也只需要担心您是否在处理您无法控制的代码。我觉得在这种情况下,设计的清洁度(使用界面)超过了这个警告的(理论上)有用性。我同意@jtahlborn。当您确实需要一个HashSet时,不能让每个方法都只接受HashSet。调用方不需要传递哈希集,任何可序列化集实现都可以。这是您现在无法用Java表达的东西,这是您必须处理的语言设计限制。我认为使用界面忽略这个警告更安全(或者只是检查一下你是否正常,而不是100%确定)。@ymajoros:对于“安全”这个词,没有一个合理的定义可以证明你的说法是正确的。事实上,安全正是使用混凝土类型比ignor更好的解决方案的原因
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
    <Match>
        <Class name="com.xxx.Foo" /> 
        <Field type="org.springframework.statemachine.StateMachineContext"/>
    </Match>
@Entity
public class Foo extends Boo {
    StateMachineContext<A, B> stateMachineContext;
 protected transient Object objectName;