Java clone()克隆对象

一、java 方法参数 理解:

方法参数 可理解为: 对于输入的实参 进行了一份拷贝,  (1) 若方法参数为基本类型,则在栈内存中开辟新的空间, 所有的方法体内部的操作都是针对这个拷贝的操作,并不会影响原来输入实参的值 (2)若方法参数为引用类型,该拷贝与输入实参指向了同一个对象,方法体内部对于对象的操作,都是针对的同一个对象。

另外,除了在函数传值的时候是"引用传递",在任何用""向对象变量赋值的时候都是"引用传递"。

下面举例说明:

package com.lqh.test;

import java.util.*;

public class HashtableAdd {
	public static void main(String[] args) {
		Hashtable<String, StringBuffer> ht = new Hashtable<String, StringBuffer>();
		StringBuffer sb = new StringBuffer();
		sb.append("abc,");
		ht.put("1", sb);
		sb.append("def,");
		ht.put("2", sb);
		sb.append("mno,");
		ht.put("3", sb);
		sb.append("xyz.");
		ht.put("4", sb);

		int numObj = 0;
		Enumeration it = ht.elements();
		while (it.hasMoreElements()) {
			System.out.print("get StringBufffer " + (++numObj)
					+ " from Hashtable: ");
			System.out.println(it.nextElement());
		}
	}
}

上面的例子的实际输出的结果是:

get StringBufffer 1 from Hashtable: abc,def,mno,xyz.
get StringBufffer 2 from Hashtable: abc,def,mno,xyz.
get StringBufffer 3 from Hashtable: abc,def,mno,xyz.
get StringBufffer 4 from Hashtable: abc,def,mno,xyz.

分析:向Hashtable 传递 StringBuffer 对象是只传递了这个StringBuffer 对象的引用!每一次向Hashtable 表中put 一次 StringBuffer ,并没有生成新的StringBuffer 对象,只是在Hashtable 表中又放入了一个指向同一StringBuffer 象的引用而已。 Hashtable 表存储的任何一个StringBuffer 对象(更确切的说应该是对象的引用)的改动,实际上都是对同一个 "StringBuffer" 的改动。所以Hashtable 并不能真正存储能对象,而只能存储对象的引用。也应该知道这条原则对与Hashtable 似的Vector, List, Map, Set 等都是一样的。

二、Java Clone()介绍

顾名思义,clone方法的含义就是克隆出一个一模一样的对象,这样是有两个对象的。

实现clone方法的步骤()

(1)实现Cloneable接口

(2)重载Object类中的clone()方法,重载时需定义为public

(3)在重载方法中,调用super.clone()

例如:

class CloneClass implements Cloneable {
    public int aInt;

    public Object clone() {
        CloneClass o = null;
        try {
            o = (CloneClass) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }
}

解释:

(1)clone()方法是定义在java.lang.Object类中,该方法是一个protected的方法,所以重载时要把clone()方法的属性设置为public,这样其它类才能调用这个clone类的clone()方法

(2)实现Cloneable接口:Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Objectclone()法(也就是调用了super.Clone()方法),那么Objectclone()方法就会抛出 CloneNotSupportedException异常。


三、浅克隆与深克隆

所谓浅克隆就是说被克隆的对象各个域都是基本类型,而不存在引用类型。如有存在引用类型的域,则需要进行深克隆。深克隆就是在重载clone()方法中,要对其引用类型的域也进行克隆。如下例:

package com.lqh.clone;

public class Address implements Cloneable{
	
	private String state;
	private String province;
	private String city;
	
	public String getState() {
		return state;
	}
	public void setState(String state) {
		this.state = state;
	}
	public String getProvince() {
		return province;
	}
	public void setProvince(String province) {
		this.province = province;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("国家:" + state + ", ");
		sb.append("省:" + province + ", ");
		sb.append("市:" + city);
		return sb.toString();
	}
	public Address(String state, String province, String city) {
		super();
		this.state = state;
		this.province = province;
		this.city = city;
	}
	
	@Override
	public Address clone() {
		Address address = null;
		try {
			address = (Address) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return address;
	}
}

package com.lqh.clone;

public class Employee implements Cloneable{
	private String name;
	private int age;
	private Address address;
	
