java克隆之深克隆和浅克隆

31.1概念

由java API文档可知:

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

protected Object clone() throws CloneNotSupportedException

创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,表达式:x.clone() != x为true,表达式: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 方法将会导致在运行时抛出异常。

 

31.2 浅克隆和深克隆

31.2.1 概念

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

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

31.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)结果

 


参考资料:

(1)、http://blog.csdn.net/u012129558/article/details/51426162

(2)、http://blog.csdn.net/jackfrued/article/details/44921941

乐山有志之士请加入群交流学习

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值