目录
对象的克隆
java当中的克隆跟生物上所说的克隆类似,就是复制出一个一模一样的个体,当然迁移到java当中,那就是复制出一个一模一样对象,别忘了java当中有句话叫做一切皆对象。通过克隆就能提高对象的独立性,使用起来更加方便,也更安全。
克隆的作用
对象克隆主要是为了解决引用类型在进行等号赋值时使得两个引用同时指向同一个对象实例,从而导致通过两个引用去操作对象时,会直接更改实例中的属性破坏对象的相互独立性。
//例如一下代码段
public class Test {
public static void main(String[] args) {
// TODO
Student s1 = new Student("Tom", 12);
Student s2 = s1;//s2的引用指向s1
System.out.println(s1);
System.out.println(s2);
s2.setName("Jerry");//修改s2的值时s1的属性
System.out.println(s1);
System.out.println(s2);
}
上述代码运行的结果如图:
由上述运行结果可知,在引用类型当中,由于都是指向同一个对象实例,当我们用引用类型去修改对象实例的值时,原来对象的属性也会跟着改变,从而导致了数据的不一致性。对象的克隆就能解决上述问题,防止发生此类情况。
克隆的分类
浅克隆
浅克隆如下图所示,只适用于基本的数据类型,对于值传递就只是赋值,把所有属性都拿过来赋值给当前对象。当要克隆的对象里面存在另外一个非基本数据类型的对象实例时,由于只是简单地通过等号赋值,又相当于指向的是同一个实例对象,在进行修改时同样会修改原来的属性。
深度克隆
而深度克隆就是把对象的所有属性都统统复制一份新的到目标对象里面去,使他成为一个独立的对象,当修改新的对象实例的属性时,原来对象中的属性任然不变。
克隆的使用
浅克隆的使用
浅克隆的实现代码,例如:
package com.vince;
//实现Cloneable接口
public class Product implements Cloneable{
private String name;
private Integer price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Product(String name, Integer price) {
super();
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Product [name=" + name + ", price=" + price + "]";
}
//重写clone的方法
@Override
public Product clone() {
Product product = null;
try {
product = (Product)super.clone();
} catch (CloneNotSupportedException e) {
product = null;
e.printStackTrace();
}
return product;
}
}
package com.vince;
public class Test {
public static void main(String[] args) {
// TODO 对象的克隆
Product product1 = new Product("篮球", 189);
// 1.在Product实现CoCloneable接口
// 2.重写clone方法
Product product2 = product1.clone();
System.out.println(product2);
product2.setPrice(200);//篮球涨价了
System.out.println(product1);//此时修改product2不会影响product1的值
System.out.println(product2);
}
}
深度克隆的使用
深度克隆的实现代码,例如:
package com.vince;
//实现Cloneable接口
public class Product implements Cloneable{
private String name;
private Integer price;
public Shop shop;
public Product(String name, Integer price, Shop shop) {
this.name = name;
this.price = price;
this.shop = shop;
}
public Shop getShop() {
return shop;
}
public void setShop(Shop shop) {
this.shop = shop;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public Product(String name, Integer price) {
super();
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "商店名:" + "\t" +name + "价格:" +"\t"+ price + "商店:" + shop;
}
//重写clone的方法
@Override
public Product clone() {
Product product = null;
try {
// clone 的过程,new一个新的对象,将原对象中的属性完全复制(赋值)
product = (Product)super.clone();
// Product中的Shop实例克隆得到的对象
shop = (Shop)product.getShop().clone();
// 将新得到的克隆对象覆盖掉原来shop的属性值
product.setShop(shop);
} catch (CloneNotSupportedException e) {
product = null;
e.printStackTrace();
}
return product;
}
}
package com.vince;
public class Shop implements Cloneable{
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Shop(String name, String address) {
super();
this.name = name;
this.address = address;
}
@Override
public String toString() {
return "Shop [name=" + name + ", address=" + address + "]";
}
@Override
protected Shop clone() {
Shop shop = null;
try {
shop = (Shop)super.clone();
} catch (CloneNotSupportedException e) {
shop = null;
e.printStackTrace();
}
return shop;
}
}
深度克隆的实现方式
深度克隆的第一种方式:如果被克隆对象中出现引用类型的变量(用户自定义变量),需要手动再次进行克隆。
缺点:如果类与类之间的关联关系较为复杂时,逐层实现克隆较为繁琐
深度克隆的第二种方式:如果被克隆对象中出现引用类型的变量(用户自定义变量),用序列化的方式实现克隆。序列化将在下一篇介绍。
总结
对象的克隆主要是为了解决在引用类型赋值时,通过引用类型赋值时修改属性值时所造成的修改后的数据不一致的问题;克隆分为浅克隆和深克隆,浅克隆只是适用于基本的数据类型,而对于非基本数据类型的任然不实用,同样修改时会存在破坏对象之间的独立性(如上述深克隆的使用中的例子,当Product中存在另外一个自定义类型Shop时,通过克隆后修改shop的属性任然会修改原来shop的属性值);深度克隆是把每一个对象都new一份到新的目标对象中,在要克隆的对象里面嵌套的非基本的数据类型和自定义对象都要进行克隆或者实现克隆的接口,当层次变多时实现起来就比较繁琐。还有最重要的一点是,所有的对象要想实现克隆,都必须要实现Cloneable方法和重写clone()方法。
附语
由于本人知识有限,若发现错误,希望大家能够批评和指正,谢谢