【问题标题】:Java, is this a Deep copy?Java,这是一个深拷贝吗?
【发布时间】:2013-03-14 16:54:04
【问题描述】:

我似乎无法通过阅读任何类似的问题得到明确的答案,我正在尝试使用复制构造函数在 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);效果就像一个参考,所有这些都与上次分配的位置相同。

【问题讨论】:

    标签: java deep-copy


    【解决方案1】:

    这是深拷贝吗?

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

    如果我要在各自的位置渲染两个图块,那会起作用吗?或者是分配 tileList.get(1).setNewLocation(0,64);效果就像一个参考,所有这些都与上次分配的位置相同。

    不,x 和 y 的值在 Tiles 的两个对象中是独立的,代码应该可以工作,两个对象将具有不同的 x 和 y 值。

    【讨论】:

    • 我要补充一点,int 字段没有这个问题,因为它们是原始数据类型
    • 好的,所以我需要做类似的事情:this.sprite = copyTile.sprite.copy();
    【解决方案2】:

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

    浅拷贝指向与源相同的引用。因此,如果复制名为 B 的实例 A 的副本,对 B 中包含对象的字段的任何更改都会修改 A 中的这些字段,因为它们指向相同的引用。

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

    在你的情况下,我相信你已经做了一个浅拷贝,因为你将 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 属性。

    【讨论】:

    • 我已经意识到它显然是部分复制,图像仍然是一个引用,因为我正在传递一个对象,但 int 被完全复制,因为它们是原语!要使其成为深拷贝 id,必须使用 .clone() 或 new Image(copyImage.sprite) 克隆图像;以便它在内存中创建一个新位置。感谢您的回复!
    • @JayPC 很高兴我能帮到一些忙,听起来你是正确的。好问题!
    【解决方案3】:

    在java中有两种数据类型:

    • 没有深/浅复制概念的基元(float、int、double、boolean ...),因为它们与分配一起使用。
    • 浅拷贝意味着传递引用的对象。而深拷贝意味着拥有一个与源具有相同值的新对象。

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

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

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

    【讨论】:

    • 太棒了!谢谢!它可以被渲染多次,所以我猜我不必像其他答案中提到的那样进行完全深度复制。但现在我知道了区别,当我向 Tile 类添加更多东西时,我会记住这一点。
    【解决方案4】:

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

    查看您的示例,尽管此副本似乎足以满足您的需求,因为它允许您在不同位置重用相同的 Image 对象(这可能更有效)。

    【讨论】:

    • 我现在明白了,谢谢!然后,如果我更改基本图像,所有这些图像都会发生变化,非常好记。
    【解决方案5】:

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

    【讨论】:

    • 对!忘了图片只是参考。
    猜你喜欢
    • 1970-01-01
    • 2013-03-09
    • 2012-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-13
    相关资源
    最近更新 更多