文章目錄
- 拷貝
-
- 直接指派
- 淺拷貝
-
- 實作方式
- 特殊情況
- 深拷貝
-
- 實作方式
- 多層克隆
拷貝
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL3FFRNVTT65kMNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLygTM3ATOyIjM2ADNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
直接指派
直接指派的方式沒有生産新的對象,隻是生新增了一個對象引用
淺拷貝
如果原型對象的成員變量是值類型,将複制一份給克隆對象,也就是說在堆中擁有獨立的空間;如果原型對象的成員變量是引用類型,則将引用對象的位址複制一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的記憶體位址。換句話說,在淺克隆中,當對象被複制時隻複制它本身和其中包含的值類型的成員變量,而引用類型的成員對象并沒有複制。![]()
Java拷貝(指派、淺拷貝、深拷貝)拷貝 ![]()
Java拷貝(指派、淺拷貝、深拷貝)拷貝
實作方式
被複制類需要實作 Cloneable 接口,重寫
clone
方法即可,對 person 類進行改造,使其可以支援淺拷貝。
public class Person implements Cloneable {
private String name; // 姓名
private int age; // 年齡
private String email; // 郵件
private String desc; // 描述
private Person friend; // 朋友
/* 重寫 clone 方法,需要将權限改成 public ,直接調用父類的 clone 方法就好了 */
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
特殊情況
public class PersonApp {
public static void main(String[] args) throws Exception {
// 初始化一個對象
Person person = new Person("張三",20,"[email protected]","我是張三");
// 複制對象
Person person1 = (Person) person.clone();
// 改變 person1 的屬性值
person1.setName("我是張三的克隆對象");
// 修改 person age 的值
person1.setAge(22);
System.out.println("person對象:"+person);
System.out.println();
System.out.println("person1對象:"+person1);
}
}
// person對象:Person{name='張三', age=20, email='[email protected]', desc='我是張三'}
// person1對象:Person{name='我是張三的克隆對象', age=22, email='[email protected]', desc='我是張三'}
原因:
- String 和 Integer 等包裝類都是不可變的對象。
- 當需要 修改 不可變對象的值時,需要在記憶體中生成一個新的對象來存放新的值,然後将原來的引用指向新的位址。
- 是以在這裡我們修改了 person1 對象的 name 屬性值,person1 對象的 name 字段指向了記憶體中新的 name 對象,但是我們并沒有改變 person 對象的 name 字段的指向,是以 person 對象的 name 還是指向記憶體中原來的 name 位址,也就沒有變化
深拷貝
相對于淺拷貝,深拷貝是一種完全拷貝,無論是值類型還是引用類型都會完完全全的拷貝一份,在記憶體中生成一個新的對象。拷貝對象和被拷貝對象沒有任何關系,互不影響。
實作方式
- 實作Cloneable接口
- 實作Serializable接口
實作Cloneable接口:
clone 方法不能是簡單的調用super的clone()。
class Address{
String name;
String eCode;
public Address( String na, String eC;)
{
this.name = na;
this.eCode = eC;
}
}
public class Person implements Cloneable {
private String name; // 姓名
private int age; // 年齡
private String email; // 郵件
private Address address; //
private Person friend; // 朋友
/* 重寫 clone 方法,需要将權限改成 public ,直接調用父類的 clone 方法就好了 */
@Override
public Object clone() throws CloneNotSupportedException {
Person newp = new Person();
newp.name = new String(this.name);
newp.age = this.age;
newp.email = new String(this.email);
newp.address = new Address(this.address.name, this.address.eCode);
return newp;
}
}
實作Serializable接口:
protected Son deepClone() throws IOException, ClassNotFoundException {
Son son=null;
//在記憶體中建立一個位元組數組緩沖區,所有發送到輸出流的資料儲存在該位元組數組中
//預設建立一個大小為32的緩沖區
ByteArrayOutputStream byOut=new ByteArrayOutputStream();
//對象的序列化輸出
ObjectOutputStream outputStream=new ObjectOutputStream(byOut);//通過位元組數組的方式進行傳輸
outputStream.writeObject(this); //将目前student對象寫入位元組數組中
//在記憶體中建立一個位元組數組緩沖區,從輸入流讀取的資料儲存在該位元組數組緩沖區
ByteArrayInputStream byIn=new ByteArrayInputStream(byOut.toByteArray()); //接收位元組數組作為參數進行建立
ObjectInputStream inputStream=new ObjectInputStream(byIn);
son=(Son) inputStream.readObject(); //從位元組數組中讀取
return son;
}
多層克隆
Cloneable接口方式可以實作深拷貝。但是有個問題,如果引用數量或者層數太多了怎麼辦呢?不可能去每個對象挨個寫clone()吧?那怎麼辦呢?借助序列化!!!。
實作 Serializable 接口方式也可以實作深拷貝,而且這種方式還可以解決多層克隆的問題,多層克隆就是引用類型裡面又有引用類型,層層嵌套下去。