如何处理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 }
这是正确的,因为java.util.Set从不在其层次结构中实现Serializable,java.util.HashSet也不会。 然而,最好的做法是对接口进行编码而不是具体的实现。
我怎样才能最好地处理这个?
我可以在第3行添加一个@Suppresswarnings(justification =“No bug”,values =“SE_BAD_FIELD”)。我在我的实际代码中有很多设置和列表,我恐怕会乱扔我的代码太多。
有更好的方法吗?
但是,最好的做法是对接口进行编码,而不是具体的实现。
我提出不,在这种情况下不是。 Findbugs相当正确地告诉你,只要你在那个字段中有一个不可序列化的Set
实现,你就冒着运行到NotSerializableException
风险。 这是你应该处理的事情。 怎么样,这取决于你的课程devise。
- 如果这些集合是在类中初始化的,并且从来没有从外部设置的话,那么我认为声明字段的具体types绝对没有错,因为字段无论如何都是实现细节。 在公共接口中使用接口types。
- 如果通过公共接口将集合传递给类,则必须确保它们实际上是可
Serializable
。 为此,创build一个接口SerializableSet extends Set, Serializable
并将其用于您的字段。 然后,要么:- 在公共接口中使用
SerializableSet
,并提供实现它的实现类。 - 检查通过
instanceof Serializable
传递给类的集合,如果它们不是,则将它们复制到某些内容中。
- 在公共接口中使用
我知道这是一个已经回答的老问题,但是别人知道,如果你不想序列化那个能够解决FindBugs错误的特殊字段,你可以把Set<Integer>
字段Set<Integer>
为transient。
public class TestClass implements Serializable { private static final long serialVersionUID = 1905162041950251407L; private transient Set<Integer> mySet; }
我更喜欢这种方法,而不是强迫你的API的用户投到你的具体types,除非它只是内部的,那么Michael Borgwardt的回答更有意义。
您可以使用捕获帮助程序来确保在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); } }
通过这种方式,您可以拥有一个强制Serializable的公共API,而不需要检查/要求具体的实现细节。
我使用findbugs-excludefilter来收集字段:
<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>
通过将以下方法添加到您的课堂,您可以摆脱这些Critical
警告消息:
private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); }
为您的内部表示使用一个具体的Serializable集合,但是使任何公共接口使用Set接口。
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); } }
使用
private transient Set<Integer> mySet;