java提供了一个protected修饰的clone()方法,该方法用于帮助其他对象来实现”自我克隆”,所谓”自我克隆”就是得到一个当前对象的副本,而且二者之间完全隔离.由于Object类提供的clone()方法使用了protected修饰,因此该方法只能被子类重写或调用.
自定义类实现”克隆”的步骤如下:
自定义类实现Cloneable接口.这是一个标记性接口,实现该接口的对象可以实现”自我克隆”,接口里没有定义任何方法.
自定义类实现自己的clone()方法.
实现clone()方法时通过super.clone()调用Object实现的clone()方法来得到该对象的副本,并返回该副本.如下程序示范了如何实现”自我克隆”:
public class Address {
// 地址
String dizhi;
/**
* 带一个参数构造器
*
* @param dizhi 地址
*/
public Address(String dizhi) {
super();
this.dizhi = dizhi;
}
}
public class News implements Cloneable {
// 年龄
int age;
// 地址
Address address;
/**
* 对地址进行赋值
* 带一个参数构造器,调用父类构造器
* @param age 年龄
*/
public News(int age) {
super();
this.age = age;
this.address = new Address("河南省");
}
public News clone()throws CloneNotSupportedException {
return (News)super.clone();
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建News类的对象
News n=new News(24);
//调用克隆方法
News w=n.clone();
//判断两个对象是否相等 返回false,因为克隆出来的是完全不同的两个对象
System.out.println(n==w);
//判断两个对象的变量是否相等,返回true,因为克隆出来的对象的变量指向的是同一个;
System.out.println(n.address==w.address);
}
}
输出:
false
true
上面程序让News类实现了Cloneable接口,而且实现了clone()方法,因此News对象就可实现”自我克隆”,克隆出来的对象只是原有对象的副本.程序在①处代码判断原有的News对象与克隆出来的News对象是否相同,程序返回false.
Object类提供的Clone机制只对对象里各实例变量进行”简单复制”,如果实例变量的类型是引用类型,Object的Clone机制也只是简单的复制这个引用变量,这样原有对象的引用类型的实例变量与克隆对象的引用类型的实例变量依然指向内存中的同一个实例,所以上面程序中②处代码输出true.上面程序”克隆”出来的n,w所指向的对象在内存中的存储示意图如下图所示:
Object类提供的clone()方法不仅能简单地处理”复制”对象的问题,而且这种”自我克隆”机制十分高效.比如clone一个包含100个元素的int[]数组,用系统默认的clone方法比静态copy方法快近2倍.
需要指出的是,Object类的clone()方法虽然简单,易用,但它只是一种”浅克隆”,它只克隆该对象的所有成员变量值,不会对引用类型的成员变量所引用的对象进行克隆.如果开发者需要对对象进行”深克隆”,则需要开发者自己进行”递归”克隆,保证所有引用类型的成员变量值所引用的对象都被复制了.
下面程序示范如何实现深克隆:
public class Address implements Cloneable {
String detail;
/**
* 一个参数构造器
* @param detail
*/
public Address(String detail) {
//调用父类构造器
super();
this.detail = detail;
}
/**
* 重写克隆方法,返回值为当前对象
*/
public Address clone() throws CloneNotSupportedException {
//调用父类克隆方法
return (Address) super.clone();
}
}
public class News implements Cloneable {
// 年龄
int age;
// 地址
Address address;
/**
* 对地址进行赋值
* 带一个参数构造器,调用父类构造器
* @param age 年龄
*/
public News(int age) {
super();
this.age = age;
this.address = new Address("河南省");
}
/**
* 重写克隆方法,返回值为当前对象
*/
public News clone()throws CloneNotSupportedException {
//调用父类克隆方法
News n=(News)super.clone();
//把地址克隆结果给当前变量
n.address=n.address.clone();
//返回当前变量指向的对象
return n;
}
}
public class CloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
//创建News类的对象
News n=new News(24);
//调用克隆方法
News w=n.clone();
//判断两个对象是否相等 返回false,因为克隆出来的是完全不同的两个对象
System.out.println(n==w);
//判断两个对象的变量是否相等,返回false,因为克隆出来的对象的变量没有指向同一个;
System.out.println(n.address==w.address);
}
}
输出结果:
false
false
打印会输出两个false,说明深克隆成功!