一、克隆对象从简单的变量克隆入手,就是以下代码
int apples = 5;
int pears = apples;
不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。
二、克隆对象从简单的方法入手,就是以下代码
package com.xuecheng.test.freemarker;
import org.junit.Test;
public class Test001 {
@Test
public void ss(){
Student s1 = new Student();
s1.setAge(12);
Student s2 = s1;
System.out.println(s1);
System.out.println(s2);
}
}
打印结果为:
Student{name='null', age=12}
Student{name='null', age=12}
但是真有这么简单吗?我们给s2对象中的一个属性改变后
s2.setAge(18);
打印结果为:
Student{name='null', age=18}
Student{name='null', age=18}
三、总结
为什么把s2的属性改变后,s1的属性也改变了?原因出在 s2 = s1上,这一部是将s1的引用赋值给s2。也就是说s1和s2指向堆中的同一对象。
四、为什么要进行对象克隆吗?为什么不能直接new 一个吗?
因为被克隆对象有些属性是被改过的,而new一个对象的话,他的属性都是初始值。而如果你通过new出来的对象,一个一个进行赋值的话。一、可能比较麻烦。二、因为通过Object提供的方法clone,这个clone方法是native修饰的所以执行效率会比较快。
而上面一个对象给另外一个对象赋值时,这种是引用,即引用的是一个地址值,还是指向同一个对象。Object提供的这个clone方法是独立的两个对象。
五、如何进行对象的克隆
5.1、使被克隆对象实现cloneable(该接口只是标识接口,没有任何方法)
5.2、并在被克隆对象中重写Object中的clone方法。并且把protected改成public
六、以上是浅克隆,下面谈谈浅克隆和深克隆的区别
6.1、浅克隆
浅克隆就是,如果对象的属性是值类型就克隆该值类型;如果是引用类型,就克隆其引用地址。所以说浅克隆的属性是引用类型的话,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
所以说浅克隆只是克隆了属性是值类型的对象,而对属性是引用类型的对象并没有克隆。
6.2、深克隆
深克隆就是不仅将原型对象的值类型克隆,还将原型对象的引用类型也进行了克隆。是真正的克隆,引用类型的克隆后,完全是独立的两个地址,而不是仅仅克隆引用。
但是如果克隆对象里,有引用类型,但是引用类型对象里,又有引用类型。这样嵌套了好几层的引用类型。使用clone方法就很麻烦,这时,我们就可以使用序列化来简单实现。因为对象序列化写在流中,就是对象的一个拷贝,在内存中原有的对象还在。在流中,不仅可以拷贝对象,还可以拷贝其中的引用对象。
因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。
需要注意的是能够实现序列化的对象其类必须实现Serializable接口,否则无法实现序列化操作。
-------------------------扩展----------------------------
Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。
七、解决多层克隆问题
如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。
public class Outer implements Serializable{
private static final long serialVersionUID = 369285298572941L; //最好是显式声明ID
public Inner inner;
//Discription:[深度复制方法,需要对象及对象所有的对象属性都实现序列化]
public Outer myclone() {
Outer outer = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
outer = (Outer) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return outer;
}
}
Inner也必须实现Serializable,否则无法序列化:
public class Inner implements Serializable{
private static final long serialVersionUID = 872390113109L; //最好是显式声明ID
public String name = "";
public Inner(String name) {
this.name = name;
}
@Override
public String toString() {
return "Inner的name值为:" + name;
}
}
这样也能使两个对象在内存空间内完全独立存在,互不影响对方的值。
总结
实现对象克隆有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。