java克隆(clone)的两种实现方法

1概念

java API文档可知:

clone 属于 java.long.Object的一个方法

protected Object clone() throws CloneNotSupportedException

创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,表达式:x.clone() != xtrue,表达式:x.clone().getClass() == x.getClass()

也为 true,但这些并非必须要满足的要求。一般情况下:x.clone().equals(x)true,但这并非必须要满足的要求。按照惯例,返回的对象应该通过调用 super.clone 获得。如果一个类及其所有的超类(Object 除外)都遵守此约定,则 x.clone().getClass() == x.getClass()

按照惯例,此方法返回的对象应该独立于该对象(正被复制的对象)。要获得此独立性,在 super.clone 返回对象之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被复制对象的内部“深层结构”的所有可变对象,并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用,那么通常不需要修改 super.clone 返回的对象中的字段。Object 类的 clone 方法执行特定的复制操作。首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。注意,所有的数组都被视为实现接口 Cloneable。否则,此方法会创建此对象的类的一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有被自我复制。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。

 Object 类本身不实现接口 Cloneable,所以在类为 Object 的对象上调用 clone 方法将会导致在运行时抛出异常。

 

2 浅克隆和深克隆

2.1 概念

如上所述,java在克隆的时候会复制并返回对象的一个副本(其中包含所有字段和基础数据类型字段的数值),如果对象中存在复合数据类型(比如数组变量、其他对象等),则只复制复合数据类型的引用地址,不会复制复合数据中的具体值,而复制对象中的基础数据类型(比如intlongfloatString等)则会把数值也会复制过去。因此这种克隆方式称为“浅克隆”。同理可得,“深克隆”就是将基础数据类型和所有复合数据类型都一并复制拷贝一份,相互之间互不影响。

浅克隆是存在弊端的,假如克隆的两个对象其中一个更改了自己对象中的复合数据中的数据,则另一个对象中的数据也会同步被修改,因为他们引用的是同一个复合数据对象。

2.2 实现方式

实现克隆的方式主要有两种:

(1)、对象实现Cloneable接口并重写Object类中的clone()方法(浅克隆方式);

 代码如下

(a)用户信息
public class Person implements Cloneable{
	private String name;
	private String sex;
	private Phone phone;
	/**
	 * 实现Cloneable接口,并重写clone方法
	 */
	@Override
	protected Object clone(){
		Person p=null;
		try {
/**
			 * 若要实现深克隆,此处就必须将对象中所有的复合数据类型统统单独复制拷贝一份,
			 * 但是实际开发中,无法确定对象中复合数据的种类和个数,
			 * 因此一般不采用此种方式实现深克隆
			 */
			p = (Person) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return p;
	}
	
	public Person(String name, String sex, Phone phone) {
		super();
		this.name = name;
		this.sex = sex;
		this.phone = phone;
	}
	
	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;
	}
	public Phone getPhone() {
		return phone;
	}
	public void setPhone(Phone phone) {
		this.phone = phone;
	}
	@Override
	public String toString() {
		return "[name=" + name + ", sex=" + sex + ", phone=" + phone
				+ "]";
	}
}
(b)手机信息
public class Phone{

	private String number;
	private String area;
	
	public Phone(String number, String area) {
		super();
		this.number = number;
		this.area = area;
	}
	
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public String getArea() {
		return area;
	}
	public void setArea(String area) {
		this.area = area;
	}
	@Override
	public String toString() {
		return "[number=" + number + ", area=" + area + "]";
	}
}
(c)测试代码
public static void main(String[] args) {
		Person p=new Person("雷小涛", "man", new Phone("123456", "四川成都"));
		Person p2=(Person) p.clone();
		System.out.println("p:"+p.toString());
		System.out.println("p2:"+p2.toString());
		p2.setName("leixiaotao");
		p2.getPhone().setArea("四川乐山");
		System.out.println("-------其中一个对象修改值过后-------");
		System.out.println("p:"+p.toString());
		System.out.println("p2:"+p2.toString());
	}

d)结果:修改基础数据结构数值不会影响其他对象,修改复合数据数值,全部都会受影响

 

 (2)、对象实现Serializable接口,通过对象的序列化和反序列化实现克隆(此方法可以实现真正的深度克隆)。

代码如下:

(a)用户信息类
public class Person implements Serializable{

	private static final long serialVersionUID = -3936148364278781089L;
	
	private String name;
	private String sex;
	private Phone phone;
	
	public Person(String name, String sex, Phone phone) {
		super();
		this.name = name;
		this.sex = sex;
		this.phone = phone;
	}
	
	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;
	}
	public Phone getPhone() {
		return phone;
	}
	public void setPhone(Phone phone) {
		this.phone = phone;
	}
	@Override
	public String toString() {
		return "[name=" + name + ", sex=" + sex + ", phone=" + phone
				+ "]";
	}
}
(b)手机信息类
public class Phone implements Serializable{

	private static final long serialVersionUID = -4482468804013490322L;
	
	private String number;
	private String area;
	
	public Phone(String number, String area) {
		super();
		this.number = number;
		this.area = area;
	}
	
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public String getArea() {
		return area;
	}
	public void setArea(String area) {
		this.area = area;
	}
	@Override
	public String toString() {
		return "[number=" + number + ", area=" + area + "]";
	}
}
(c)CloneUtil工具类
public class CloneUtil {

	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T clone(T object) throws Exception{
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(object);

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        // 此处不需要释放资源,说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
        // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放
        return (T) ois.readObject();
	}
}
(d)测试代码
public static void main(String[] args) throws Exception {
		Person p=new Person("雷小涛", "man", new Phone("123456", "四川成都"));
//		Person p2=(Person) p.clone();
		Person p2=CloneUtil.clone(p);
		System.out.println("p:"+p.toString());
		System.out.println("p2:"+p2.toString());
		p2.setName("leixiaotao");
		p2.getPhone().setArea("四川乐山");
		System.out.println("-------其中一个对象修改值过后-------");
		System.out.println("p:"+p.toString());
		System.out.println("p2:"+p2.toString());
	}

(e)结果

 


 【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】


  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java语言中,我们可以使用两种方法实现对象的克隆。 1. 通过实现Cloneable接口并重写clone()方法: 首先,在需要克隆的对象类中实现Cloneable接口,该接口是一个标记接口,表示该类支持克隆操作。然后,重写clone()方法,该方法是Object类中的一个非final方法,用于创建并返回一个新的对象。在这个方法内部,我们可以调用super.clone()方法来获得一个浅表克隆,也可以根据需要对属性进行深度克隆。最后,我们可以通过调用该对象的clone()方法来创建该对象的一个克隆副本。 2. 通过实现Serializable接口并使用序列化和反序列化: 首先,在需要克隆的对象类中实现Serializable接口,该接口是一个标记接口,表示该类支持序列化。然后,我们可以通过将该对象序列化为字节流,并再将字节流反序列化为一个新的对象来实现克隆。这种方法实现了对象的深拷贝,即复制了原对象及其内部的所有引用对象。 这两种方法在使用上有一些不同。实现Cloneable接口并重写clone()方法的方式更为直接,但需要开发者自行处理属性深度克隆问题。而使用序列化和反序列化方式则相对更容易实现对象的深拷贝,但在性能上略逊于直接克隆。 需要注意的是,使用clone()方法进行对象克隆时,会调用对象的构造函数来创建新的对象,而使用序列化和反序列化方式则不会。所以在使用这两种方法时,需要确保对象类的构造函数是正确的和可访问的。此外,还需要确保被克隆的对象类中的所有引用类型的成员变量都是可序列化的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值