美文网首页
Java对象的复制——小明和他的克隆人的故事

Java对象的复制——小明和他的克隆人的故事

作者: 肥兔子爱豆畜子 | 来源:发表于2021-05-30 20:42 被阅读0次

Java对象的复制分为以下3种:

  • 直接赋值
    这个最简单了,Object a=b, 相当于变量a和b都只是指向同一个对象的引用,
    所以不管操作a还是b对这俩对象中的任一个做了更改、比如修改了成员变量的值,那另一个也会变的,因为本质上一直是堆上的同一个对象。
  • 浅拷贝
    跟直接赋值不同,浅拷贝这个确实是创建了另一个不同的对象,对非静态的成员变量进行复制,但是只对基本类型的成员变量进行真正的复制,对引用类型的成员变量只是指向了同一个引用对象。
  • 深拷贝
    在浅拷贝的基础上,对引用类型的成员变量也做了真正的值的拷贝。深拷贝不仅复制对象的本身,也复制了包含的引用所指向的所有的对象。这可能是个递归的过程。

接下来用一个程序代码讲述一个故事,来加深一下深拷贝和浅拷贝的区别。而直接赋值太简单了,就不讲了。
故事是这样的,小明有个女朋友叫小美,小明有一天有了自己的克隆人,这个克隆人也叫小明,但是真正的小明显然是不想跟自己的克隆人共享女朋友的 -_- ,所以他试图将小丽介绍给克隆人当做女朋友,故事就是这样。。。
接下来看代码:

public class Boy implements Cloneable{
    private String name;
    private GirlFriend gf;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public GirlFriend getGf() {
        return gf;
    }
    public void setGf(GirlFriend gf) {
        this.gf = gf;
    }
    
    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class GirlFriend {
    String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class TestClone {
    public static void main(String[] args) {
        Boy xiaoming = new Boy();
        xiaoming.setName("小明");
        GirlFriend xiaomei = new GirlFriend();
        xiaomei.setName("小美");
        xiaoming.setGf(xiaomei);
        
        Boy clone = (Boy) xiaoming.clone(); //小明的克隆人
        
        clone.getGf().setName("小丽"); //克隆人的女朋友
        
        System.out.println(xiaoming.getName() + "的女朋友是" + xiaoming.getGf().getName());
        System.out.println(clone.getName() + "的克隆人的女朋友是" + clone.getGf().getName());
    }
}

运行结果:

小明的女朋友是小丽
小明的克隆人的女朋友是小丽

乱了,全乱了。。。
以上,就是浅拷贝。clone对象赋值了小明的String name,因为名字是个基本类型String,但是女朋友gf是个引用类型GirlFriend,经过拷贝之后clone对象只是简单将自己的gf变量指向了同一个GirlFriend对象。
所以当试图给克隆人重新介绍女朋友的时候,clone.getGf().setName("小丽")也修改了原对象小明的女朋友。酿成了杯具。所以,我们需要深拷贝。
修改上面的代码:

public class GirlFriend implements Cloneable{
    String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public Object clone(){
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class Boy implements Cloneable{
    private String name;
    private GirlFriend gf;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public GirlFriend getGf() {
        return gf;
    }
    public void setGf(GirlFriend gf) {
        this.gf = gf;
    }
    
    @Override
    public Object clone() {
        try {
            GirlFriend gf_clone = (GirlFriend) gf.clone();
            Boy clone = (Boy) super.clone();
            clone.setGf(gf_clone); //女朋友也克隆一个
            return clone;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

运行结果

小明的女朋友是小美
小明的克隆人的女朋友是小丽

正确了,大家都美滋滋。

总结

深拷贝和浅拷贝都是要实现Cloneable接口,然后Override重新Object clone()方法。
但是一个对象如果都只是调用父类也就是Object类的clone方法的话只是实现了浅拷贝,如果要实现深拷贝,方法是在clone方法内部除了调用super.clone()之外,对本对象的引用类型的成员再进行clone一次。就像上面的程序里对gf.clone()那样。
深拷贝的实现还有一种方法,就是对对象进行序列化,然后再反序列化回来,就可以得到一个深拷贝之后的全新的对象了。

相关文章

网友评论

      本文标题:Java对象的复制——小明和他的克隆人的故事

      本文链接:https://www.haomeiwen.com/subject/nbemsltx.html