	public Employee(String name, int age, Address address) {
		super();
		this.name = name;
		this.age = age;
		this.address = address;
	}
	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 Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	@Override
	public Employee clone(){
		Employee employee = null;
		try {
			employee = (Employee) super.clone();  
			employee.address = address.clone();  //对引用类型的域进行克隆
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return employee;
	}
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("姓名:" + name + ",");
		sb.append("年龄:" + age+ ", ");
		sb.append("地址:" + address);
		return sb.toString();
	}
	
	public static void main(String[] args) {
		System.out.println("克隆之前:");
		Address address = new Address("中国", "吉林", "长春");
		Employee employee1 = new Employee("明日科技", 12, address);
		System.out.println("员工1信息:" + employee1 );
		
		Employee employee2 = employee1.clone();
		
		employee2.getAddress().setState("中国");
		employee2.getAddress().setProvince("四川");
		employee2.getAddress().setCity("成都");
		
		System.out.println("克隆之后:");
		System.out.println("员工2信息:" + employee2);
		System.out.println("员工1信息:" + employee1);
	}
}

Object类中clone()方法产生的过程是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,若不使用深克隆,即不对引用类型的域进行克隆,会导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。

不是所有的类都能实现深度clone,StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个 final类,这也是说我们也不能用继承的办法间接实现StringBufferclone。如果一个类中包含有StringBuffer类型对象或和 StringBuffer相似类的对象,我们有两种选择:要么只能实现影子clone,要么就在类的clone()方法中加一句(假设是 SringBuffer对象,而且变量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原来的是:o.unCA = (UnCloneA)unCA.clone();

四、CloneStringStringBuffer的区别

下面的例子中包括两个类,CloneC类包含一个String类型变量和一个StringBuffer类型变量,并且实现了clone()方法。在 StrClone类中声明了CloneC类型变量c1,然后调用c1clone()方法生成c1的拷贝c2,在对c2中的String StringBuffer类型变量用相应的方法改动之后打印结果:

package clone;

class CloneC implements Cloneable {
    public String str;
    public StringBuffer strBuff;

    public Object clone() {
        CloneC o = null;
        try {
            o = (CloneC) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return o;
    }

}

public class StrClone {
    public static void main(String[] a) {
        CloneC c1 = new CloneC();
        c1.str = new String("initializeStr");
        c1.strBuff = new StringBuffer("initializeStrBuff");
        System.out.println("before clone,c1.str = " + c1.str);
        System.out.println("before clone,c1.strBuff = " + c1.strBuff);

        CloneC c2 = (CloneC) c1.clone();
        c2.str = c2.str.substring(0, 5);
        c2.strBuff = c2.strBuff.append(" change strBuff clone");
        System.out.println("=================================");
        System.out.println("after clone,c1.str = " + c1.str);
        System.out.println("after clone,c1.strBuff = " + c1.strBuff);
        System.out.println("=================================");
        System.out.println("after clone,c2.str = " + c2.str);
        System.out.println("after clone,c2.strBuff = " + c2.strBuff);
    }
}


/* RUN RESULT
before clone,c1.str = initializeStr
before clone,c1.strBuff = initializeStrBuff
=================================
after clone,c1.str = initializeStr
after clone,c1.strBuff = initializeStrBuff change strBuff clone
=================================
after clone,c2.str = initi
after clone,c2.strBuff = initializeStrBuff change strBuff clone
*
*/ 

打印的结果可以看出,String类型的变量好象已经实现了深度clone,因为对c2.str的改动并没有影响到c1.str!难道Java Sring类看成了基本数据类型?其实不然,这里有一个小小的把戏,秘密就在于c2.str = c2.str.substring(0,5)这一语句!实质上,在clone的时候c1.strc2.str仍然是引用,而且都指向了同一个 String对象。但在执行c2.str = c2.str.substring(0,5)的时候,它作用相当于生成了一个新的String类型,然后又赋回给c2.str。这是因为String Sun公司的工程师写成了一个不可更改的类(immutable class),在所有String类中的函数都不能更改自身的值。


参考:http://www.blogjava.net/jerry-zhaoj/archive/2009/10/14/298141.html

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值