欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android冷知识——Java中的克隆

程序员文章站 2022-05-14 08:27:25
对象的创建 在android中,对象的创建分为两种形式,一种是使用new操作符创建对象,另一种是调用clone方法复制对象。这两个操作在使用上有以下的区别: 使用new操作符创建对象:对new的对象...

对象的创建

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类型的拷贝具有特殊性。