Java 深克隆与浅克隆 详解

#一、克隆的作用
快速构建一个和已有对象相同的副本,创建一个新对象,将已有对象的数据导入到新对象里面;

#二、克隆基本简介
我们说的克隆,都是基于超类 Object 来的,里面有个native方法,具体实现是它调用底层C语言的实现,我们是看不到的

protected native Object clone() throws CloneNotSupportedException;

由此可知,有几个约束

  1. 使用时必须继承Object类,我们所有的类都是Object派生的
  2. 接收对象必须强转
  3. 必须实现 Cloneable 接口标识 (表示重写了clone() )

不实现这个接口会发生什么呢?

public class User{

    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        User user=new User();
        user.setUserName("zhangsan");
        System.out.println(JSONObject.toJSONString(user));
        User user1= (User) user.clone();
        System.out.println(JSONObject.toJSONString(user1));
    }
}

结果:

{"userName":"zhangsan"}
Exception in thread "main" java.lang.CloneNotSupportedException: com.demo.cs.study.clones.User
	at java.lang.Object.clone(Native Method)
	at com.demo.cs.study.clones.User.clone(User.java:19)
	at com.demo.cs.study.clones.User.main(User.java:32)

添加实现后

public class User implements Cloneable{

    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        User user=new User();
        user.setUserName("zhangsan");
        System.out.println(JSONObject.toJSONString(user));
        User user1= (User) user.clone();
        System.out.println(JSONObject.toJSONString(user1));
    }
}

结果:

{"userName":"zhangsan"}
{"userName":"zhangsan"}

1、浅克隆

概念:如果被复制的对象所有的变量与原来的变量值相同,且所持有对其它对象的引用任然指向原来的对象就叫浅克隆

User.java

public class User implements Cloneable{

    private String userName;

    private HavingDinner havingDinner;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public HavingDinner getHavingDinner() {
        return havingDinner;
    }

    public void setHavingDinner(HavingDinner havingDinner) {
        this.havingDinner = havingDinner;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

HavingDinner.java

public class HavingDinner {

    private String foodName;

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public HavingDinner(String foodName) {
        this.foodName = foodName;
    }
}

Demo.java

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("青椒炒萝卜");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

结果:

哈比-->今天晚上要吃青椒炒萝卜  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃青椒炒萝卜  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃青椒炒萝卜  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380

看输出结果,引用地址一样。这就是浅克隆;那么问题来了,哈比要吃土豆鸡块,我们改下

Demo2

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("土豆鸡块");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

结果:

哈比-->今天晚上要吃土豆鸡块  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃土豆鸡块  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃土豆鸡块  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380

会发现其它人都给吃土豆鸡块了,我们再改下哈比2的试试

Demo3:

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("土豆鸡块");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        user1.getHavingDinner().setFoodName("冰淇淋");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

结果:

哈比-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380

结果还是 都吃冰淇淋,这样的话就有问题了,我想哈比吃土豆鸡块,哈比2吃冰淇淋;
这样也证实了上面的概念;基本属性的值是复制了,但是其它对象里的属性根本就没有复制,只是单纯的复制了引用地址;

2、深克隆

User.java

public class User implements Cloneable{

    private String userName;

    private HavingDinner havingDinner;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public HavingDinner getHavingDinner() {
        return havingDinner;
    }

    public void setHavingDinner(HavingDinner havingDinner) {
        this.havingDinner = havingDinner;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException{
        User user=(User) super.clone();
        user.setHavingDinner((HavingDinner) getHavingDinner().clone());
        return user;
    }
}

HavingDinner.java

public class HavingDinner implements Cloneable{

    private String foodName;

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public HavingDinner(String foodName) {
        this.foodName = foodName;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

Demo.java

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("土豆鸡块");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        user1.getHavingDinner().setFoodName("冰淇淋");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

结果:

哈比-->今天晚上要吃土豆鸡块  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@29453f44
哈士奇-->今天晚上要吃土豆鸡块  引用地址:com.demo.cs.study.clones.HavingDinner@5cad8086

总结:达到了互不影响的结果,对象里面包含的对象也复制了,这是第一种方式,有点复杂;
2、序列化深克隆
原理:把对象序列化输出到流里面,然后把流里面的数据序列化出来,得到一个新的对象
事例:
User.java

public class User implements Serializable {

    private String userName;

    private HavingDinner havingDinner;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public HavingDinner getHavingDinner() {
        return havingDinner;
    }

    public void setHavingDinner(HavingDinner havingDinner) {
        this.havingDinner = havingDinner;
    }

    protected User deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);
        ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
        return (User) objectInputStream.readObject();
    }
}

HavingDinner.java

public class HavingDinner implements Serializable {

    private String foodName;

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public HavingDinner(String foodName) {
        this.foodName = foodName;
    }
}

测试类

public class Demo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        HavingDinner hd=new HavingDinner("土豆鸡块");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1=user.deepClone();
        user1.setUserName("哈比2");
        user1.getHavingDinner().setFoodName("冰淇淋");
        User user2=user.deepClone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

结果:

哈比-->今天晚上要吃土豆鸡块  引用地址:com.demo.cs.study.clones.HavingDinner@5e2de80c
哈比2-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@34a245ab
哈士奇-->今天晚上要吃土豆鸡块  引用地址:com.demo.cs.study.clones.HavingDinner@7cc355be
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值