1. 程式人生 > >Java中深拷貝和淺拷貝(deepcopy和shallowcopy)

Java中深拷貝和淺拷貝(deepcopy和shallowcopy)

原型模式要求用深拷貝(複製/克隆)的方式實現,對這個概念很模糊。在自己查了相關資料後,我將從三個方面講述深淺拷貝:

  • 圖形表述深淺拷貝區別及特點
  • Object類中的clone()方法實現深淺拷貝
  • 實際應用中深淺拷貝區別

一 圖形表述深淺拷貝區別及特點

前提:深淺拷貝基本上都是討論引用型別的變數(物件,String類等),對基本資料型別的變數,深淺拷貝都是要複製的。下面以圖形的形式表述深淺拷貝的區別:

這裡寫圖片描述

淺拷貝:只複製當前的物件,對該物件內部的引用(其他類物件作為自己的元素-也叫對其他物件的引用)不能複製(在堆記憶體中從新建立空間,內容相同,但地址不同)。 
深拷貝:對物件內部的引用均複製,是建立一個新的例項,並複製例項。

簡言之:是否複製了子物件。/修改了克隆後的物件的屬性值,影響到原物件-淺拷貝;不影響叫深拷貝。

二 Object類中的clone()方法實現深淺拷貝

2.1 java記憶體模型

棧:存放基本型別的變數資料,區域性變數,和物件的引用,但物件本身不存放在棧中,而是存放在堆(new 出來的物件)或者常量池中(字串常量物件存放在常量池中。)

堆:用來存放動態產生的資料,比如new產生的物件,陣列。注意創建出來的物件只包含屬於各自的成員變數,並不包括成員方法。因為同一個類的物件擁有各自的成員變數,儲存在各自的堆中,但是他們共享該類的方法,並不是每建立一個物件就把成員方法複製一次。

2.2 “==”和equals()區別

  • 對於基本資料型別(int,float,double,byte,short,long,boolean,char),只能用==
  • 對於基本資料型別的封裝類(Boolean,Integer,Long,Float,Double),==比較的是指向物件的地址,比較的是棧記憶體中存放的物件的引用(地址),equals比較的是值。
  • 如果是類物件,那麼在物件的類沒有重寫equals()方法時,==和equals都是比較地址。如果覆蓋了equals()方法,根據實際情況比較。
  • String類,Data類都重寫了equals方法,所以==比較地址,equals比較值。

另外equals滿足四大特性:自反性

:x.equals(x)為true; 對稱性:y.equals(x)為真時,x.equals(y)也為真;傳遞性:x.equals(x)為真,y.equals(z)為真,那麼x.equals(z)為真;非空性:對任何非空的引用值,x.equals(null)為假。

2.3 clone()方法的使用

Java中父類java.lang.Object提供了clone()方法,但考慮到安全性問題,一方面將clone()方法的訪問級別設定為protected型別,限制外部類訪問,另一方面,強制需要提供clone功能的子類實現java.lang.Cloneable介面。總結就是如果一個類需要被克隆,該類需要實現clone方法和實現Cloneable介面,缺一不可。

在java中使用clone()方法如何判別深淺拷貝呢,就是判斷拷貝後的物件是否相等,即判斷兩物件的地址是否相同。B=clone(A),A==B為真否。以下圖為例:p1是物件1的引用變數,物件1含有對物件2和物件3的引用;p2是p1的clone()。執行p1.物件1==p2.物件1為ture,因為他們共同儲存物件2的引用。p1==p2為false,因為指向不同的地址。下面從4個角度說明clone()如何體現深淺拷貝。

這裡寫圖片描述

Clone()方法只複製當前物件,對於沒有內部引用的物件,p2克隆p1後,兩個物件指向不同的地址,p1==p2操作返回false。

這裡寫圖片描述

package clone1;
class  Person implements Cloneable{
    int age;
    String name;
    public  Person(int a, String b){
        age=a;
        name=b;
    }
    public Person(Person p){
        this.age=p.age;
        this.name=p.name;
    }
@Override
public Object clone() {
    Person per=null;    
    try {
            per=(Person)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return  per;
}
 
}
public class Body {
    public static void main(String[] args){
        Person p1=new Person(10,"wang");
        Person p2 = (Person) p1.clone();
        System.out.println(p1);//輸出堆記憶體的地址
        System.out.println(p2);
        System.out.println(p1==p2);
    }
 
}
}
輸出:
P1地址:[email protected]
P2地址:[email protected]
P1==p2:false
 

當Person類含有其他物件的引用時(含有其他類的物件),增加一個類student,內含成績grade這個元素,該類物件要作為Person類的成員,新增內部引用。呼叫clone方法會發現其他物件的引用並沒有克隆成功。

這裡寫圖片描述

