Java克隆clone的浅复制与深复制

来源:互联网 发布:alex卞正伟淘宝店 编辑:程序博客网 时间:2024/05/07 14:37

一、需求

1.问题引入:

假设在你的应用中想修改某个对象的属性或值,比如:我想修改某个ArrayList 集合对象list存有的元素,但我又不想影响原来的集合对象list,那么该怎么做呢?

或许你会想到直接这样 ArrayList list2 = list;  不就行了吗?呵呵,其实这样不是拷贝,这样的话list2 就完全等于list,操作list2即是操作list。

换换言之,在你执行这个操作后仍然只有一个对象,你只是多加了一个该list对象的一个引用。那么该如何解决这个问题呢?

2.解决方案:

使用所有类的父类Object 的clone()方法。

3.测试用例:

package org.xjh.test;import java.util.ArrayList;/** * ArrayList的clone方法测试 * @author xjh * */public class CloneTest {public static void main(String[] args) {ArrayList list = new ArrayList();list.add("123");list.add(1);list.add(100.0);System.out.println("初始list集合中的所有元素:" + list);ArrayList list2 = list;list2.remove(0);System.out.println("移除list2集合第一个元素之后,list集合的所有元素:" +list);ArrayList list3 = (ArrayList) list.clone();list3.remove(0);System.out.println("移除list3集合第一个元素之后,list集合的所有元素:" + list);}}


4.测试结果:

初始list集合中的所有元素:[123, 1, 100.0]
移除list2集合第一个元素之后,list集合的所有元素:[1, 100.0]
移除list3集合第一个元素之后,list集合的所有元素:[1, 100.0]


二、自定义可克隆的类(浅复制)


1.定义

浅拷贝(浅复制、浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。

2.实现步骤:
  1).在自定义类中覆盖Object类的clone()方法,并声明为public(Object类中的clone()方法是protected的,在子类重写的时候,可以扩大访问修饰符的范围)。
  2).在自定义类的clone()方法中,调用super.clone()。
  因为在运行时刻,Object类中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
  3).在派生类中实现Cloneable接口。Cloneable接口中没有任何方法,只是作一种声明,表示该类具有复制的作用。

2.测试用例:

package org.xjh.test;import java.util.ArrayList;/** * 浅复制测试 * @author xjh * */public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {Student s1 = new Student("张三", 12);Student s2 = (Student) s1.clone();Student s3 = s1;System.out.println("s1的初始age的值="+s1.getAge());s2.setAge(20);System.out.println("修改s2的age值="+s2.getAge());System.out.println("修改s2的age值后,s1的age的值="+s1.getAge());s3.setAge(30);System.out.println("修改s3的age值="+s3.getAge());System.out.println("修改s2的age值后,s1的age的值="+s1.getAge());}}/** * 实现Cloneable接口并且覆写Object的clone方法的Student类 * @author xjh * */class Student implements Cloneable{    private String name;    private int age;        public Student() {super();}public Student(String name, int age) {super();this.name = name;this.age = 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;    }    /**     * 覆写Object类的clone方法,注意此处要把protected改为public,不然没法调用     */    @Override    public Object clone() throws CloneNotSupportedException    {    return super.clone();    }}

3.测试结果:

s1的初始age的值=12
修改s2的age值=20
修改s2的age值后,s1的age的值=12
修改s3的age值=30
修改s2的age值后,s1的age的值=30


三、自定义可复制的类(深复制)

1.定义

深拷贝(深复制、深克隆):被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。
那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。
换言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

2.实现步骤
        前提假设,A类中定义了一个属性,这个属性是另外一个类B的引用对象b。

  1).在自定义类中覆盖Object类的clone()方法,并声明为public(Object类中的clone()方法是protected的,在子类重写的时候,可以扩大访问修饰符的范围)。
  2).在自定义类的clone()方法中,调用super.clone()。
  因为在运行时刻,Object类中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
  3).在派生类中实现Cloneable接口。Cloneable接口中没有任何方法,只是作一种声明,表示该类具有复制的作用。

        4).在A类的clone方法中调用B类引用对象b的clone方法,并且让b重新指向b.clone的对象


3. 测试用例

package org.xjh.test;/** * 深复制测试 * @author xjh * */public class CloneTest2{    public static void main(String[] args) throws Exception    {        Teacher teacher = new Teacher();        teacher.setName("Teacher Zhang");        teacher.setAge(40);        Student2 student1 = new Student2();        student1.setName("ZhangSan");        student1.setAge(20);        student1.setTeacher(teacher);        Student2 student2 = (Student2) student1.clone();        System.out.println("拷贝得到的信息");        System.out.println(student2.getName());        System.out.println(student2.getAge());        System.out.println(student2.getTeacher().getName());        System.out.println(student2.getTeacher().getAge());        System.out.println("-------------");        // 修改老师的信息        teacher.setName("Teacher Zhang has changed");        System.out.println(student1.getTeacher().getName());        System.out.println(student2.getTeacher().getName());        // 两个引用student1和student2指向不同的两个对象        // 但是两个引用student1和student2中的两个teacher引用指向的是同一个对象        // 所以说明是浅拷贝        // 改为深复制之后,对teacher对象的修改只能影响第一个对象    }}class Teacher 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 Object clone() throws CloneNotSupportedException    {        return super.clone();    }}class Student2 implements Cloneable{    private String name;    private int age;    private Teacher teacher;    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;    }    public Teacher getTeacher()    {        return teacher;    }    public void setTeacher(Teacher teacher)    {        this.teacher = teacher;    }    @Override    public Object clone() throws CloneNotSupportedException    {        // 浅复制时:        // Object object = super.clone();        // return object;        // 改为深复制:        Student2 student = (Student2) super.clone();        // 本来是浅复制,现在将Teacher对象复制一份并重新set进来        student.setTeacher((Teacher) student.getTeacher().clone());                //当然,也可以这样做,效果与上面的一样        //this.teacher = (Teacher) student.getTeacher().clone();                return student;    }}

3.测试结果:

拷贝得到的信息
ZhangSan
20
Teacher Zhang
40
-------------
Teacher Zhang
Teacher Zhang has changed



四、应用场景

那么,什么时候需要进行对象的克隆呢,一般需要满足以下条件:
  1. 克隆对象与原对象不是同一个对象。即对任何的对象x:
  x.clone() != x
  2.克隆对象与原对象的类型一样。即对任何的对象x:
  x.clone().getClass() == x.getClass()
  3.如果对象x的equals()方法定义恰当,那么下式应该成立:
  x.clone().equals(x)
  因为一个定义良好的equals()方法就应该是用来比较内容是否相等的。



0 0
原创粉丝点击