Java 深拷貝與淺拷貝的分析
在正式的進(jìn)入主題之前,我們先來(lái)了解下深拷貝和前拷貝的概念:
淺拷貝:
會(huì)創(chuàng)建一個(gè)新對(duì)象,這個(gè)對(duì)象有著原始對(duì)象屬性值的一份精確拷貝,如果屬性是基本類(lèi)型,拷貝的是基本類(lèi)型的值;如果屬性是內(nèi)存地址,拷貝的就是內(nèi)存地址,因此如果一個(gè)對(duì)象改變了這個(gè)地址就會(huì)影響到另一個(gè)對(duì)象;
深拷貝:
不僅要復(fù)制對(duì)象的所有非引用成員變量值,還要為引用類(lèi)型的成員變量創(chuàng)建新的實(shí)例,并且初始化為形式參數(shù)實(shí)例值;
了解完概念之后,我們來(lái)測(cè)試下普通的對(duì)象賦值操作屬于深拷貝還是淺拷貝:
測(cè)試代碼:
public class DepthCopy {
public static void main(String[] args) {
Copy first = new Copy("hzw", 24);
Copy second = first;
second.name = "shanxi";
System.out.println(first.name);//輸出shanxi
}
}
class Copy
{
public String name;
public int age;
public Copy(String name,int age) {
this.name = name;
this.age = age;
}
}
可以發(fā)現(xiàn),在second將name屬性值修改為shanxi之后,first的name屬性值也變成了shanxi,這點(diǎn)就可以看出普通的對(duì)象賦值屬于淺拷貝;
明白了對(duì)象之間賦值是淺拷貝之后,接下來(lái)我們來(lái)看看克隆到底是深拷貝還是淺拷貝,測(cè)試代碼是讓上面的Copy對(duì)象實(shí)現(xiàn)Cloneable接口里面的clone方法:
public class DepthCopy {
public static void main(String[] args) {
Copy first = new Copy("hzw", 24);
Copy second = null;
try {
second = (Copy) first.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
second.name = "shanxi";
System.out.println(first.name);//輸出: hzw
System.out.println(first);//輸出: com.hzw.day33.Copy@7f39ebdb
System.out.println(second);//輸出: com.hzw.day33.Copy@33abb81e
}
}
class Copy implements Cloneable
{
public String name;
public int age;
public Copy(String name,int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
可以看出原先創(chuàng)建出的對(duì)象first和克隆創(chuàng)建出的對(duì)象second是兩個(gè)實(shí)例,因此對(duì)于second中name屬性的修改并不會(huì)影響first中的name屬性;但是,我們并不能單純的認(rèn)為克隆就是深拷貝的,比如下面這個(gè)例子:
public class DepthCopy {
public static void main(String[] args) {
Student student = new Student(95);
Copy first = new Copy("hzw", 24,student);
Copy second = null;
try {
second = (Copy) first.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
second.name = "shanxi";
second.student.score = 60;
System.out.println(first == second);//false
System.out.println(first.student == second.student);//true
System.out.println(first.student.score);//60
}
}
class Copy implements Cloneable
{
public String name;
public int age;
public Student student;
public Copy(String name,int age,Student student) {
this.name = name;
this.age = age;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student
{
public int score;
public Student(int score) {
this.score = score;
}
}
看到?jīng)]有呢?我們通過(guò)克隆的方式創(chuàng)建了second,很明顯發(fā)現(xiàn)first和second是兩個(gè)實(shí)例,因?yàn)閒irst == second輸出為false,但是first和second里面的student對(duì)象卻是一樣的,通過(guò)second修改了student的score值之后,first里面student的score也發(fā)生了改變,這也就是說(shuō)first和second里面的student是相同的,這也就說(shuō)明了克隆是淺拷貝的,我們要想實(shí)現(xiàn)克隆的深拷貝,必須讓Copy對(duì)象里面的Student對(duì)象也要實(shí)現(xiàn)Cloneable接口里面的clone方法,并且在Copy里面的克隆方法返回Student的一個(gè)克隆即可,這樣就可以保證Student的唯一啦,修改之后的代碼如下:
public class DepthCopy {
public static void main(String[] args) {
Student student = new Student(95);
Copy first = new Copy("hzw", 24,student);
Copy second = null;
try {
second = (Copy) first.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
second.name = "shanxi";
second.student.score = 60;
System.out.println(first == second);//false
System.out.println(first.student == second.student);//false
System.out.println(first.student.score);//95
System.out.println(second.student.score);//60
}
}
class Copy implements Cloneable
{
public String name;
public int age;
public Student student;
public Copy(String name,int age,Student student) {
this.name = name;
this.age = age;
this.student = student;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Copy copy = (Copy)super.clone();
copy.student = (Student) student.clone();
return copy;
}
}
class Student implements Cloneable
{
public int score;
public Student(int score) {
this.score = score;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
可以看到此時(shí)first和second和first.student和second.student都不是相同的,因此我們修改second的student的score之后并沒(méi)有影響到first里的student的score值,達(dá)到了深拷貝的目的;
但是,仔細(xì)一想問(wèn)題就出來(lái)了,假如我們上面例子的Student類(lèi)中也存在引用類(lèi)型的屬性,比如College類(lèi),那么我們必須讓College類(lèi)實(shí)現(xiàn)Cloneable接口,然后在Student類(lèi)里面的clone方法里面調(diào)用College類(lèi)的clone方法,在Copy類(lèi)的clone方法中調(diào)用Student類(lèi)的clone方法,發(fā)現(xiàn)沒(méi)有了,這個(gè)過(guò)程好復(fù)雜,必須讓類(lèi)中的有關(guān)引用類(lèi)型全部實(shí)現(xiàn)Cloneable接口,感覺(jué)好麻煩是不是,好的,接下來(lái)就該牛人登場(chǎng)了;
解決深拷貝問(wèn)題最好的方式就是采用序列化方式,這樣各種類(lèi)均不用實(shí)現(xiàn)Cloneable接口的,直接序列化反序列化就可以啦,我們來(lái)見(jiàn)識(shí)下吧。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class DepthCopy {
public static void main(String[] args) {
College school = new College("nongda");
Student student = new Student(95, school);
Copy copy = new Copy("hzw",23, student);
Copy another = null;//表示反序列化出來(lái)的類(lèi)實(shí)例
//進(jìn)行序列化操作
try {
FileOutputStream fos = new FileOutputStream(new File("d:/copy.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(copy);
} catch (Exception e) {
e.printStackTrace();
}
//進(jìn)行反序列化操作
FileInputStream fis;
try {
fis = new FileInputStream(new File("d:/copy.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
another = (Copy) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(copy == another);//false
System.out.println(copy.student == another.student);//false
System.out.println(copy.student.school == another.student.school);//false
another.student.school.schoolName = "wuda";
System.out.println(copy.student.school.schoolName);//nongda
}
}
class Copy implements Serializable
{
public String name;
public int age;
public Student student;
public Copy(String name,int age,Student student) {
this.name = name;
this.age = age;
this.student = student;
}
}
class Student implements Serializable
{
public int score;
public College school;
public Student(int score,College school) {
this.score = score;
this.school = school;
}
}
class College implements Serializable
{
public String schoolName;
public College(String schoolName) {
this.schoolName = schoolName;
}
}
從輸出就可以看出來(lái),反序列化之后生成的對(duì)象完全就是對(duì)原對(duì)象的一份拷貝,除了屬性值相同之外并不和原對(duì)象有任何關(guān)系,因此當(dāng)我們修改反序列化生成對(duì)象的schoolName為"wuda"的時(shí)候并沒(méi)有修改原來(lái)實(shí)例的schoolName值,還是輸出"nongda",因此達(dá)到了真正的深拷貝效果,但是要想實(shí)現(xiàn)序列化,所有的有關(guān)類(lèi)都必須實(shí)現(xiàn)Serializable接口,這總也比既實(shí)現(xiàn)Cloneable接口又實(shí)現(xiàn)clone方法更方便吧。
以上就是對(duì)Java 深拷貝和淺拷貝的詳細(xì)講解,有需要的可以參考下。
相關(guān)文章
spring-boot2.7.8添加swagger的案例詳解
這篇文章主要介紹了spring-boot2.7.8添加swagger的案例詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01
spring AOP代理執(zhí)行@EnableAspectJAutoProxy的exposeProxy屬性詳解
這篇文章主要為大家介紹了spring AOP代理執(zhí)行@EnableAspectJAutoProxy的exposeProxy屬性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
SpringMVC 異常處理機(jī)制與自定義異常處理方式
這篇文章主要介紹了SpringMVC 異常處理機(jī)制與自定義異常處理方式,具有很好的開(kāi)車(chē)價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Java:com.netflix.client.ClientException錯(cuò)誤解決
本文主要介紹了Java:com.netflix.client.ClientException錯(cuò)誤解決,主要是指出客戶(hù)端?module-sso?試圖通過(guò)負(fù)載均衡器訪問(wèn)服務(wù)時(shí),負(fù)載均衡器沒(méi)有找到可用的服務(wù)器來(lái)處理請(qǐng)求,下面就來(lái)介紹一下解決方法2024-08-08
SpringMVC中請(qǐng)求參數(shù)的獲取方式
這篇文章主要為大家介紹了SpringMVC中請(qǐng)求參數(shù)的獲取方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05

