当前位置: 移动技术网 > IT编程>移动开发>Android > Android冷知识——Java中的克隆

Android冷知识——Java中的克隆

2018年10月30日  | 移动技术网IT编程  | 我要评论

无限生机演员表,无人驾驶演员表,铭天传奇

对象的创建

android中,对象的创建分为两种形式,一种是使用new操作符创建对象,另一种是调用clone方法复制对象。这两个操作在使用上有以下的区别:

使用new操作符创建对象:对new的对象分配内存,调用其构造方法,并将创建好的对象引用发布到外部 调用clone方法复制对象:对clone的对象分配内存,对新分配的内存域使用原对象进行填充,并将clone的对象引用发布到外部

克隆的使用

在对象中可以使用clone(),必须实现cloneable接口,复写clone方法,外部才可以调用clone()

public class person implements cloneable{

 public string name;
 public int age;

 @override
 protected object clone() throws clonenotsupportedexception {
  return super.clone();
 }
}

复制对象和复制引用

一、复制对象

person p1 = new person();
person p2 = p1;

log.e("tag", p1.tostring());
log.e("tag", p2.tostring());

通过输出的结果看出,两个对象的地址是相同的,所以这两个对象是同一个对象,这种现象叫做对象的复制

com.hensen.fashionsource.person@a6ea782
com.hensen.fashionsource.person@a6ea782

二、复制引用

try {
 person p1 = new person();
 person p2 = (person) p1.clone();

 log.e("tag", p1.tostring());
 log.e("tag", p2.tostring());
} catch (clonenotsupportedexception e) {
 e.printstacktrace();
}

通过输出的结果看出,两个对象的地址不相同,所以这两个对象是不同对象,这种现象叫做引用的复制

com.hensen.fashionsource.person@38b31e93
com.hensen.fashionsource.person@3046fed0

浅拷贝和深拷贝

使用clone()的过程中,clone会遇到浅拷贝和深拷贝的问题,下面是浅拷贝和深拷贝的概念介绍:

浅拷贝:将对象中的所有字段复制到新的对象中。其中,基本数据类型的值被复制到对象中后,在对象中的修改不会影响到源对象对应的值。而引用类型的值被复制到对象中还是引用类型的引用,而不是引用的对象,在对象中对引用类型的字段值做修改会影响到源对象本身。简单的说,只拷贝基本数据类型,引用类型不会被拷贝。 深拷贝:将对象中的所有字段复制到新的对象中。不过,无论是对象的基本数据类型,还是引用类型,都会被重新创建并赋值,对于新对象的修改,不会影响到源对象本身。简单的说,拷贝出完全相同的对象,对新对象的修改和源对象没有任何关系。

clone()属于浅拷贝,但是也可以将clone()方法转换成深拷贝的处理,下面开始介绍

一、string类型的特殊性

由于clone()属于浅拷贝,那么在上面的例子中,按理说,person对象的name字段,string类型不是基本数据类型,且string没有实现cloneable接口,在这里调用clone()仅仅是复制了string引用,那么克隆之后的对象修改name字段的值会不会将原来的值也改变了?下面我们通过例子验证

try {
 person p1 = new person();
 person p2 = (person) p1.clone();

 p1.name = "aaa";
 p2.name = "bbb";

 log.e("tag", p1.name.tostring()); //aaa
 log.e("tag", p2.name.tostring()); //bbb

} catch (clonenotsupportedexception e) {
 e.printstacktrace();
}

这个例子中我们期待的输出应该都是bbb,实际上输出的结果是aaa和bbb。这是因为string类型被final修饰符修饰,在内存中是不可以被改变的对象,每次对新的字符串赋值都会分配一块新内存,并指向它。所以string类型在clone中属于特殊情况。

二、浅拷贝

回归真题,clone()属于浅拷贝,那么怎么去验证它呢?下面我们对person增加hand(手)引用类型,然后再hand(手)中又增加finger(手指)引用类型,代码如下

public class person implements cloneable{

 public string name;
 public int age;
 public hand hand;

 public person(hand hand) {
  this.hand = hand;
 }

 @override
 protected object clone() throws clonenotsupportedexception {
  return super.clone();
 }
}
public class hand {
 public finger finger;

 public hand(finger finger) {
  this.finger = finger;
 }
}
public class finger {

}

我们通过比较person里和hand里和finger里的引用类型的地址是否相同,可以看出clone()的本质是浅拷贝

try {
 person p1 = new person(new hand(new finger()));
 person p2 = (person) p1.clone();

 log.e("tag", "" + p1.hand);
 log.e("tag", "" + p2.hand);

 log.e("tag", "" + p1.hand.finger);
 log.e("tag", "" + p2.hand.finger);

} catch (clonenotsupportedexception e) {
 e.printstacktrace();
}

输出结果可以看出,clone()引用类型的地址是相同的

com.hensen.fashionsource.hand@38b31e93
com.hensen.fashionsource.hand@38b31e93
com.hensen.fashionsource.finger@3046fed0
com.hensen.fashionsource.finger@3046fed0

三、深拷贝

如果我们想对person对象进行深拷贝该怎么做呢?我们可以让person的引用hand具有拷贝的功能,那么很自然的可以在person拷贝的时候拷贝一份hand,所以hand我们对hand实现cloneable接口,让其具备克隆功能。

public class person implements cloneable{

 public string name;
 public int age;
 public hand hand;

 public person(hand hand) {
  this.hand = hand;
 }

 @override
 protected object clone() throws clonenotsupportedexception {
  person newper = (person) super.clone();
  newper.hand = (hand) hand.clone();
  return newper;
 }
}

public class hand implements cloneable{
 public finger finger;

 public hand(finger finger) {
  this.finger = finger;
 }

 @override
 protected object clone() throws clonenotsupportedexception {
  return super.clone();
 }
}

下面我们继续输出拷贝前p1的地址和拷贝后p2的地址,我们可以发现hand已经完成了深拷贝了

com.hensen.fashionsource.hand@38b31e93
com.hensen.fashionsource.hand@3046fed0
com.hensen.fashionsource.finger@14be0ec9
com.hensen.fashionsource.finger@14be0ec9

接着,就剩下finger类了,同样我们重复上面的步骤即可,对finder类和hand类改造

public class hand implements cloneable{
 public finger finger;

 public hand(finger finger) {
  this.finger = finger;
 }

 @override
 protected object clone() throws clonenotsupportedexception {
  hand newhand = (hand) super.clone();
  newhand.finger = (finger) finger.clone();
  return newhand;
 }
}

public class finger implements cloneable{

 @override
 protected object clone() throws clonenotsupportedexception {
  return super.clone();
 }
}

下面我们继续输出拷贝前p1的地址和拷贝后p2的地址,我们可以发现finger已经完成了深拷贝了

com.hensen.fashionsource.hand@38b31e93
com.hensen.fashionsource.hand@3046fed0
com.hensen.fashionsource.finger@14be0ec9
com.hensen.fashionsource.finger@35e3b9ce

总结

至此,person对象已经完成了完整的深拷贝,这里简单的总结上面的所有内容

clone的使用必须实现cloneable的接口 clone属于浅拷贝,当可以通过复写clone()实现深拷贝 clone对string类型的拷贝具有特殊性。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网