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滿足四大特性:自反性
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中深拷貝和淺拷貝(deepcopy和shallowcopy)
原型模式要求用深拷貝(複製/克隆)的方式實現,對這個概念很模糊。在自己查了相關資料後,我將從三個方面講述深淺拷貝: 圖形表述深淺拷貝區別及特點 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)深拷貝和淺拷貝
本人是新手,為了以後的厚積薄發,也為了把自己的一些學習經驗寫下來與需要的朋友們分享,今兒特地早起理理! 定義: 淺拷貝:只複製一個物件,物件內部存在的、指向其他物件的陣列或者引用則不復制。 深拷貝:物件、物件內部的引用都複製。 為了更好的理解它們