日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java list拷贝_深入了解浅拷贝与深拷贝

發(fā)布時(shí)間:2025/3/12 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java list拷贝_深入了解浅拷贝与深拷贝 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在學(xué)習(xí)深拷貝和淺拷貝之前,咱們先來一個(gè)例子:

import java.util.ArrayList;public class MyBaby implements Cloneable {/*** 私有變量*/private ArrayList<String> list = new ArrayList<>();@Overrideprotected Object clone() throws CloneNotSupportedException {MyBaby myBaby = null;try {myBaby = (MyBaby) super.clone();} catch (CloneNotSupportedException ex) {ex.printStackTrace();}return myBaby;}/*** 給List設(shè)置** @param value 值*/public void setValue(String value) {this.list.add(value);}/*** 獲取list** @return list*/public ArrayList<String> getValue() {return this.list;} }

在MyBaby類中有一個(gè)私有變量list,類似為List,然后咱們使用setValue對其進(jìn)行設(shè)值,使用getValue進(jìn)行取值。接下來咱們來看看他是如何拷貝的。

public class TestMyBaby {public static void main(String[] args) {MyBaby baby = new MyBaby();baby.setValue("Java后端技術(shù)棧");try {MyBaby myBabyClone = (MyBaby) baby.clone();myBabyClone.setValue("咖啡");System.out.println(baby.getValue());} catch (CloneNotSupportedException e) {e.printStackTrace();}} }

猜想運(yùn)行結(jié)果會是神馬?

[Java后端技術(shù)棧, 咖啡]

怎么會這樣呢?怎么會有“咖啡”呢?

是因?yàn)镴ava給我們做了一個(gè)偷懶性的拷貝動作,Object類原本就提供一個(gè)方法clone用來拷貝對象,因?yàn)槠鋵ο髢?nèi)部的數(shù)組、引用對象等都不拷貝,還是指向了原生對象的內(nèi)部元素地址,這種拷貝就叫做淺拷貝。

淺拷貝

上面這個(gè)拷貝也太淺了吧,兩個(gè)對象引用都boby、myBaby共享一個(gè)私有變量list,都可以對list進(jìn)行改變,是一種非常不安全的方式。

再看一個(gè)例子;

public class Person implements Cloneable {private int age;private String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {Person person=null;try{person=(Person)super.clone();}catch (CloneNotSupportedException ex){ex.printStackTrace();}return person;} }

來寫一個(gè)測試類,對其clone方法進(jìn)行測試:

public class TestPerson {public static void main(String[] args) throws CloneNotSupportedException {Person person = new Person();person.setAge(22);person.setName("Java后端技術(shù)棧");//克隆一個(gè)對象Person clone = (Person) person.clone();//對person的age重新賦值為25person.setAge(25);//對person的age重新賦值為25person.setName("咖啡");System.out.print(clone.getAge()+","+clone.getName());} }

運(yùn)行后將輸出什么呢?先猜想一下。

具體運(yùn)行結(jié)果如下:

22,Java后端技術(shù)棧

是不是覺得很神奇呢?為什么沒有變化呢?

原始數(shù)據(jù)類型會被拷貝,如果從原始數(shù)據(jù)類型考慮,因?yàn)閍ge是int類型,int是原始數(shù)據(jù)類型,所以上述場景沒變,那也就無話可說,但是String并不是原始數(shù)據(jù)類型,那又是為什么呢?因?yàn)镾tring是一個(gè)特殊類型,因?yàn)檫@種場景下Java希望String也看成原始類型。所以String并沒有clone方法。

String aa="aa"; aa.clone();

String定義

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {//...省略其他 }

這段代碼編譯通不過,提示無法訪問,為什么呢?請看Object源碼中對clone方法的定義

protected native Object clone() throws CloneNotSupportedException;

String處理機(jī)制比較特殊,通過字符串池在需要的時(shí)候再內(nèi)存中創(chuàng)建新的字符串,以后大家就在使用(clone)的時(shí)候就直接把String當(dāng)做原始數(shù)據(jù)類型就行了。

深拷貝

淺拷貝是有風(fēng)險(xiǎn)的,那么如何才能深拷貝呢?我們對前面的Mybaby程序進(jìn)行修改一下就成了深拷貝了;

import java.util.ArrayList;/*** @author tianweichang* @date 2019/7/13*/ public class MyBaby implements Cloneable {/*** 私有變量*/private ArrayList<String> list = new ArrayList<>();@SuppressWarnings("unchecked")@Overrideprotected Object clone() throws CloneNotSupportedException {MyBaby myBaby = null;try {myBaby = (MyBaby) super.clone();//增加了一個(gè)list.clone();this.list = (ArrayList<String>) this.list.clone();} catch (CloneNotSupportedException ex) {ex.printStackTrace();}return myBaby;}/*** 給List設(shè)置** @param value 值*/public void setValue(String value) {this.list.add(value);}/*** 獲取list** @return list*/public ArrayList<String> getValue() {return this.list;} }

再次運(yùn)行TestMyBaby,結(jié)果:

[Java后端技術(shù)棧]

改短代碼就實(shí)現(xiàn)了完全的拷貝,兩個(gè)對象引用指向的就不再是同一個(gè)地址了。相互之間沒有什么關(guān)系了,你修改你的,我修改我的,完全不會有什么安全問題。這就是深拷貝。

深拷貝還有一種實(shí)現(xiàn)方式:通過寫自己的二進(jìn)制流來操作對象,然后實(shí)現(xiàn)對象的深拷貝。

建議:

深拷貝和淺拷貝不要混合使用,特別是在涉及到類的繼承時(shí)候,父類中有多個(gè)引用的情況下就會非常復(fù)雜,建議方案是深拷貝和淺拷貝分開實(shí)現(xiàn)。

clone與final兩個(gè)冤家

對象的clone與對象內(nèi)的final關(guān)鍵字是有沖突,前者是要重新賦值,后者是賦值了就不能變了。

咱們繼續(xù)幫上忙的代碼進(jìn)行改造:

public class MyBaby implements Cloneable {/*** 私有變量*/private final ArrayList<String> list = new ArrayList<>();@SuppressWarnings("unchecked")@Overrideprotected Object clone() throws CloneNotSupportedException {MyBaby myBaby = null;try {myBaby = (MyBaby) super.clone();//下面的this.list編譯通不過,提示不能給final修飾的變量重新賦值this.list = (ArrayList<String>) this.list.clone();} catch (CloneNotSupportedException ex) {ex.printStackTrace();}return myBaby;}/*** 給List設(shè)置** @param value 值*/public void setValue(String value) {this.list.add(value);}/*** 獲取list** @return list*/public ArrayList<String> getValue() {return this.list;} }

所以請注意,要使用clone方法的時(shí)候,類的成員變量上不要加final修飾。

總結(jié)

以上是生活随笔為你收集整理的java list拷贝_深入了解浅拷贝与深拷贝的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。