java提高(15)---java深淺拷貝
#java深淺拷貝
一、前言
為什麼會有深淺拷貝這個概念?
我覺得主要跟JVM記憶體分配有關,對於基本資料型別,只存在棧記憶體,所以它的拷貝不存在深淺拷貝這個概念。而對於物件而言,一個物件的建立會在記憶體中分配兩塊空間,一個在棧記憶體存物件的引用指標,一個在堆記憶體存放物件。這個時候會有一個問題,你拷貝的只是這個引用指標還是拷貝兩塊記憶體一起拷貝,這個時候就會有深淺拷貝一說。
還有之前我認為Arrays.copyOf()是深度拷貝,親測後發現原來它也是淺拷貝。下面進行具體說明。
二、資料型別
資料分為 基本資料
型別(int, boolean, double, byte, char等)和 物件資料
型別。
基本資料型別的特點: 直接儲存在棧(stack)中的資料
.
在棧記憶體儲存物件引用,真實的資料存放在堆記憶體裡
引用資料型別在棧中儲存了指標,該指標指向堆中該實體的起始地址。當直譯器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。
三、什麼是淺拷貝和深拷貝
首先需要明白, 深拷貝和淺拷貝是隻針對Object和Array這樣的引用資料型別的
。那先來看看淺拷貝和深拷貝的概念。
在 Java 中,除了基本資料型別(元型別)之外,還存在 類的例項物件 這個引用資料型別。而一般使用 =
號做賦值操作的時候。對於基本資料型別,實際上是拷貝的它的值,但是對於物件而言,其實賦值的只是這個物件的引用,將原物件的引用傳遞過去,他們實際上還是指向的同一個物件。
淺拷貝
:如果在拷貝這個物件的時候,只對基本資料型別進行了拷貝,而對引用資料型別只是進行了引用的傳遞,而沒有真實的建立一個新的物件。
深拷貝
:在對引用資料型別進行拷貝的時候,建立了一個新的物件,並且複製其內的成員變數。
深拷貝和淺拷貝的示意圖大致如下:

具體接下來程式碼演示。
四、程式碼演示
1、淺拷貝
Person
public class Person { public String name; public Integer age; public String sex; /** * 提供get和set方法和全參建構函式 */ }
Test
public static void main(String[] args) throws Exception { Person person = new Person("小小",3,"女"); //將person值賦值給person1 Person person1 = person; System.out.println(person); System.out.println(person1); person1.setName("小小她爸"); System.out.println("person 中 name為:"+person.getName()); System.out.println("person1 中 name為:"+person.getName()); }
檢視執行結果

從圖片中我們可以很明顯看出,它們指向的記憶體地址是一致的,同樣我改變person1的屬性值時發現person的屬性值也改變了。
說明
:對於物件用 "="
賦值 其實只是引用指標的複製,這兩個引用還是指向同一個物件。
2、深拷貝
如果要實現深拷貝就會比較複雜點
Student
/** * 如果物件要實現深拷貝 那麼實體需要做兩步 * 1、實體實現Cloneable介面 * 2、重寫 clone()方法 */ public class Student implements Cloneable { public String name; public Integer age; public String sex; //這也是個實體 public Address address; /** * 提供get和set方法和全參建構函式 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
Test
public static void main(String[] args) throws Exception { Student student = new Student("小小", 3, "女", null); //將person值賦值給person1 Student student1 = (Student) student.clone(); System.out.println(student); System.out.println(student1); student1.setName("小小她爸"); System.out.println("person 中 name為:" + student.getName()); System.out.println("person1 中 name為:" + student1.getName()); }
這裡可以已經是兩個不同的物件了。但是這裡需要注意的是,如果物件中含有物件,這個物件還是淺拷貝。
Address
public class Address{ public Stringcity; publicint phone; /** * 提供get和set方法和全參建構函式 */ }
Test
public static void main(String[] args) throws Exception { Address address = new Address("杭州", 1888888888); Student student2 = new Student("小小", 3, "女", address); //將person值賦值給person1 Student student3 = (Student) student2.clone(); address.setCity("北京天安門"); System.out.println("person2 中 city為:" + student2.getAddress().getCity()); System.out.println("person3 中 city為:" + student3.getAddress().getCity()); }
我們發現雖然Student是實現了深拷貝,但Address卻還是淺拷貝,那如何讓Adress也實現深拷貝呢。
Address修改
public class Address implements Cloneable { public Stringcity; publicint phone; /** * 提供get和set方法和全參建構函式 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }
Student修改
//修改clone方法 @Override protected Object clone() throws CloneNotSupportedException { Student s = (Student) super.clone(); s.address = (Address) address.clone(); return s; }
弊端
: 這裡我們Person 類只有一個 Address 引用型別,而 Address 類沒有,所以我們只用重寫 Address 類的clone 方法,但是如果 Address 類也存在一個引用型別,
那麼我們也要重寫其clone 方法,這樣下去,有多少個引用型別,我們就要重寫多少次,如果存在很多引用型別,那麼程式碼量顯然會很大,所以這種方法不太合適。
所以還有另一種實現深拷貝方法。
序列化實現深拷貝
//序列化實現深拷貝 public Object deepClone() throws Exception{ // 序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); // 反序列化 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } //因為序列化產生的是兩個完全獨立的物件,所有無論巢狀多少個引用型別,序列化都是能實現深拷貝的。
五、Arrays.copyOf()
之前我誤以為Arrays.copyOf()為深拷貝,那只是因為我用的是基本資料型別作為陣列,而基本資料型別上面已經說過它沒有深淺拷貝這個概念,可以把他理解成只有深拷貝。
public static void main(String[] args) { //1、基本資料型別 int[] a = {0, 1, 2, 3}; // Arrays.copyOf拷貝 int[] copy = Arrays.copyOf(a, a.length); a[0] = 1; System.out.println(Arrays.toString(copy)); System.out.println(Arrays.toString(a)); //2、物件陣列 Student[]stuArr = {new Student("小小", 3, "女"),new Student("小小爸", 29, "男"),new Student("小小媽", 27, "女")}; // Arrays.copyOf拷貝 Student[] copyStuArr = Arrays.copyOf(stuArr, stuArr.length); copyStuArr[0].setName("小小爺爺"); System.out.println(Arrays.toString(stuArr)); System.out.println(Arrays.toString(copyStuArr)); }
執行結果:
可以明顯看出,對於基本資料型別只有深拷貝,而對於陣列物件而言,明視訊記憶體在深淺拷貝,而且可以看出 Arrays.copyOf()為淺拷貝
。
只要自己變優秀了,其他的事情才會跟著好起來(少將2)