首先我们看看浅拷贝和深拷贝的定义

    浅拷贝:只复制一个对象,对象内部存在的指向其他对象数组或者引用则不复制

    深拷贝:对象,对象内部的引用均复制

    为了更好的理解它们的区别我们假设有一个对象A,它包含有2对象对象A1和对象A2


 

    对象A进行浅拷贝后,得到对象B但是对象A1和A2并没有被拷贝


    对象A进行深拷贝,得到对象B的同时A1和A2连同它们的引用也被拷贝


  方法(一) 引用拷贝

Employee original = new Employee ("tomsui",4000);
 Employee copy = original ;

如果改变该对象,则不同引用都会受影响。)

方法(二) 浅克隆

直接利用Object的clone()方法:

protected Objectclone()   throws CloneNotSupportedException

Employee copy = (Employee)original.clone();  

注意三点:

 implements Cloneable

2) Object的clone() 是protected. 只能在包内或子类调用.(要想在保外调用必须重写clone, 并申明为public )

3) 如果浅克隆的对象中存在对象形式的成员变量:

public class Employee
 {
         String name;
         int salary;
         Date birthday;
 }

那么:

  Employee copy = (Employee) original.clone();

可变的对象成员变量拷贝得到的仍然是引用.

不变量应该包括:(可以参见<java多线程设计模式>Immutable模式中界定的情况):

a. String类对象

b. 被final定义,子对象在生存周期中仅保存一些常量 

方法(三) 深克隆

例子:

class Employee implements Cloneable
 {
         private String name;
         private double salary;
         private Date birthday;
         // setter 与 getter
         public Object clone()
         {
                 try
                 {
                         Employee cloned = (Employee ) super.clone();
                         cloned.birthday = (Date) birthday.clone();
                 }catch(CloneNotSupportedException e) {
                 return null;
                 }
         }
 }

 说明:

1)Employee 必须实现 Cloneable接口 (标志接口)

     标志接口:完全的空接口。这里的作用是告诉JVM,类的设计者理解了cloneable()方法,可以通过isInstanceOf进行运行时检查。

2)覆写的clone()方法必须定义为public (原是protected)

3)clone()方法体必须在try-catch {}中,捕获处理CloneNotSupportedException 。(防止类没有实现Cloneable接口,正确实现了深克隆的话,这个异常肯定不会抛出)

4) 此处Date类(或者其他类)必须已经实现Cloneable接口(这样才能调用clone() )

ps.   JDK中,StringBuffer没有覆写clone()方法,虽然它确实继承了Object的clone(),但在实际应用中, 因为


StringBuffer绝不会有子类,而且是在JDK外使用StringBuffer,所以被protected界定的clone()方法是完全不可见的!

 

方法(四)使用序列化进行克隆

这种方法涉及IO操作,所以相对来讲要比方法(三)慢.

import java.io.*;
 import java.util.*; public class SerialCloneTest
 {  
    public static void main(String[] args)
    {  
       Employee harry = new Employee("Harry Hacker", 35000,
          1989, 10, 1);
       // clone harry
       Employee harry2 = (Employee)harry.clone();       // mutate harry
       harry.raiseSalary(10);       // now harry and the clone are different
       System.out.println(harry);
       System.out.println(harry2);
    }
 } /**
    A class whose clone method uses serialization.
 */
 class SerialCloneable implements Cloneable, Serializable
 {  
    public Object clone()
    {  
       try
       {  
          // save the object to a byte array
          ByteArrayOutputStream bout = new 
             ByteArrayOutputStream();
          ObjectOutputStream out 
             = new ObjectOutputStream(bout);
          out.writeObject(this);
          out.close();          // read a clone of the object from the byte array
          ByteArrayInputStream bin = new 
             ByteArrayInputStream(bout.toByteArray());
          ObjectInputStream in = new ObjectInputStream(bin);
          Object ret = in.readObject();
          in.close();          return ret;
       }  
       catch (Exception e)
       {  
          return null;
       }
    }
 } /**
    The familiar Employee class, redefined to extend the
    SerialCloneable class. 
 */
 class Employee extends SerialCloneable
 {  
    public Employee(String n, double s, 
       int year, int month, int day)
    {  
       name = n;
       salary = s;
       GregorianCalendar calendar
          = new GregorianCalendar(year, month - 1, day);
          // GregorianCalendar uses 0 for January
       hireDay = calendar.getTime();
    }    public String getName()
    {  
       return name;
    }    public double getSalary()
    {  
       return salary;
    }    public Date getHireDay()
    {  
       return hireDay;
    }    public void raiseSalary(double byPercent)
    {  
       double raise = salary * byPercent / 100;
       salary += raise;
    }    public String toString()
    {  
       return getClass().getName()
          + "[name=" + name
          + ",salary=" + salary
          + ",hireDay=" + hireDay
          + "]";
    }    private String name;
    private double salary;
    private Date hireDay;
 }