package clone1;
class Student{
    String grade;
    public Student(){
    }
    public void  setGrade(String a){
        this.grade=a;
    }
}
class  Person implements Cloneable{
    int age;
    String name;
    Student  grades;
    public void setGrades(Student gra){
        this.grades=gra;
    }
    public  Person(int a, String b,Student gra){
        age=a;
        name=b;
        this.grades=gra;
    }
    public Person(Person p){
        this.age=p.age;
        this.name=p.name;
        //this.grade1=p.grade;
    }
@Override
public Object clone() {
    Person per=null;    
    try {
            per=(Person)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return  per;
}
}
public class Body {
    public static void main(String[] args){
        // TODO Auto-generated method stub
        Student stu=new Student();
        stu.setGrade("100");
        Person p1=new Person(10,"wang",stu);
        Person p2 = (Person)  p1.clone();
        System.out.println(p1.grades==p2.grades);
    }
}
結果:p1.grades==p2.grades:true

執行

stu.setGrade("200");
System.out.println(p2.grades.grade);
System.out.println(p1.grades.grade);

後,會發現,兩個結果相同,說明指向同一個物件,一變全變。這就表明grade物件並沒有拷貝,p1和p2存放的是grade的引用。地址相同,這是淺拷貝,也就是說對於不重寫的clone()方法,預設是淺拷貝。

下面仔細的重寫下clone方法,變成深拷貝,不再是修改克隆後的物件的屬性值,影響到原物件。 
這裡寫圖片描述

package clone1;
class Student implements Cloneable{
    String grade;
    public Student(){
 
    }
    public void  setGrade(String a){
        this.grade=a;
    }
    public Object clone(){
        Student stu=null;
        try {
            stu=(Student)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return stu;
    }
}
class  Person implements Cloneable{
    int age;
    String name;
    Student  grades;
    public void setGrades(Student gra){
        this.grades=gra;
    }
    public  Person(int a, String b,Student gra){
        age=a;
        name=b;
        this.grades=gra;
    }
    public Person(Person p){
        this.age=p.age;
        this.name=p.name;
        this.grades=p.grades;
    }
@Override
public Object clone() {
    Person per=null;    
    try {
            per=(Person)super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    per.grades=(Student)grades.clone();//深拷貝:就是對引用的物件單獨拷貝一次哦。
        return  per;
}
 
}
public class Body {
    public static void main(String[] args){
        // TODO Auto-generated method stub
        Student stu=new Student();
        stu.setGrade("100");
        Person p1=new Person(10,"wang",stu);
        Person p2 = (Person)  p1.clone();
        System.out.println(p1.grades==p2.grades);
    }
 
}
 
結果:p1.grades==p2.grades:fals

執行

stu.setGrade("200");
System.out.println(p2.grades.grade);
System.out.println(p1.grades.grade);

發現執行結果為200和100,說明的確不是指向同一個物件了(相同引用),而是單獨指向,不再一變全變。

三 實際應用中深淺拷貝區別

  • 如果拆開來對物件2的元素依次賦值,這樣的肯定就是深度複製了。例如p2.grades=new Student(){}因為這樣是明顯的建立新例項來進行賦值,沒有傳遞引用等操作。

  • Person a1=new Person(“yellow”,new Person(“li”,10,F)); 
    Person a2=new Person(a1) //若a1中沒有引用,那肯定地址不同,若有物件引用,這種方式不能對引用建立新的記憶體空間,儲存引用。

