关于java中对象拷贝(克隆)的相关总结

首先,我们来明确,为什么要用克隆?

克隆的对象可能包含一些已经修改过的属性,为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; }
}

测试结果:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值