Java 中的 Clone()

Clone() 方法是 Object 类提供的方法,顾名思义,通过调用改方法可以拷贝/克隆一个对象,作为 Java 语言的一个基本特性,在面试和工作中会经常遇到。方法签名如下:

protected native Object clone() throws CloneNotSupportedException;

使用方法

一个对象要使用 clone() 方法来克隆对象时,改对象要继承 Cloneable 接口,这个接口是一个标记接口,然后重写 Object 对象的 clone() 方法,并设置方法的访问修饰符为 public。在重写的 clone() 方法中调用父类的 clone() 方法也就是 Object 的 clone() 方法,该方法的返回值是 Object 类型,所以我们要强制类型转换成我们期待的类型。

public class Person implements Cloneable{

    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public Person clone() {
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

深拷贝和浅拷贝

Java 中的对象拷贝分为深拷贝和浅拷贝,所谓浅拷贝,就是使用上述方法生成的拷贝对象。

  • 浅拷贝:基本数据类型的成员变量拷贝值,引用类型的成员变量拷贝引用地址
  • 深拷贝:基本数据类型的成员变量拷贝值,引用类型的成员变量递归的调用 clone() 方法

clone() 方法返回的对象总是一个新的对象,当时如果是浅拷贝,对象中的引用类型变量和被拷贝对象中的是同一个对象,而深拷贝中的引用类型变量会是一个新的对象。

通过简单的测试代码即可看出其中的差别:

public class Person implements Cloneable{

    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public Person clone() {
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class Teacher implements Cloneable{
    public Person person;
    public int id;
    public String schoolName = "清华大学";

    public Teacher(Person person, int id, String schoolName) {
        this.person = person;
        this.id = id;
        this.schoolName = schoolName;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "person=" + person +
                ", id=" + id +
                ", schoolName='" + schoolName + '\'' +
                '}';
    }

    @Override
    public Teacher clone() {
        try {
            return (Teacher) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class DeepCloneTeacher extends Teacher {

    public DeepCloneTeacher(Person person, int id, String schoolName) {
        super(person, id, schoolName);
    }

    @Override
    public Teacher clone() {
        Teacher teacher = super.clone();
        teacher.person = person.clone();
        return teacher;
    }
}
@Test
public void test02(){
    // 浅拷贝
    Teacher teacher = new Teacher(new Person("小红", 12), 50, "清华大学");
    Teacher clone = teacher.clone();

    System.out.println("teacher == clone: " + (teacher == clone));
    System.out.println("teacher.person == clone.person: " + (teacher.person == clone.person));
    System.out.println("teacher.toString(): " + teacher.toString());
    System.out.println("clone.toString(): " + clone.toString());
    System.out.println("=================================");

    // 深拷贝
    Teacher deepCloneTeacher = new DeepCloneTeacher(new Person("小红", 12), 50, "清华大学");
    Teacher clone2 = deepCloneTeacher.clone();

    System.out.println("deepCloneTeacher == clone2: " + (deepCloneTeacher == clone2));
    System.out.println("deepCloneTeacher.person == clone2.person: " + (deepCloneTeacher.person == clone2.person));
    System.out.println("deepCloneTeacher.toString(): " + deepCloneTeacher.toString());
    System.out.println("clone2.toString(): " + clone2.toString());
}

输出:

teacher == clone: false
teacher.person == clone.person: true
teacher.toString(): Teacher{person=Person{name='小红', age=12}, id=50, schoolName='清华大学'}
clone.toString(): Teacher{person=Person{name='小红', age=12}, id=50, schoolName='清华大学'}
=================================
deepCloneTeacher == clone2: false
deepCloneTeacher.person == clone2.person: false
deepCloneTeacher.toString(): Teacher{person=Person{name='小红', age=12}, id=50, schoolName='清华大学'}
clone2.toString(): Teacher{person=Person{name='小红', age=12}, id=50, schoolName='清华大学'}

数组对象的 clone()

在 Java 中,数组也是一个对象,那么它也可以调用 clone() 方法来拷贝自己,对于以下几种情景它的行为会是怎么样被?

  • 一维基本数据类型数组是深拷贝还是浅拷贝

  • 多维基本数据类型数组是深拷贝还是浅拷贝

  • 一维引用数据类型数组是深拷贝还是浅拷贝

测试代码如下:

@Test
public void test01() {
    // 引用类型一维数组
    Person[] persons = new Person[2];
    persons[0] = new Person("小红", 15);
    persons[1] = new Person("小名", 20);
    Person[] clone = persons.clone();

    System.out.println("persons == clone:" + (persons == clone));
    System.out.println("persons[0] == clone[0]:" + (persons[0] == clone[0]));
    System.out.println("persons.toString():" + Arrays.toString(persons));
    System.out.println("clone.toString():" + Arrays.toString(clone));
    System.out.println("==============================");

    // 基本数据类型一维数组
    int[] ints = new int[2];
    ints[0] = 1;
    ints[1] = 2;
    int[] clone1 = ints.clone();

    System.out.println("ints == clone1:" + (ints == clone1));
    System.out.println("ints[0] == clone1[0]:" + (ints[0] == clone1[0]));
    System.out.println("ints.toString():" + Arrays.toString(ints));
    System.out.println("clone1.toString():" + Arrays.toString(clone1));
    System.out.println("==============================");


    // 基本数据类型二维数组
    int[][] intss = new int[2][2];
    intss[0][0] = 0;
    intss[0][1] = 1;
    intss[1][0] = 2;
    intss[1][1] = 3;
    int[][] clone2 = intss.clone();

    System.out.println("intss == clone2:" + (intss == clone2));
    System.out.println("intss[0] == clone2[0]:" + (intss[0] == clone2[0]));
    System.out.println("intss.toString():");
    for (int[] intss1 : intss) {
        System.out.println(Arrays.toString(intss1));
    }
    System.out.println("clone2.toString():");
    for (int[] ints1 : clone2) {
        System.out.println(Arrays.toString(ints1));
    }

}

输出:

persons == clone:false
persons[0] == clone[0]:true
persons.toString():[Person{name='小红', age=15}, Person{name='小名', age=20}]
clone.toString():[Person{name='小红', age=15}, Person{name='小名', age=20}]
==============================
ints == clone1:false
ints[0] == clone1[0]:true
ints.toString():[1, 2]
clone1.toString():[1, 2]
==============================
intss == clone2:false
intss[0] == clone2[0]:true
intss.toString():
[0, 1]
[2, 3]
clone2.toString():
[0, 1]
[2, 3]

由输出结果可知:

  • 数组对象的 clone() 方法的行为和其他对象并无差别,即都是浅拷贝
  • 当数组是一维基本数据类型的时候,简单的浅拷贝即可实现拷贝对象的需求
  • 当数组是一维引用数据类型的时候,因为是浅拷贝,所以拷贝数组中的元素和被拷贝数据中的元素是同一个对象
  • 当数组是二维基本数据类型的时候,二维数组相当于是一维数组的数组,而数组是引用数据类型,所以二维基本数据类型 clone() 方法的行为相当于一个一维引用数据类型的行为

拷贝构造器和拷贝工厂

除了使用 clone() 我们还有其他拷贝对象的方法吗?有。

在 《Effective Java》 中提到

对象拷贝的更好的方法是提供一个拷贝构造器或拷贝工厂。拷贝构造器就是一个参数为类的构造器。例如:

public Yum(Yum yum{...})

拷贝工厂是类似于拷贝构造器的静态工厂:

public static Yum newInstance(Yum yum){...}

在 Java 集合框架中,所有通用集合实现都提供了一个拷贝构造器,其参数类型为 Collection 或者 Map 接口,比如 new HashMap(Map map)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值