clone的理解

参考地址:https://blog.csdn.net/u011679955/article/details/52736379

在Javd中所有的传参都是值传递,并没有引用传递。有些方法参数是一个对象,也是值传递,传递的是对象的引用地址,这个地址指向堆中的一个对象,并没有传递一个对象。但是,拿到这个地址,传入对象的属性就可以被改变。不想对象的内容被方法改变,就是创建一个和原来对象一样的临时对象,进行参数传递。可以使用clone方法进行对象的复制。

clone方法主要分成两步:

1.对象所属的类实现Cloneable接口,这个接口就像序列化接口一样,标识这是一个可以克隆的对象

2.方法中使用它, 如person.clone()

clone分为浅克隆和深克隆:因为克隆的对象和原来的对象属性一样,原对象中属性是引用地址,克隆的也是引用地址,并不是克隆引用地址指向的对象。当这个引用地址指向的对象内容改变,所有的引用这个对象的内容也就改变了,这就是浅克隆。比如 a b c三个对象,b是a的一个对象属性,c是a的克隆对象,当a中b对象发生了改变,c中的b对象也发生改变。与浅克隆相对应的就是深克隆,深克隆是将对象属性也进行克隆,不再是直接复制原对象的引用地址。主要是在属性对象所在的类中也实现Cloneable接口,再在要克隆类的clone方法中指明属性对象也进行克隆,将引用地址改变。比如a b c三个对象,b是a的一个对象属性,c是a的克隆对象。b也实现cloneable接口,覆写clone方法,a在clone中指定 a=super.clone();a.b=B.clone()
  

public class Person implements  Cloneable {
	private  int age ;;
	private String name;
	private String sex;
	
	public Person() {
		
	}
	
	public Person(int age, String name, String sex) {
		this.age = age;
		this.name = name;
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	@Override
	public String toString() {
		return "Person [age=" + age + ", name=" + name + ", sex=" + sex + "]";
	}

	@Override
	public Object clone()  {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

}
public class Group implements Cloneable{
	private String groupName;
	private String groupId;
	private Person person;
	
	public Group() {
		
	}
	public Group(String groupName, String groupId, Person person) {
		super();
		this.groupName = groupName;
		this.groupId = groupId;
		this.person = person;
	}
	public String getGroupName() {
		return groupName;
	}
	public void setGroupName(String groupName) {
		this.groupName = groupName;
	}
	public String getGroupId() {
		return groupId;
	}
	public void setGroupId(String groupId) {
		this.groupId = groupId;
	}
	public Person getPerson() {
		return person;
	}
	public void setPerson(Person person) {
		this.person = person;
	}
	@Override
	public String toString() {
		return "Group [groupName=" + groupName + ", groupId=" + groupId + ", person=" + person + "]";
	}
	public Object clone() {
		Group group = null;
		try {
			group = (Group)super.clone();
//            深克隆,将属性person也进行克隆,Person要实现Cloneable
			group.person = (Person) person.clone();
			return group;
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return group;
		
	}

}
public class ThinkInJava {
	
	/**
	 * clone()方法,是在堆中复制一个对象,就像new一个对象一样,这个对象属性和原来的对象一样,只是栈中的引用地址变了
	 * 想使用clone(),必须实现Cloneable接口,Cloneable是个标志,就像序列化一样
	 * clone()分为浅克隆和深克隆,因为克隆的对象和原来的对象属性一样。如果原对象中属性是引用地址,克隆的也是引用地址,没有克隆引用地址指向的对象
	 * 当这个引用地址指向的对象内容改变,所有的引用这个对象的内容也就改变了,这就是浅克隆。
	 * 比如 a b c三个对象,a对象中有b的对象属性,c是a的克隆对象,当a中b对象发生了改变,c中的b对象也发生改变
	 * 与浅克隆相对应的就是深克隆,深克隆是在引用对象中也实现clone方法,再在对象中指明引用对象也克隆,将引用地址改变
	 * 如 a = super.clone()  a.b=b.clone()
	 *  
	 */
	@Test
	public void testBean()  {
		Person p1 = new Person(12, "p1", "man");
		Person p2 =(Person) p1.clone();
		System.out.println("Person的String引用地址是否相同"+p1.getName().equals(p2.getName()));
		p1.setName("p2");
		System.out.println("Person的String引用地址是否相同"+p1.getName().equals(p2.getName()));
		System.out.println(p1.toString());
		System.out.println(p2.toString());
	}
	
	@Test
	public void testGroup()  {
		Person p1 = new Person(12, "p1", "man");
		Group g1 = new Group("g1", "g1", p1);
		Group g2 = (Group)g1.clone();
		System.out.println("Group的Person引用地址是否相同"+g1.getPerson().equals(g2.getPerson()));
		p1.setName("p2");
		System.out.println("Group的Person引用地址是否相同"+g1.getPerson().equals(g2.getPerson()));
		System.out.println(g1.toString());
		System.out.println(g2.toString());
	}


}

testBean运行后结果是这个:

Person的String引用地址是否相同true
Person的String引用地址是否相同false
Person [age=12, name=p2, sex=man]
Person [age=12, name=p1, sex=man]

好像和我说的引用地址没变冲突,其实没有冲突。主要是string类型的特殊造成的,String类型是final的不可变的,当string 的发生变化时它的引用地址就会发生变化,指向一个新的引用地址。在上面的结果中,第一个true说明刚刚克隆完p1对象,p1和p2的string引用地址是相同的,当p1的name字段发生改变,它的引用地址发生了改变,原来的引用废弃了,结果就是p2,p1的引用还是原来的结果是p1,他们的引用地址不同了

属性字段是对象的克隆,属性对象没有实现克隆接口,testGroup运行后:

Group的Person引用地址是否相同true
Group的Person引用地址是否相同true
Group [groupName=g1, groupId=g1, person=Person [age=12, name=p2, sex=man]]
Group [groupName=g1, groupId=g1, person=Person [age=12, name=p2, sex=man]]

发现g1 g2中p1对象一样,他们的属性person指向同一个对象,p1变了,他们的person属性发生变化,这就是浅克隆

下面是深克隆,属性对象实现了克隆接口,Group中指出person属性也进行对象克隆,将引用地址改变,结果如下:

Group的Person引用地址是否相同false
Group的Person引用地址是否相同false
Group [groupName=g1, groupId=g1, person=Person [age=12, name=p2, sex=man]]
Group [groupName=g1, groupId=g1, person=Person [age=12, name=p1, sex=man]]

猜你喜欢

转载自blog.csdn.net/guo131003430325/article/details/81701854