浅谈java浅层科隆以及深层克隆

版权声明:原创 https://blog.csdn.net/rambler_designer/article/details/89956687

前言 

本文主要讲述clone以及浅层clone(影子clone)和深度clone

并且由于Java不能 通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象

java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的:

也就是说,如果你new了一个User,通过"=="运算符是不能获得另一个User的,举个简单例子

User user = new User();
User user2 = user;

第二行语句,user2 = user;只是将use的引用赋值给user2,此时user和user2指向同一个对象,修改user,user2也会对应发生变化

因此语句user2==user为true

因此想要做到对象的复制就必须使用clone()

首先讲一下浅层克隆(影子克隆)

实现方法:

  • 实现Cloneable接口
  • 重载clone()方法,访问等级提升为public,在Object声明的是protected修饰符

先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容

因此两个对象指向不同的内存空间,修改一个对象,另一个对象当然不会改变

举个例子:

创建一个实体类User,两个属性,重载的clone()方法,以及一个显示的toString方法,

package bean;

public class User implements Cloneable {
    private String name;
    private int age;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Object clone() {
        User copy = null;
        try {
            copy = (User) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return copy;
    }
}

测试代码

package test;

import bean.User;

public class CloneDemo {
    public static void main(String[] args) {
        User user = new User();
        user.setName("rambler");
        user.setAge(100);
        System.out.println("创建的user: "+user.toString());
        User userCopy = (User) user.clone();
        userCopy.setName("副本");
        userCopy.setAge(999);
        System.out.println("通过clone方法获得的user: "+userCopy.toString());
    }
}

运行结果:

创建的user: User{name='rambler', age=100}
通过clone方法获得的user: User{name='副本', age=999}

 通过这种方法,我们可以简单获得一个原对象的副本

但是User属性都是简单数据类型,如果我们增加一个引用数据类型,比如自定义一个Friend类作为User的属性,测试一下结果

package bean;

public class Friend {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

User类中增加Friend属性,并修改toString方法即可,不在贴代码了

测试代码

package test;

import bean.Friend;
import bean.User;

public class CloneDemo {
    public static void main(String[] args) {
        Friend friend = new Friend();
        friend.setAge(15);
        friend.setName("张三");
        User user = new User();
        user.setName("rambler");
        user.setAge(100);
        user.setFriend(friend);
        User userCopy = (User) user.clone();
        userCopy.setName("副本");
        userCopy.setAge(999);
        userCopy.getFriend().setName("李四");
        System.out.println("克隆的user: "+userCopy.toString());
        System.out.println("创建的user: "+user.toString());
    }
}

结果:

克隆的user: User{name='副本', age=999, Friend{name='李四', age=15}}
创建的user: User{name='rambler', age=100, Friend{name='李四', age=15}}

我们只是单纯的修改了userCopy的friend中的name属性,结果user也发生了改变,原因很简单,这是浅层克隆

User在创建是开辟了一块内存空间,name属性和age属性都是直接保存,而Friend则不能直接保存,而是保存了对象的引用,因此在克隆的过程中,副本和原来具有相同的name,age以及Friend对象的引用,因此修改userCopy的Friend,user的Friend也发生了改变

如果想要真正复制User,就必须使用深层克隆了

如果待克隆对象包含其他实体对象的引用,那么这些实体类也必须实现clone方法,才能被克隆

因此Friend类添加Clone方法

package bean;

public class Friend implements Cloneable{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public Object clone(){
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

User类只附带clone部分代码

    public Object clone() {
        User copy = null;
        try {
            copy = (User) super.clone();
            Friend friend = copy.getFriend();
            copy.setFriend((Friend) friend.clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return copy;
    }

实质上深层克隆也就是将迭代将浅层克隆对象中的实体类进行再次克隆

本例中就是先克隆一下User对象,然后User中有Friend对象,因此在将Friend对象再次进行克隆

测试代码

package test;

import bean.Friend;
import bean.User;

public class CloneDemo {
    public static void main(String[] args) {
        Friend friend = new Friend();
        friend.setAge(15);
        friend.setName("张三");
        User user = new User();
        user.setName("rambler");
        user.setAge(100);
        user.setFriend(friend);
        User userCopy = (User) user.clone();
        userCopy.setName("副本");
        userCopy.setAge(999);
        userCopy.getFriend().setName("李四");
        userCopy.getFriend().setAge(25);
        System.out.println("克隆的user: "+userCopy.toString());
        System.out.println("创建的user: "+user.toString());
    }
}

运行结果:

克隆的user: User{name='副本', age=999, Friend{name='李四', age=25}}
创建的user: User{name='rambler', age=100, Friend{name='张三', age=15}}

通过结果可以发现,修改了副本,原来的user并没有发生变化

猜你喜欢

转载自blog.csdn.net/rambler_designer/article/details/89956687