天天看點

Java拷貝(指派、淺拷貝、深拷貝)拷貝

文章目錄

  • 拷貝
    • 直接指派
    • 淺拷貝
      • 實作方式
      • 特殊情況
    • 深拷貝
      • 實作方式
      • 多層克隆

拷貝

Java拷貝(指派、淺拷貝、深拷貝)拷貝

直接指派

直接指派的方式沒有生産新的對象,隻是生新增了一個對象引用

Java拷貝(指派、淺拷貝、深拷貝)拷貝

淺拷貝

如果原型對象的成員變量是值類型,将複制一份給克隆對象,也就是說在堆中擁有獨立的空間;如果原型對象的成員變量是引用類型,則将引用對象的位址複制一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的記憶體位址。換句話說,在淺克隆中,當對象被複制時隻複制它本身和其中包含的值類型的成員變量,而引用類型的成員對象并沒有複制。
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='我是張三'}
           

原因:

  1. String 和 Integer 等包裝類都是不可變的對象。
  2. 當需要 修改 不可變對象的值時,需要在記憶體中生成一個新的對象來存放新的值,然後将原來的引用指向新的位址。
  3. 是以在這裡我們修改了 person1 對象的 name 屬性值,person1 對象的 name 字段指向了記憶體中新的 name 對象,但是我們并沒有改變 person 對象的 name 字段的指向,是以 person 對象的 name 還是指向記憶體中原來的 name 位址,也就沒有變化

深拷貝

相對于淺拷貝,深拷貝是一種完全拷貝,無論是值類型還是引用類型都會完完全全的拷貝一份,在記憶體中生成一個新的對象。拷貝對象和被拷貝對象沒有任何關系,互不影響。

Java拷貝(指派、淺拷貝、深拷貝)拷貝

實作方式

  1. 實作Cloneable接口
  2. 實作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()吧?那怎麼辦呢?借助序列化!!!。

Java拷貝(指派、淺拷貝、深拷貝)拷貝
實作 Serializable 接口方式也可以實作深拷貝,而且這種方式還可以解決多層克隆的問題,多層克隆就是引用類型裡面又有引用類型,層層嵌套下去。