Findbugs 通过参数或 clone() 分配字节数组值

Findbugs assigning byte array value through parameter or clone()

例如我有这个构造函数

public Example(byte[] bytes) {
    this.bytes = bytes;
}

在 运行 FindBugs 之后,它会说我应该考虑克隆它...所以我尝试了它并消除了警告。

public Example(byte[] bytes) {
    this.bytes = bytes.clone();
}

但是,我想知道这种变化是否有任何影响或负面影响。据我了解,它也适用于所有数组,而不仅仅是 byte[].

谢谢

想法是采取防御性副本,以便后续对数组的更新不会写入 Example 对象。它还有助于垃圾回收,因为您没有保留对数组的引用。

就影响而言,您可能会使用更多内存,因为您最终可能会将数组存储两次。

这是一种非常普遍的情况 - 当您创建一个依赖于某些 "outside" 数组或列表的对象时 - 该对象是否应该简单地重新使用现有引用?还是您需要创建该输入数据的副本?!

重复使用意味着:您不会在复制操作上花费 CPU 个周期;显然,您也可以重复使用内存(一个副本,而不是两个、三个……)

另一方面:当您保留引用时,其他人更新了那个 "external" 东西……您持有该引用的所有对象也可能会受到影响。

从这个意义上讲:从设计的角度来看,您应该主要根据 "best" 来平衡这两个选项。一般来说,现在很多人都试图实现 immutable classes/objects。含义:创建后,对象的 "content" 将永远不会再改变;换句话说:如果你想不可变,你必须转向"defensive copying"。但是从中可以得到很多东西。

可能的错误是,如果您在实例中重用数组,您会将它与调用代码结合起来,即对数组所做的每项更改,但在 class 之外都可能产生副作用。

示例:

char[] chars = new char[]{ 'a', 'b', 'c', ..., 'z' };
Alphabet lower = new Alphabet( chars );

for( int i = 0; i < chars.length; i++ ) {
  chars[i] = Character.toUpperCase( chars[i] );
}
Alphabet upper = new Alphabet( chars );

如果数组在 Alphabet 中重复使用(即未克隆),您可能会认为 lower 处理小写字符,而实际上它使用的是元素已被替换为大写字母的数组对应部分。

因此在大多数情况下,创建数组的副本更安全,例如通过在其上调用 clone()(还有其他方法)。

这样做的缺点是增加了内存使用量(当然)和执行复制的一些性能成本 - 对于一些小数组来说,这两者都可以忽略不计,但当大小 and/or 时会增加阵列增加。

另一个缺点 可能 是调用代码可能希望共享数组,而创建克隆可能会破坏它。你也必须检查一下。