对象的克隆

发表时间:2017-05-10 14:09:00 浏览量( 23 ) 留言数( 0 )

学习目标:

1、了解Java的历史

2、为什么要学习Java语言

3、端正学习态度


学习过程:

一、对象克隆简介

1、什么是对象的克隆

在java面向对象的编程当中,要复制引用类型的对象,就必须克隆这些对象。克隆对象,就是为新的对象分配空间,并进行对象的复制,并将原始对象的内容一一复制到新的对象空间去。

我们在编码过程经常会碰到将一个对象传递给另一个对象,java中对于基本型变量采用的是值传递,而对于对象比如JavaBean传递时采用的是引用传递,而很多时候对于对象传递我们也希望能够象值传递一样,使得传递之前和之后有不同的内存空间,这就是对象的克隆:一个新的对象和一个新的对象数据空间。

2、赋值不能实现克隆。

实现克隆需要通过调用可用的clone方法。如果是值类型的实例,那么“=”赋值运算符就可以将源对象的状态逐字节地复制到目标对象中。但是对象是对象空间的引用,所以赋值“=”方式只能是复制对象引用,是不会复制对象空间的。我们看看下面的代码:

(1)先构造一个Father类,实现代码如下;

  public class Father  {

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

}

(2)新建一个Run类,构造一个father1对象,并为其属性name赋值“刘邦”,然后把father1对象赋值给另外一个对象father2,打印father2的name属性,和father1一样,但是如果我们修改father2对象的name属性,这时候father1对象的name属性也会发生改变,也就是father1对象和father2对象指向的对象空间是同一个,并没有克隆。实现代码如下:

public class Run {

	public static void main(String[] args) {

		Father father1 = new Father();
		father1.setName("刘邦");

		Father father2 = father1;

		System.out.println("father2的name="+father2.getName());
		//修改father2的name属性
		father2.setName("李世民");
		System.out.println("====修改后====");
		System.out.println("father2的name="+father2.getName());
		//father1的name属性 也改变了。
		System.out.println("father1的name="+father1.getName());

	}

}

运行结果如下:

attcontent/f4172a5f-db53-4d4a-b162-274c83aa1afa.png

也就是说赋值仅仅只是对象引用的复制,不是空间的复制,图示如下:

attcontent/eff9fb18-4bf8-413b-9e62-8a8c83f46880.png

也就是说对象克隆失败了。那如何才能真正做到对象的克隆呢?

二、对象的克隆的基本步骤

要实现对象的克隆我们需要完成按照以下几个步骤,然后调用clone()方法实现克隆。

1、可以利用Object类的clone()方法。实现Cloneable接口。

2、在派生类中覆盖基类的clone(),最好修改访问修饰符为public。

2、在派生类的clone()方法中,调用super.clone()。

示例代码如下:

1、让Father类实现Cloneable接口,并重写clone()方法。代码如下:

//1、实现Cloneable  接口
public class Father implements Cloneable {

	private String name;

	public String getName() {
		return name;
	}

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

	@Override
	public Father clone() {

		try {
			Object object = super.clone();
			Father father = (Father) object;
			return father;
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}

2、修改原来的Run类的main方法,通过调用clone()方法完成克隆。

public class Run {

	public static void main(String[] args) {

		Father father1 = new Father();
		father1.setName("刘邦");

		Father father2 = father1.clone();//通过调用clone()方法

		System.out.println("father2的name="+father2.getName());
		//修改father2的name属性
		father2.setName("李世民");
		System.out.println("====修改后====");
		System.out.println("father2的name="+father2.getName());
		//father1的name属性 也改变了。
		System.out.println("father1的name="+father1.getName());

	}

}

唯一修改的代码就是把赋值改成调用clone()方法,这时候再次运行就会修改了father2对象的name属性,不会对father1的name属性有任何影响,因为现在有两个对象引用,指向两个不同的对象空间。运行结果如下:

attcontent/69604af7-c7fe-483d-ab6a-cbb68f1eaeb9.png

三、深度克隆

如果Father类的属性是一个对象,那么该对象并不会克隆的,如果你想要也克隆一份,那么该对象属性的实现类也需要实现克隆,我们称为深度克隆。

1、测试Father的属性是对象能否克隆成功。

比如我们添加一个Child类。实现代码如下:

public class Child {

	private String name;

	public String getName() {
		return name;
	}

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

}

把这个Child类作为Father类的一个属性。

//1、实现Cloneable  接口
public class Father implements Cloneable {

	private String name;
	private Child child;
	
	public Child getChild() {
		return child;
	}

	public void setChild(Child child) {
		this.child = child;
	}

	public String getName() {
		return name;
	}

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

	@Override
	public Father clone() {

		try {
			Object object = super.clone();
			Father father = (Father) object;
			return father;
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}

修改Run方法,测试Child有没有克隆。

public class Run {

	public static void main(String[] args) {

		Father father1 = new Father();
		father1.setName("刘邦");
		
		//设置Child属性。
		Child child=new Child();
		child.setName("刘三儿");
		father1.setChild(child);

		Father father2 = father1.clone();//通过调用clone()方法

		System.out.println("father2的name="+father2.getName());
		System.out.println("father2的child的name="+father2.getChild().getName());
		//修改father2的name属性
		father2.setName("李世民");
		System.out.println("====修改后====");
		System.out.println("father2的name="+father2.getName());
		System.out.println("father1的name="+father1.getName());
		
		System.out.println("=====Child======");
		father2.getChild().setName("李四");
		System.out.println("father2的child的name="+father2.getChild().getName());
		System.out.println("father1的child的name="+father1.getChild().getName());

	}

}

运行结果如下:

attcontent/22c01bf8-0215-4eeb-8337-6a6d40e94a12.png

也就是说Child并没有克隆。

2、实现深度克隆。

让Child也可克隆:

public class Child implements Cloneable {

	private String name;
	public String getName() {
		return name;
	}

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

	@Override
	public Child clone() {

		try {
			Object object = super.clone();
			Child child = (Child) object;
			return child;
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return null;
	}

}

修改Father类的clone()方法,在克隆Father的时候同时把Child也克隆一份:

@Override
	public Father clone() {

		try {
			Object object = super.clone();
			Father father = (Father) object;
			
			//同时克隆Child对象
			Child child=father.getChild().clone();
			father.setChild(child);
			
			return father;
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

再次运行Run类,结果如下:

attcontent/c168ff90-6755-4f70-92a7-123e0530ca27.png