首先我们看看浅拷贝和深拷贝的定义
浅拷贝:只复制一个对象,对象内部存在的指向其他对象数组或者引用则不复制
深拷贝:对象,对象内部的引用均复制
为了更好的理解它们的区别我们假设有一个对象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;
}