实际上,数组没有值,只有指向对象或原始数据类型的指针。如果你想要详细的答案,你应该在这里阅读我的评论:Java is NEVER pass-by-reference, right?...right? 或这里:In Java, what is a shallow copy?
那么,由于数组是指针,如果你克隆一个带有指针的指针会发生什么?起初,指针是真实复制的,但这些指针仅指向未克隆的其他对象。所以如果你想克隆,我建议不要使用数组,而是使用“更难”的数据结构:类。另一种可能性是永远不要在数组中存储数组......就像我只将数组用于容器!
但我无法向您提供有关 Java 多维泛型的详细信息,因为我从不处理它们,不仅因为它们可能不一致,因为它们是数组(它们无论如何都违反了一些 OO 原则并使代码看起来很丑)。
编辑
我正在运行一些测试 clone 方法如何用于类中的数组,问题是什么以及我们有哪些解决方法。
首先是测试数据结构:
public class Foobar implements Cloneable {
String[] array;
public Foobar() {
this.array = new String[10];
}
public String getValue(){
return array[0];
}
public String[] getArray(){
return array;
}
public void setArray(String[] array){
this.array = array;
}
@Override
public Object clone(){
try{
Foobar foobar = (Foobar) super.clone();
foobar.setArray(array);
return foobar;
}
catch(Exception e){
return null;
}
}
}
现在是控制器:
String[] array = new String[10];
array[0] = "111";
Foobar foo1 = new Foobar();
foo1.setArray(array);
Foobar foo2 = foo1; //Alternation: Foobar foo2 = (Foobar) foo1.clone();
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());
array[0] = "999";
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());
测试结果总是这样——无论我使用 = 还是 clone():
Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 999
Instance: [Ljava.lang.String;@42e816 with value: 999
这不好好!!
那么解决方法是什么?我建议在每个数据结构类中都这样做:
public class Foobar implements Serializable {
//any class variables...it doesn't matter which!
public Foobar() {
//do initialisation here...it doesn't matter what you do!
}
public Foobar copy(){
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Foobar foobar = (Foobar) ois.readObject();
return foobar;
}
catch(Exception e){
return null;
}
}
}
因此,您只需实现一行代码即可获得完整副本:
Foobar foo2 = foo1.copy(); //nice and easy!!
此解决方案的优点:通常实现接口 Serializable 以使类“可复制”就足够了。如果没有,您可以通过阅读 Serializable Javadoc 中的内容来解决任何问题!
更重要的是:无论您想要“可复制”类中的哪种对象都没有关系,因此您无需在此问题上花费更多时间。毕竟,上面的代码是深度嵌入 Java 以来最简单、最快的解决方案,并且只使用 RAM! (感谢 ByteArrayOutputStream)
享受吧!
更新:请注意,如果您想要一个临时堆栈或者您正在处理线程(通常:如果您需要让对象完全相互独立,那么您只需要使用对象的副本) )。否则你根本不应该复制任何东西!此外,如果您将一些数据写入文件或套接字,则不需要副本。甚至我建议仅在真正使用时才实现复制方法:对于数据结构(模型)。因此,使用这种强大的方法时要小心(否则它可能会减慢您的应用程序,甚至如果您无缘无故地制作数百万份副本,甚至会填满 Java VM 存储,这确实会导致堆栈溢出:o)。
编辑
我在这个问题上做了更多的工作。因为我突然发现,Java API 中没有“原始”数组的公共 clone() 方法! (来自 SUN 的“复活节彩蛋”,用于 String[] 或 int[] 等数组;-)
由于我使用真正的数组作为 Foobar 的基本数据结构(不是 ArrayLists!),我可以像这样更改(上述类的)克隆方法:
@Override
public Object clone(){
try{
Foobar foobar = (Foobar) super.clone();
String[] arrayClone = array.clone(); //who thought that this is possible?!
foobar.setArray(arrayClone);
return foobar;
}
catch(Exception e){
return null;
}
}
现在我们可以直接得到这个结果:
Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@9304b1 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 999
Instance: [Ljava.lang.String;@9304b1 with value: 111
“双嵌套” 对象解决了问题!!!如您所见,克隆具有独立于原始对象的不同对象...因此 foo1.equals(foo2)) 将是错误的!
解决方案:在一个类的clone方法中,你也需要克隆它所有的类变量!(但是如果某些类变量是ArrayLists或者多维数组,即使这个解决方案也行不通'不行!)
最后,真正的问题是什么? ArrayList 类没有克隆它的数组,它只调用了 Array 类中的方法 copyOf,这是有害的。 所以永远不要使用 ArrayList 类的 clone 方法,也永远不要从 ArrayList 继承任何类,因为它的 clone 方法不起作用!(只有当 ArrayList 类只包含基元而没有对象时它才有效.. .否则只需使用上面简单的 ByteArray 解决方案!)。
请注意,对于像 Object[][] 这样的多维数组,您总是需要实现上面的 ByteArray 解决方案,它们不能被克隆!如果您的阵列很大,则可能需要一段时间并且还需要一些 RAM。
现在您是克隆专家了! :-D