Java,这是一个Deep副本吗?

时间:2013-03-25 08:56:35

标签: java deep-copy

我似乎无法通过阅读任何类似的问题得到明确的答案,我正在尝试使用复制构造函数深入克隆Java中的对象,这是一个深层次的副本:

public class Tile{
    Image sprite = null;
    int x = 0;
    int y = 0;
    public Tile(Image setImage, int sX, int sY){
         this.sprite = setImage;
         this.x = sX;
         this.y = sY;
    }
    public Tile(Tile copyTile){
         this.sprite = copyTile.sprite;
         this.x = copyTile.x;
         this.y = copyTile.y;
    }
    public void setNewLocation(int sX, int sY){
         this.x = sX;
         this.y = sY;
    }
}

然后当我创建我的瓷砖地图时,我可以做这样的事情

List<Tile> tileList = new List<Tile>();
Tile masterGrassTile = new Tile(grassImage, 0,0);
tileList.set(0,new Tile(masterGrassTile));
tileList.set(1,new Tile(masterGrassTile));
tileList.get(0).setNewLocation(0,32);
tileList.get(1).setNewLocation(0,64);

如果我要在各自的位置渲染两块瓷砖那会有用吗?或者是赋值tileList.get(1).setNewLocation(0,64);影响像引用,所有这些都与上一次分配的位置相同。

5 个答案:

答案 0 :(得分:11)

  

这是一份深刻的副本吗?

不,这不是因为this.sprite = copyTile.sprite; Tile的两个对象都引用Image的同一个对象。

  

如果我要在各自的位置渲染两块瓷砖那会有用吗?或者是赋值tileList.get(1).setNewLocation(0,64);影响像引用,所有这些都与上一次分配的位置相同。

不,x和y的值在Tiles的两个对象中是独立的,代码应该有效,并且两个对象都有不同的x和y值。

答案 1 :(得分:4)

首先,让我们回顾一下深拷贝和浅拷贝之间的差异。

浅表副本指向与源相同的引用。因此,如果制作名为A的实例B的副本,则对B中包含对象的字段所做的任何更改都将修改A中的这些字段,因为它们指向相同的引用。< / p>

深层副本具有独立的字段/引用,它不指向源的引用。对副本上的字段/属性的更改不会影响源的字段/属性。

在你的情况下,我相信你已经制作了一份浅色的副本,因为你将sprite字段的引用从源代码分配给了副本的sprite字段。

为了说明这一点,我修改了Tile类来源以公开Image并模拟了Image类。

概念证明的修改

class Tile{
        /* Rest of Class*/
        //Additions
    public Image getSprite() {
        return sprite;
    }
    public void setSprite(Image sprite) {
        this.sprite = sprite;
    }

}

//Mock
class Image{
    public String name;
    public Image(String name){
        this.name = name;
    }
}

概念证明

public static void main(String[] args) {
    List<Tile> tileList = new ArrayList<Tile>();
    Tile masterGrassTile = new Tile(new Image("grass.png"), 0,0);

    Tile copyTile = new Tile(masterGrassTile);
    copyTile.getSprite().name = "water.png";

    System.out.println(masterGrassTile.getSprite().name); //Prints water.png
}

注意如何更改复制实例的sprite属性会影响原始实例sprite属性。

答案 2 :(得分:3)

不,这不是深拷贝,因为在Tile的所有对象之间共享相同的Image对象。

看看你的例子虽然看起来这个副本足以满足你的需要,因为它允许你在不同的位置重复使用相同的Image对象(这可能更有效)。

答案 3 :(得分:2)

在java中有两种数据类型:

  • 原语(float,int,double,boolean ...),它没有深/浅复制的概念,因为它们与赋值一起使用。
  • 浅层复制意味着传递引用的对象。深度复制意味着拥有一个与源具有相同值的新对象。

在传递Image对象时,给出上面的代码不满足深度复制。 如果使用该对象进行渲染,则该对象只能有一个位置。这将导致您的代码在同一个地方呈现两个图块。

要解决此问题,您应该克隆Image对象。

 public Tile(Tile copyTile){
     this.sprite = copyTile.sprite.clone();
     this.x = copyTile.x;
     this.y = copyTile.y;
}

答案 4 :(得分:2)

http://www.oracle.com/technetwork/java/seccodeguide-139067.html#6

如果方法返回对内部可变对象的引用,则客户端代码可能会修改实例的内部状态。除非意图共享状态,否则复制可变对象并返回副本。

所以你必须从Image中实际创建一个新实例。

public Tile(Tile copyTile){
     this.sprite = new Image();
     //Then copy the image to the newly instantiated sprite
     this.x = copyTile.x;
     this.y = copyTile.y;
}