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

一、需求

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值