一.引用拷贝与对象拷贝
class Person implements Cloneable{
private String name;
private int age;
。。。省略get和set方法
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
1.引用拷贝
Person person=new Person("jerry",23);
Person p=person;
System.out.println(person);
System.out.println(p);
Example.jerry.pratice.Person@4554617c
Example.jerry.pratice.Person@4554617c
person和p地址相同,二者引用同一个对象new Person("jerry",23)。并没有创建出一个新的对象。此种为引用拷贝。
引用拷贝:创建一个指向对象的引用变量的拷贝。
2.对象拷贝
【注】实现对象拷贝的类必须实现Cloneable接口,并覆写clone方法。
Person person=new Person("jerry",23);
Person p1=(Person) person.clone();
System.out.println(person);
System.out.println(p1);
Example.jerry.pratice.Person@4554617c
Example.jerry.pratice.Person@74a14482
peson和p1地址不同,即创建了新的对象,而非把仅仅把地址赋给变量。此种为对象拷贝。
二.深拷贝和浅拷贝
【注】深拷贝和浅拷贝都是对象拷贝
浅拷贝
Def:按位拷贝,会创建一个新对象。这个新对象有原始对象属性值的一份拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址。即如果其中一个对象改变了这个地址,就会影响到另一个对象。【浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象】
class Info implements Cloneable{
private Person person;
private String city;
private int id;
。。。。省略get和set方法
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
在原来的基础上加上Info类,引用原来的Person类
Person person=new Person("jerry",23);
Info info=new Info();
info.setCity("北京");
info.setId(11111);
info.setPerson(person);
Info info1=(Info)info.clone();
System.out.println("拷贝后,,,");
System.out.println(info);
System.out.println(info1);
System.out.println(info1.getId());
System.out.println(info1.getCity());
System.out.println(info1.getPerson().getName()+","+info1.getPerson().getAge());
System.out.println("修改被复制对象中的对象引用");
info1.getPerson().setName("Tom");
info1.getPerson().setAge(18);
System.out.println(info.getPerson().getName()+","+info.getPerson().getAge());
System.out.println(info1.getPerson().getName()+","+info1.getPerson().getAge());
拷贝后,,,
Example.jerry.pratice.Info@4554617c
Example.jerry.pratice.Info@74a14482
11111
北京
jerry,23
修改被复制对象中的对象引用
Tom,18
Tom,18
结果分析:info和info1指向两个不同的对象,而两个引用info和info1中的两个person引用指向同一个对象,所以person变化时,两个同时发生了变化。即浅拷贝
clone方法存在一个缺陷,它并不会将对象的所有属性全部拷贝过来,而是有选择的拷贝。基本规则如下:
1.基本类型:拷贝其值。如int float等
2.对象:如果变量是一个实例对象,则拷贝其地址引用。即新对象和原来对象共享该实例变量。
3.String字符串:若变量为String字符串则拷贝其地址引用。但在修改时,它会从字符池中重新生成一个新的字符串,原有对象保持不变。
深拷贝
Def:深拷贝会拷贝所有的属性,并且拷贝属性指向的动态分配的内存。即对象和它所引用的对象(对象中的对象)一起拷贝就是深拷贝。相比于浅拷贝,深拷贝速度较慢且花销较大。【即,深拷贝就是把对象引用的对象都复制一份】
public Object clone() throws CloneNotSupportedException {
// Object object = super.clone();
// return object;
Info info1=(Info)super.clone();
info1.setPerson((Person)info1.getPerson().clone());//将对象中的对象复制一份重新set
//info1.person=(Person)peson.clone; 两种方式均可
return info1;
}
拷贝后,,,
Example.jerry.pratice.Info@4554617c
Example.jerry.pratice.Info@74a14482
11111
北京
jerry,23
修改被复制对象中的对象引用
jerry,23
Tom,18
改写Info类中的clone方法,将Info中的Person也复制一份。可以看到修改拷贝对象中的Person对象时,原来的主对象中的person对应属性并没有改变。
【注】根据上述实例可以知道:如果要深拷贝一个对象,这个对象必须实现cloneable接口,实现clone方法,并在clone方法内部把对象引用的其他对象也要clone一份,所以被引用的对象也要实现cloneable接口和实现clone方法。
CJ:设想,如果当person中有多了一个引用对象为Hobby类,那么进行深拷贝的时候,如果还按照上述的方法,对于两个Info来说,两个独立的Info对象内的head引用已经指向了两个独立的Peson对象,但是对于这两个Person对象来说,它们指向的是同一个Hobby对象,即并没有完全的独立。要保证完全独立,也需将Person中的Hobby对象也拷贝一份。即,让Hobby类也实现Cloneable接口,实现clone方法,并在Person对象的clone方法中拷贝它所引用的Hobby对象。
class Person implements Cloneable{
private String name;
private int age;
private Hobby hobby;
protected Object clone() throws CloneNotSupportedException {
//return super.clone();
Person person=(Person)super.clone;
peson.hobby=(Hobby)this.hobby.clone;
}
}
class Hobby implements Cloneable{
private String ho1;
private String ho2;
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
依次类推,如果Hobby对象还引用的其他的对象比如,Special,如果在Hobby类中的clone方法不处理Special的复制,Info对象拷贝之后会一级一级的引用,引用到同一个Special对象。所以要实现完全独立,只能让Special也拷贝。
如果在拷贝一个对象时,要想让这个拷贝的对象和源对象完全彼此独立,那么在引用链上的每一级对象都要被显式的拷贝。所以创建彻底的深拷贝是非常麻烦的,尤其是在引用关系非常复杂的情况下, 或者在引用链的某一级上引用了一个第三方的对象, 而这个对象没有实现clone方法, 那么在它之后的所有引用的对象都是被共享的。