1.什麼是"克隆"?
由一個現有對象A,複製生成一個新的對象B的過程(B的初始值是由A對象確定的)。
- 拷貝對象返回的是一個新對象,而不是一個引用。
- 拷貝對象與用 new操作符返回的新對象的區別就是這個拷貝已經包含了一些原來對象的信息,而不是對象的初始信息。
2.淺克隆與深克隆
- 淺拷貝的效果就是拷貝對象時僅僅拷貝對象本身(包括對象中的基本變量和不可變引用變量(比如String類型屬性))
- 深拷貝不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象。
3.實現克隆的方法
- 實現Cloneable
- 在要實現克隆的對象類中實現Cloneable接口
Cloneable接口爲標記接口(標記接口爲用戶標記實現該接口的類具有該接口標記的功能,常見的標記接口有Serializable、Cloneable、RandomAccess),如果沒有實現該接口,在調用clone方法時就會拋出CloneNotSupportException異常。
- 在類中重寫Object的clone方法。
1.重寫是爲了擴大訪問權限【Object類的clone方法使用protected【只有Object的同包和直接子類纔可以訪問】修飾】
2.爲了實現深拷貝【Object的clone表現出來的是淺拷貝。對於可變的引用屬性也使用clone方法,即遞歸調用clone即可實現深克隆】
- 對象序列化
對象序列化也可以實現對象克隆,並且是深克隆,但是(序列化)串行化卻很耗。
- 代碼示例
//Teacher類
public class Teacher implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
Teacher (String name, int age) {
this.name = name;
this.age = age;
}
//學生類
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
Teacher t;
Student (String name, int age, Teacher t) {
this.name = name;
this.age = age;
this.t = t;
}
public Object deepClone() throws IOException, OptionalDataException,
ClassNotFoundException {
// 將對象寫到流裏(序列化)
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
// 從流裏讀出來(反序列化)
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
}
}
//測試類
public class TestDemo {
/**
* @param args
*/
public static void main(String[] args) throws OptionalDataException,
IOException, ClassNotFoundException {
long t1 = System.currentTimeMillis();
Teacher t = new Teacher("wangwu", 50);
Student s1 = new Student("zhangsan", 18, t);
Student s2 = (Student) s1.deepClone();
s2.t.name = "lisi";
s2.t.age = 30;
System.out.println("name=" + s1.t.name + "," + "age=" + s1.t.age); // 學生1的教師不改變。
long t2 = System.currentTimeMillis();
System.out.println(t2-t1);
}
}
- BeanUtils.copyProperties
- spring的BeanUtils.copyProperties實現的淺克隆
- 如果要實現深克隆,需要對於引用屬性也調用BeanUtils.copyProperties,即循環copy
- 第三方框架
對象複製工具類,實現方法如下:
- 複製對象(深度拷貝)
- 複製集合(深度拷貝)
- 複製對象到指定類(深度拷貝)
- 複製集合到指定類(深度拷貝)
- 引入maven依賴
<!-- 對象拷貝 -->
<dependency>
<groupId>uk.com.robust-it</groupId>
<artifactId>cloning</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
- 工具類
import com.alibaba.fastjson.JSON;
import com.rits.cloning.Cloner;
import java.util.ArrayList;
import java.util.List;
/**
* Bean工具類
*
*/
public class BeanUtils {
private static final Cloner cloner = new Cloner();
/**
* 複製對象(深度拷貝)
* @param object
* @param <T>
* @return
*/
public static <T> T clone(final T object){
if (object == null) {
return null;
}
return cloner.deepClone(object);
}
/**
* 複製集合(深度拷貝)
* @param object
* @param <T>
* @return
*/
public static <T> List<T> cloneList(final List<T> object){
if (object == null) {
return null;
}
return cloner.deepClone(object);
}
/**
* 複製對象到指定類(深度拷貝)
* @param object
* @param destclas 指定類
* @param <T>
* @return
*/
public static <T> T clone(final Object object, Class<T> destclas){
if (object == null) {
return null;
}
String json = JSON.toJSONString(object);
return JSON.parseObject(json, destclas);
}
/**
* 複製集合到指定類(深度拷貝)
* @param object
* @param destclas 指定類
* @param <T>
* @return
*/
public static <T> List<T> cloneList(List<?> object, Class<T> destclas) {
if (object == null) {
return new ArrayList<T>();
}
String json = JSON.toJSONString(object);
return JSON.parseArray(json, destclas);
}
}