`

【解惑】小心浅克隆

阅读更多

★ Java对象赋值

 

Employee e1=new Employee("李"); //Employee是一个自定义类    
Employee e2=e1; //赋值对象    
e2.setName("王");//改变对象e2的名字    
System.out.println(e1.getName()); //打印e1的结果: 王    

Employee e1=new Employee("李"); //Employee是一个自定义类 
Employee e2=e1; //赋值对象 
e2.setName("王");//改变对象e2的名字 
System.out.println(e1.getName()); //打印e1的结果: 王 

 

      这就是Java的对象赋值,改变e2的内容竟然会影响e1的内容。原因很简单,就是e1和e2这两个对象引用都指向了堆中同一个Employee类对象的内容。也就是说: Java的对象赋值的是引用(相当于C的指针)。如何让e1,e2成为内容相同的两个完全不同的对象呢,这就需要用到Java的对象克隆机制(将e2复制成e1的一个独立副本)。

 

Java对clone的支持

 

(1) 继承Object的clone方法的疑问?

     有一点我们很清楚,Java的万类之祖Object中有一个clone()方法:  

               protected native Object clone() throws CloneNotSupportedException

     既然这个方法是protected的,显然是为了让子类能够使用。看看下面的代码:

//Employee类中没有clone方法,也没有实现Cloneable接口   
Employee original=new Employee("John Public");   
Employee copy=original.clone(); //wrong  

//Employee类中没有clone方法,也没有实现Cloneable接口
Employee original=new Employee("John Public");
Employee copy=original.clone(); //wrong

     有人会提出这样的疑问:不是所有的类都是Object的子类吗?不是所有的子类都可以访问受保护的方法吗 ? 毫无疑问,这两句提问没有任何错误。但是有一点必须搞清楚:你是否正真理解protected的作用范围呢?《【Java语言】你是否真正理解了protected的作用范围呢?》 。

(2) Java支持克隆

      既然如此,难道我们就没有办法在某一个类的作用域外部来克隆这个类了吗?

      答案是否定的! 我们可以在任何一个类中重写clone方法,并升级它的访问作用域。事实上,使用的时候也就是这样做的!

      首先我们必须在需要克隆的类上实现一个重要的接口——Cloneable接口。这种接口我们叫作标记接口(tagging interface) 。这种标记接口没有任何方法,唯一的作用就是作为一个标志,用来告诉JVM一个类是否具有某个特定的功能。

       如此一来,我们只要定义一个具有 Clone 功能的类就可以了:

       1. 在类的声明中加入“ implements Cloneable ”,标志该类有克隆功能;
       2. 重载类 Object 的 clone() 方法,在该方法中调用 super.clone() :

class Employee implements Cloneable{    
            public Object clone() throws CloneNotSupportedException{//重载clone()方法    
              Employee cloned=(Employee)super.clone();    
                     return cloned;    
            }    
}  

class Employee implements Cloneable{ 
            public Object clone() throws CloneNotSupportedException{//重载clone()方法 
              Employee cloned=(Employee)super.clone(); 
                     return cloned; 
            } 
}

 

深Clone和浅Clone


       拷贝副本的问题并没有完全解决。clone技术并不是那么简单的。Object中的clone()方法是对具体类对象的各个域进行对应的赋值。如果具体类对象中还有子对象,这个问题就复杂了。

// 具备浅克隆的Employee类   
class Employee implements Cloneable{   
       public String name="";     
       public Date hireDay=null;   
  
       public Object clone(){   
            Employee cloned=(Employee)super.clone();   
            return cloned;   
      }   
}   
  
Employee orignal=new Employee();   
Employee copy=orignal.copy();  

       对于上面的代码,克隆以后orignal与copy中的hireDay指向的是同样的存储位置。也就是说当我们调用copy.hireDay.setTime()方法后,orignal中的hireDay也会发生改变。但String类(由于常量池的存储方式)和基本数据类型变量时不会改变的。这种对子对象克隆无效的方式我们叫做浅克隆

       很多情况下,我们需要将对象中的所有域(包括子对象)都进行真正的克隆。要做到这种深克隆,我们必须在重载clone()方法时克隆子对象:

//具备深度克隆功能的Employee类   
class Employee implement Cloneable{   
         public String name="";   
         private Date hireDay=null;   
  
        public Object clone(){   
                Employee cloned=(Employee)super.clone();  //浅克隆   
           cloned.hireDay=(Date)hireDay.clone(); //克隆子对象   
           return cloned;   
        }   
}  

 

 

 

 

 

分享到:
评论
10 楼 zjuttsw 2013-01-10  
shawnxjf 写道
package com;

import java.util.Date;

class Address implements Cloneable{
String city;
Address(String city){
this.city=city;
}
public String getCity() {
return city;
}

@Override
public String toString() {
return "Address [city=" + city + "]";
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Employee implements Cloneable{
String name;
int salary;
Address addr;
Date hirDate;
public Employee(String name,int salary,Address city,Date hirDate){
this.name=name;
this.salary=salary;
this.addr=city;
this.hirDate=hirDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public Date getHirDate() {
return hirDate;
}
public void setHirDate(Date hirDate) {
this.hirDate = hirDate;
}
public void setSalary(int salary) {
this.salary = salary;
}
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Employee [addr=" + addr + ", hirDate=" + hirDate + ", name="
+ name + ", salary=" + salary + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Employee cloned=(Employee)super.clone();
return cloned;
}
public static void main(String[] args) throws CloneNotSupportedException{ //
Employee e1=new Employee("李",500,new Address("北京"),new Date()); //Employee是一个自定义类     
Employee e2=e1; //赋值对象     
e2.setName("王");//改变对象e2的名字
e2.setSalary(1000);
e2.setAddr(new Address("上海"));
e2.setHirDate(new Date(2007,7,1));
System.out.println("e1:"+e1); //打印e1的结果: 王     
System.out.println("e2:"+e2); //打印e1的结果: 王     
  
Employee e11=new Employee("李",500,new Address("北京"),new Date()); //Employee是一个自定义类  
Employee e21=(Employee)e11.clone(); //赋值对象  
e21.setName("王");//改变对象e2的名字  
e21.setSalary(1000);
e21.setAddr(new Address("上海"));
e21.setHirDate(new Date(2007,7,1));
System.out.println("e11:"+e11); //打印e1的结果: 王
System.out.println("e21:"+e21); //打印e1的结果: 王
}
}

这段程序打印结果为:
e1:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
e2:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
e11:Employee [addr=Address [city=北京], hirDate=Thu Sep 29 19:58:57 CST 2011, name=李, salary=500]
e21:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
克隆也正常呀!

shawnxjf 写道
package com;

import java.util.Date;

class Address implements Cloneable{
String city;
Address(String city){
this.city=city;
}
public String getCity() {
return city;
}

@Override
public String toString() {
return "Address [city=" + city + "]";
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Employee implements Cloneable{
String name;
int salary;
Address addr;
Date hirDate;
public Employee(String name,int salary,Address city,Date hirDate){
this.name=name;
this.salary=salary;
this.addr=city;
this.hirDate=hirDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public Date getHirDate() {
return hirDate;
}
public void setHirDate(Date hirDate) {
this.hirDate = hirDate;
}
public void setSalary(int salary) {
this.salary = salary;
}
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Employee [addr=" + addr + ", hirDate=" + hirDate + ", name="
+ name + ", salary=" + salary + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Employee cloned=(Employee)super.clone();
return cloned;
}
public static void main(String[] args) throws CloneNotSupportedException{ //
Employee e1=new Employee("李",500,new Address("北京"),new Date()); //Employee是一个自定义类     
Employee e2=e1; //赋值对象     
e2.setName("王");//改变对象e2的名字
e2.setSalary(1000);
e2.setAddr(new Address("上海"));
e2.setHirDate(new Date(2007,7,1));
System.out.println("e1:"+e1); //打印e1的结果: 王     
System.out.println("e2:"+e2); //打印e1的结果: 王     
  
Employee e11=new Employee("李",500,new Address("北京"),new Date()); //Employee是一个自定义类  
Employee e21=(Employee)e11.clone(); //赋值对象  
e21.setName("王");//改变对象e2的名字  
e21.setSalary(1000);
e21.setAddr(new Address("上海"));
e21.setHirDate(new Date(2007,7,1));
System.out.println("e11:"+e11); //打印e1的结果: 王
System.out.println("e21:"+e21); //打印e1的结果: 王
}
}

这段程序打印结果为:
e1:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
e2:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
e11:Employee [addr=Address [city=北京], hirDate=Thu Sep 29 19:58:57 CST 2011, name=李, salary=500]
e21:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
克隆也正常呀!


那是因为你还没理解深克隆的含义。
通过浅克隆生成的对象和原对象,它们的实例对象引用其实指向同一对象。
而你并没有修改子对象,而是再new一个新对象,让子对象指向新的对象。
这样一来,克隆对象和原对象里相同的子对象就指向了不同的对象。
9 楼 zjuttsw 2013-01-10  
讲得很好。就是有点瑕疵,代码有些拷贝了两遍,override说成了overload。
8 楼 shawnxjf 2011-09-29  
package com;

import java.util.Date;

class Address implements Cloneable{
String city;
Address(String city){
this.city=city;
}
public String getCity() {
return city;
}

@Override
public String toString() {
return "Address [city=" + city + "]";
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Employee implements Cloneable{
String name;
int salary;
Address addr;
Date hirDate;
public Employee(String name,int salary,Address city,Date hirDate){
this.name=name;
this.salary=salary;
this.addr=city;
this.hirDate=hirDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public Date getHirDate() {
return hirDate;
}
public void setHirDate(Date hirDate) {
this.hirDate = hirDate;
}
public void setSalary(int salary) {
this.salary = salary;
}
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Employee [addr=" + addr + ", hirDate=" + hirDate + ", name="
+ name + ", salary=" + salary + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
Employee cloned=(Employee)super.clone();
return cloned;
}
public static void main(String[] args) throws CloneNotSupportedException{ //
Employee e1=new Employee("李",500,new Address("北京"),new Date()); //Employee是一个自定义类     
Employee e2=e1; //赋值对象     
e2.setName("王");//改变对象e2的名字
e2.setSalary(1000);
e2.setAddr(new Address("上海"));
e2.setHirDate(new Date(2007,7,1));
System.out.println("e1:"+e1); //打印e1的结果: 王     
System.out.println("e2:"+e2); //打印e1的结果: 王     
  
Employee e11=new Employee("李",500,new Address("北京"),new Date()); //Employee是一个自定义类  
Employee e21=(Employee)e11.clone(); //赋值对象  
e21.setName("王");//改变对象e2的名字  
e21.setSalary(1000);
e21.setAddr(new Address("上海"));
e21.setHirDate(new Date(2007,7,1));
System.out.println("e11:"+e11); //打印e1的结果: 王
System.out.println("e21:"+e21); //打印e1的结果: 王
}
}

这段程序打印结果为:
e1:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
e2:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
e11:Employee [addr=Address [city=北京], hirDate=Thu Sep 29 19:58:57 CST 2011, name=李, salary=500]
e21:Employee [addr=Address [city=上海], hirDate=Thu Aug 01 00:00:00 CST 3907, name=王, salary=1000]
克隆也正常呀!
7 楼 junJZ_2008 2010-05-06  
深度克隆可以参考这个
http://www.iteye.com/topic/659877#1484197
6 楼 meiowei 2010-04-19  
又复习了一遍基础知识,thks
5 楼 tasnow8 2010-04-19  
浅COPY bean的话可以用 BeanUtils.cloneBean方法..
深copy用对象串行化的方法吧
4 楼 Heart.X.Raid 2010-04-19  
for(int i =0;i<10000;i++){

}

你知道现代计算机对这样一个循环需要多久时间吗?连毫秒都显示不出来的。

循环100000000大概281ms左右

你的new Date()是没有什么不太一样的。换个别的对象试试
3 楼 chenyulong1 2010-04-18  
handleException 写道
不对啊..我试过了..那个原始对象的d 没有改变啊
import java.util.Date;

public class CloneTest implements Cloneable{

private String s ;
private Date d ;

public void setD(Date d) {
this.d = d;
}
public void setS(String s) {
this.s = s;
}
public String say(){
return s + d ;
}
public Object clone(){ 
CloneTest cloned = null ;
try {
cloned = (CloneTest)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();

  return cloned; 
     }

public static void main(String[] args) {
CloneTest clone = new CloneTest();
clone.setS("ssssssssssss") ;
clone.setD(new Date());
for(int i =0;i<10000;i++){

}
System.out.println( clone.say());
CloneTest clone2 =  (CloneTest) clone.clone();
clone2.setS("sss");
clone2.setD(new Date());
System.out.println(clone.say()) ;
System.out.println( clone2.say()) ;
}
}


哥,一分钟之内是跑得完i从0到10000的,而且不同是值引用的对象不同。clone.getD()==clone2.getD()是false的。
2 楼 handleException 2010-04-18  
不对啊..我试过了..那个原始对象的d 没有改变啊
import java.util.Date;

public class CloneTest implements Cloneable{

private String s ;
private Date d ;

public void setD(Date d) {
this.d = d;
}
public void setS(String s) {
this.s = s;
}
public String say(){
return s + d ;
}
public Object clone(){ 
CloneTest cloned = null ;
try {
cloned = (CloneTest)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();

  return cloned; 
     }

public static void main(String[] args) {
CloneTest clone = new CloneTest();
clone.setS("ssssssssssss") ;
clone.setD(new Date());
for(int i =0;i<10000;i++){

}
System.out.println( clone.say());
CloneTest clone2 =  (CloneTest) clone.clone();
clone2.setS("sss");
clone2.setD(new Date());
System.out.println(clone.say()) ;
System.out.println( clone2.say()) ;
}
}
1 楼 舞指如歌 2010-04-18  
学习了,感谢

相关推荐

Global site tag (gtag.js) - Google Analytics