首先,我们来明确,为什么要用克隆?
克隆的对象可能包含一些已经修改过的属性,为new出来的对象的属性都还是初始化时候的值!
所以当需要一个新的对象来保存当前对象的状态的时候,就靠克隆方法了!
克隆又分为深克隆和浅克隆
浅克隆:当对象被复制的时候,只复制他本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有实现真正意义上的复制,只是引用值传递!
深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将复制,强调的就是引用类型的,实现了真正的复制!
具体的操作去实现克隆:
1.第一种:我们利用Cloneable接口和重写的object超类中的clone方法
(1)首先,关于cloneable接口的说明:
标志性接口,其接口内部是空的,仅作为一个标志使用。
而且这个标志也仅仅是针对object类中clone()方法的,如果要有克隆功能的类,没有实现这个接口,就使用了object类的clone方法,那么此时,clone方法会CloneableNotSupportedException异常
(2)关于clone方法
首先明确其在超类Object中的实现:
声明是这样的:protected native Object clone() throws ........
函数逻辑是:可以理解为浅克隆!
其次在来说明clone()方法
①Object中的clone方法是一个native方法,native方法的效率一般来说都是远高于java中的非native方法,这也就理解了为什么我们实现对象的拷贝要利用clone方法,而不是先new一个类,然后把原始对象的信息复制到新对象中去
②clone方法返回的是Object类,具体使用时注意类型强转
③protected修饰符的理解:其权限表明只能在本包中或者在其他包中的子类中访问,一定要理解,是在子类中访问!
相信,稍微学习过java的都知道,protected访问权限外部是不可见的!
这也就解释了,为什么我们不能直接使用clone方法,而必须在类中重写方法,间接调用clone方法,以使用!
(3)而其在实现类中的具体实现Clone()方法则确定了其克隆逻辑
就是说,重写clone()的逻辑,决定了其是浅克隆(直接使用超类的clone)还是深克隆(手动克隆引用类型)
PS:强调一点,实现克隆,自己在实现类中,实现克隆逻辑的方法不是必须对clone()方法重写,只是借用了clone。
这一点,很容易理解,因为这种方法具体实现克隆逻辑的代码完全是程序员自身实现的!只不过为了效率问题,借用到了超类的clone方法,而要借用这个方法,就要实现相应的标志性接口(上述也说了,仅作为标志使用,内部为空,所以并没有需要实现接口内的方法,接口中根本就没有方法)。所以,可以看出,没有任何环节要求实现方法必须为clone。所以,本质上是自己的代码逻辑借用到了clone()方法,而不是重写clone()方法。所以无论是,传入的形式参数,方法名,返回类型都可以跟Object中的Clone方法没有一点关系!
当然了,为了保证代码的常规规范,或者说可读性,方法命名clone()显得更加合理!
附上简单的浅克隆代码:
public class CloneTest {
public static void main(String args[]) throws CloneNotSupportedException
{
CTest cTest = new CTest();
CTest cTest2 = cTest.myClone();
System.out.println("比较克隆前后的相关对比:");
System.out.println
("cTest == cTest2 :" + (cTest == cTest2)); //false
System.out.println
("cTest.num == cTest2.num :" + (cTest.num == cTest2.num)); //true
System.out.println
("cTest.string == cTest2.string :" + (cTest.string == cTest2.string)); //true
System.out.println
("cTest.string.equals(cTest2.string) :" + (cTest.string.equals(cTest2.string))); //true
}
}
//借用超类Object的clone方法,必须实现Cloneable接口
class CTest implements Cloneable
{
public int num = 0;
public String string = new String("liu");
public CTest myClone() throws CloneNotSupportedException
{
return (CTest)super.clone();
}
}
测试结果显示:
如果要实现深克隆,也就是对象引用型成员变量也拷贝一份,只需要在实现类的实现方法的逻辑中实现即可。
只需要更改为:
2.第二种实现克隆的方式:
实现serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正意义上的深度克隆!
import java.util.*;
import java.io.*;
public class SerializableTest {
public static void main(String args[])
{
//将实例对象序列化
STest sTest = new STest();
SerializableTest st = new SerializableTest();
st.serializable(sTest);
STest copySTest = st.getSerializable();
System.out.println
("序列化前的信息:" + sTest.getA() + "," + sTest.getString());
System.out.println
("反序列化后的信息:" + copySTest.getA() + "," + copySTest.getString());
System.out.println("关于序列化和非序列化的克隆性比较如下:");
System.out.println(sTest == copySTest); //false,说明反序列化实际产生了新的对象实例
System.out.println(sTest.getA() == copySTest.getA()); //true
System.out.println(sTest.getString() == copySTest.getString()); //false,说明实现了深克隆
System.out.println(sTest.getString().equals(copySTest.getString())); //true
}
public void serializable(STest sTest)
{
FileOutputStream fos = null; //文件输出流,用于上层包装
ObjectOutputStream oos = null; //对象输出流,用与对象序列化的字节流传输
try {
fos = new FileOutputStream("ss.out");
oos = new ObjectOutputStream(fos);
oos.writeObject(sTest); //封装的oos把对象序列化的字节流输入进文件
oos.flush();
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
oos.close();
fos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
public STest getSerializable()
{
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("ss.out");
ois = new ObjectInputStream(fis);
STest sTest = (STest) ois.readObject(); //ois把字节流反序列化为对象传入内存,向下转型为STest
return sTest;
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
ois.close();
fis.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return null;
}
}
//封装类,实现序列化
class STest implements Serializable
{
private static int a = 0;
private String string = "liu";
public void setA(int a){ this.a = a; }
public int getA(){ return a; }
public void setString(String string){ this.string = string; }
public String getString(){ return this.string; }
}
测试结果: