美文网首页
深克隆与浅克隆

深克隆与浅克隆

作者: Lucksheep | 来源:发表于2020-06-16 15:46 被阅读0次

    一、克隆的作用

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

    二、克隆基本简介

    我们说的克隆,都是基于超类 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
    

    相关文章

      网友评论

          本文标题:深克隆与浅克隆

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