对java Object中clone方法的一点理解

先看看object对象中的源代码:

/**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object. The general
     * intent is that, for any object {@code x}, the expression:
     * <blockquote>
     * <pre>
     * x.clone() != x</pre></blockquote>
     * will be true, and that the expression:
     * <blockquote>
     * <pre>
     * x.clone().getClass() == x.getClass()</pre></blockquote>
     * will be {@code true}, but these are not absolute requirements.
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * x.clone().equals(x)</pre></blockquote>
     * will be {@code true}, this is not an absolute requirement.
     * <p>
     * By convention, the returned object should be obtained by calling
     * {@code super.clone}.  If a class and all of its superclasses (except
     * {@code Object}) obey this convention, it will be the case that
     * {@code x.clone().getClass() == x.getClass()}.
     * <p>
     * By convention, the object returned by this method should be independent
     * of this object (which is being cloned).  To achieve this independence,
     * it may be necessary to modify one or more fields of the object returned
     * by {@code super.clone} before returning it.  Typically, this means
     * copying any mutable objects that comprise the internal "deep structure"
     * of the object being cloned and replacing the references to these
     * objects with references to the copies.  If a class contains only
     * primitive fields or references to immutable objects, then it is usually
     * the case that no fields in the object returned by {@code super.clone}
     * need to be modified.
     * <p>
     * The method {@code clone} for class {@code Object} performs a
     * specific cloning operation. First, if the class of this object does
     * not implement the interface {@code Cloneable}, then a
     * {@code CloneNotSupportedException} is thrown. Note that all arrays
     * are considered to implement the interface {@code Cloneable} and that
     * the return type of the {@code clone} method of an array type {@code T[]}
     * is {@code T[]} where T is any reference or primitive type.
     * Otherwise, this method creates a new instance of the class of this
     * object and initializes all its fields with exactly the contents of
     * the corresponding fields of this object, as if by assignment; the
     * contents of the fields are not themselves cloned. Thus, this method
     * performs a "shallow copy" of this object, not a "deep copy" operation.
     * <p>
     * The class {@code Object} does not itself implement the interface
     * {@code Cloneable}, so calling the {@code clone} method on an object
     * whose class is {@code Object} will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @throws  CloneNotSupportedException  if the object's class does not
     *               support the {@code Cloneable} interface. Subclasses
     *               that override the {@code clone} method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
    protected native Object clone() throws CloneNotSupportedException;

从注释内容中我们可以得到以下几点信息:

  • 这个方法能创建并返回对象的拷贝。
  • 返回的拷贝对象包含原有对象拥有的属性值。
  • 拷贝分为浅拷贝和深拷贝。
  • 如果这个类没有实现Cloneable接口,那么对象在调用clone()方法时将报异常。

然而我们在什么情况下使用这个方法呢?试想这样一种情况:我们创建了一个对象,这个对象中有很多属性,我们已经为这些属性赋了值,当我们想创建一个新的对象时我们就可以使用这个方法了。当然你可能会说为什么不直接new一个新的对象呢,但是别忘了在new新的对象时,我们的新对象中的属性是初始值,在使用clone方法复制这个对象时,我们获得的对象可以拥有原有对象的属性值。

先写几个类试一下这个功能:

1.Student类:

public class Student 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
	protected Student clone() throws CloneNotSupportedException {
		Student stu = (Student) super.clone();
		return stu;
	}

}
2.Teacher类:
public class Teacher {
	private String name;
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
3.测试类:

@Test
    public void test_shallowCopy() throws CloneNotSupportedException {
        Teacher t1 = new Teacher();
        t1.setName("t1");

        Student student = new Student();
        student.setName("s1");
        student.setAge(10);
        student.setTeacher(t1);
        
        Student clone = student.clone();
        System.out.println("原来对象的地址:" + student);
        System.out.println("拷贝对象的地址:" + clone);
        
        System.out.println("原来对象的教师地址:" + student.getTeacher());
        System.out.println("拷贝对象的教师地址:" + student.getTeacher());
        //测试对字符串修改是否影响原来对象,不同则不影响
        clone.setName("s2");
        System.out.println("原来对象的名字:" + student.getName());
        System.out.println("拷贝对象的名字:" + clone.getName());
        //测试对基本类型修改是否影响原来对象,不同则不影响
        clone.setAge(20);
        System.out.println("原来对象的年龄:" + student.getAge());
        System.out.println("拷贝对象的年龄:" + clone.getAge());
        //测试对应用类型修改是否影响原来对象,不同则不影响
        clone.getTeacher().setName("t2");
        System.out.println("原来对象的教师名字:" + student.getTeacher().getName());
        System.out.println("拷贝对象的教师名字:" + clone.getTeacher().getName());
    }

输出结果:
原来对象的地址:clone.Student@4ee285c6
拷贝对象的地址:clone.Student@621be5d1
原来对象的教师地址:clone.Teacher@573fd745
拷贝对象的教师地址:clone.Teacher@573fd745
原来对象的名字:s1
拷贝对象的名字:s2
原来对象的年龄:10
拷贝对象的年龄:20
原来对象的教师名字:t2
拷贝对象的教师名字:t2

在内存中的体现如下:

由上可知我们在内存中创建了一个新的对象,但是新的对象中的引用类型变量teacher却和原有对象指向同一个地址,这意味着我们在更改clone对象的teacher属性时,我们不可避免的要更改原有对象(即上图student对象)的teacher属性。(注:你可能会问为什么String类型变量name也指向同一个地址为什么,改变拷贝对象String的值原来对象的String的值未改变,这是因为String类型是final修饰的不可改变,只能创建一个地址,当改变拷贝对象的name是name不再指向s1,而是指向新地址s2了)上述过程被称为浅拷贝,可以对基本数据类型和直接复制的String变量进行拷贝,但是无法对引用数据类型变量进行拷贝。这往往是我们不希望看到的结果,这时候我们就不能使用默认的浅拷贝了,应该使用深拷贝。

那么如何使用深拷贝呢?深拷贝即让原来对象和拷贝的新对象中的引用类型指向不同的地址,各自拥有一块内存空间。所以在本例中我们只需修改Student类的clone方法即可,修改如下:

	@Override
	protected Student clone() throws CloneNotSupportedException {
		Student stu = (Student) super.clone();
		//添加修改内容
		Teacher tea = new Teacher();
		tea.setName(stu.getTeacher().getName());
		stu.setTeacher(tea);

		return stu;
	}

运行结果如下:

原来对象的教师名字:t1
拷贝对象的教师名字:t2
总结:当我们编写的类只有基本类型和String类型时,可以只使用浅拷贝,当有引用类型时应该使用深拷贝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值