  • Person a2=a1;//最顯而易見的淺複製,全是引用。

相關推薦

Java拷貝拷貝(deepcopyshallowcopy)

原型模式要求用深拷貝(複製/克隆)的方式實現,對這個概念很模糊。在自己查了相關資料後,我將從三個方面講述深淺拷貝: 圖形表述深淺拷貝區別及特點 Object類中的clone()方法實現深淺拷貝 實際應用中深淺拷貝區別 一 圖形表述深淺拷貝區別及特點 前提:深淺拷貝基本

Java拷貝(復制)拷貝(復制)

alt public min import containe long serializa port nbsp 深拷貝(深復制)和淺拷貝(淺復制)是兩個比較通用的概念,尤其在C++語言中,若不弄懂,則會在delete的時候出問題,但是我們在這幸好用的是Java。雖然java

Java拷貝拷貝

detail tle pac err @override 復制對象 deep har 間接   淺談Java中的深拷貝和淺拷貝(轉載) 原文鏈接: http://blog.csdn.net/tounaobun/article/details/8491392 假如說你想復制一

java拷貝複製)拷貝複製)

淺拷貝: 淺拷貝又稱為淺複製,淺克隆,淺拷貝是指拷貝時只拷貝物件本身(包括物件中的基本變數),而不拷貝物件包含的引用所指向的物件,拷貝出來的物件的所有變數的值都含有與原來物件相同的值,而所有對其他物件的引用都指向原來的物件,簡單地說,淺拷貝只拷貝物件不拷貝引用

JAVA對象的克隆及拷貝拷貝

output err 解釋 深拷貝和淺拷貝 color 賦值語句 的區別 num article 使用場景: 在日常的編程過程 中,經常會遇到,有一個對象OA,在某一時間點OA中已經包含了一些有效值 ,此時可能會需一個和OA完全相對的新對象OB,並且要在後面的操作中對OB

java拷貝拷貝

做專案時,可能會碰到這樣的一個問題,就是需要把一個物件的屬性完全拷貝到另一個物件上.當這個物件是個簡單物件(即屬性不包括對其他物件的引用)時用淺拷貝來完成物件的拷貝.即在實體類中實現Clonable介面,實現 public Object Clone()方法.通過呼叫父類的su

Java 拷貝拷貝

淺拷貝:對基本資料型別進行值傳遞,對引用資料型別進行引用傳遞般的拷貝,此為淺拷貝

js拷貝拷貝

所有 object 簡單的 col images new color 其他 java 深復制和淺復制只針對像 Object, Array 這樣的復雜對象的。簡單來說,淺復制只復制一層對象的屬性,而深復制則遞歸復制了所有層級。 深淺拷貝 的主要區別就是:復制的是引用(地址)還

js 引用類型 的拷貝 拷貝的區別

而是 query reac cat 避免 string val this 臨時 一、曾經在讀JQ源碼的時候,對深拷貝算是有了一點的理解。我們在項目中是不是經常會遇到這樣的問題呢? 後臺返回一個數組對象(引用類型).次數在頁面渲染中需要對部分數據進行處理 比如:銀行卡6234

淺析Python拷貝拷貝

int lis end 四種 都是 變量 內存空間 string -- 按照以下不同情況,在IDE中逐個解除註釋,就明白了 import copy """ 第一種情況,不可變類型變量,都是引用 """ # a = 1 # a = (11, 222, 333) # a =

Python拷貝拷貝

append class pre deepcopy color com htm .com har 來自:I‘m Me! python中的對象之間賦值時是按引用傳遞的,如果需要拷貝對象,需要使用copy模塊。 1. copy.copy()淺拷貝:只拷貝父對象,不拷貝對象內部的

Python復制、拷貝拷貝的區別

ron 一份 謝謝 操作 完成 函數 技術 也會 python解釋器 深拷貝定義(deepcopy) 在Python中,由於一切皆對象,所以任何變量都可以被引用,也即可以被賦值給任何變量。但是在Python中,給變量賦值,是區分的,一般情況下,Python中的變量賦值都是淺

JavaScript拷貝拷貝方法總結

在日常的程式碼書寫中常常會遇到拷貝問題,今天我們就來總結一下常用的淺拷貝和深拷貝都有哪些實現方法。 淺拷貝常用方法: slice var arr1=[1,2],arr2=arr1.slice(); console.log(arr1);//[1,2] console.log(a

深入剖析javaScript拷貝拷貝

在面試時經常會碰到面試官問:什麼是深拷貝和淺拷貝,請舉例說明?如何區分深拷貝與淺拷貝,簡單來說,假設B複製了A,當修改A時,看B是否會發生變化,如果B也跟著變了,說明這是淺拷貝,如果B沒變,那就是深拷貝;我們先看兩個簡單的案例: //案例1(深拷貝) var a1 = 1, a2= a1; conso

Java拷貝拷貝克隆克隆)

Java中建立物件有兩種方式: 通過new操作符建立一個物件 通過clone方法來複制一個物件 使用反序列化來建立一個物件 通過使用Class類的newInstance方法來建立一個物件 使用Constructor類的newInstance方法來建立一個物件 第一種方法,通過ne

java拷貝拷貝

淺拷貝: public class PrototypeClass implements Cloneable { private List<String> params = new ArrayList<String>(); @Over

Js物件的拷貝拷貝

淺拷貝:只拷貝物件的基礎屬性值,對屬性值為物件或陣列的屬性則拷貝指標。  深拷貝:拷貝物件的所有屬性作為一個全新的物件。拷貝前後的物件互不影響。 淺拷貝僅僅是指向被複制的記憶體地址,如果原地址中物件被改變了,那麼深拷貝出來的物件也會相應改變。 一、物件引用 物件引用容易

低門檻徹底理解JavaScript拷貝拷貝

規範 數據類型 ash 隨著 array 賦值 mbo 進行 函數 作者 | 吳勝斌 來源 | https://www.simbawu.com/article/search/9 在說深拷貝與淺拷貝前,我們先看兩個簡單的案例: //案例1var num1 =

python的“拷貝拷貝” copy

直接給例子 以下所有操作都是基於 a 這個list 來講解的 >>> import copy >>> a=[1,2,3,4,[5,6,7],(11,23),{1:2,3:4}] 直接“=” 這樣傳的**“引用”**,兩者指向記憶體中同樣的

java初學者基礎知識積累---(1)拷貝拷貝

本人是新手,為了以後的厚積薄發,也為了把自己的一些學習經驗寫下來與需要的朋友們分享,今兒特地早起理理!   定義:     淺拷貝:只複製一個物件,物件內部存在的、指向其他物件的陣列或者引用則不復制。     深拷貝:物件、物件內部的引用都複製。     為了更好的理解它們