Java SE Cloneable接口和深/淺拷貝

Java為我們提供了各種各樣功能的接口,Clonable接口就是其中之一。

它通常配合Object類的 clone方法使用。這個方法可以為我們創建一個對象的拷貝,即復制一個對象。在進入本文的主要內容之前,先來對訪問限定符 protected進行一個解剖。

1.再談 protected

我們知道,在一個類中被 protected修飾的字段和方法,它們的訪問權限如下:

  1. 本類內部:本類內部的所有成員都能夠訪問被protected修飾的元素。
  2. 同一個包內:同一包中的其他類,不管是子類還是非子類,都可以訪問該元素。
  3. 不同包的子類:若子類與父類不在同一個包中,子類可以通過繼承或者創建子類對象的方式來訪問父類的?protected成員。

這里重點討論第三點,當一個類中有被 protected修飾的字段和方法,并且它的子類與它在不同包時,那么子類只能在自身內部去調用父類的 protected成員,不能在外部調用。

舉個例子如下:

//父類 Animal位于Demo1包
package Demo1;public class Animal {private String name;private int age;public Animal(String name, int age) {this.name = name;this.age = age;}protected void eat() {System.out.println(this.name + "正在吃飯....");}
}//子類 Dog位于Demo2包package Demo2;import Demo1.Animal;public class Dog extends Animal {public Dog(String name, int age) {super(name, age);}public void eat1() {super.eat();}
}//測試類 Testimport Demo2.Dog;public class Test {public static void main(String[] args) {Dog dog = new Dog("大黃",1);//dog.eat();發生錯誤:java: eat() 在 Demo1.Animal 中是 protected 訪問控制dog.eat1();//正常運行,運行結果:大黃正在吃飯....}
}

我們可以發現,確實如此,eat()方法是父類Animal中被protected修飾的方法,不能在子類Dog類外部直接調用,像這樣:dog.eat(),只能在子類內部調用,只有在子類內部的方法中去調用父類Animal,像這樣:eat1()中的super.eat();這樣Dog類外部使用 dog.eat1(),就可以間接調用父類Animal的eat()方法。

當然,我們也可以對在子類中對父類的protected方法進行重寫,這樣在子類外部就能直接調用了。這里就不再舉例了。

?2. 如何進行克隆

要使用Object類的clone方法,我們通常需要做以下幾件事:

  1. 在要克隆的類中重寫Object類的 clone方法
  2. 實現 Cloneable接口
  3. 事先處理異常情況

現在有一個Person類,我們打算克隆它的一個對象。

1.在要克隆的類中重寫Object類的 clone方法

package Demo3;public class Person {public String name;public Person(String name) {this.name = name;}//重寫 clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

注:可以通過快捷鍵“Alt + Insert”快速重寫 clone方法。?

?2.實現 Cloneable接口

package Demo3;public class Person implements Cloneable{private String name;public Person(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

注:當我們進入 Cloneable接口時,會發現它是一個空接口,它的作用是 宣布實現了這個接口的類可以克隆

?3.處理異常情況,我們創建一個測試類,在測試類中去克隆一個Person對象。

package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();}
}
//這里,需要做兩件事
//1.把重寫的 clone方法中的:throws CloneNotSupportedException 這個部分復制到 mian方法后面。
//2.因為重寫的 clone方法的返回值為 Object,因此這里需要強轉。

讓我們測試一下:

package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();System.out.println("person1的名字:" + person1.name);System.out.println("person2的名字:" + person2.name);}
}//運行結果
person1的名字:小明
person2的名字:小明

顯然,確實做到克隆一份person1。

那么它的過程是怎么樣的呢?我們通過圖畫的方式來說明:

?

?3. 淺拷貝

知道了如何進行克隆,接著我們來討論一下什么是淺拷貝,顧名思義可以理解為:淺度的克隆。

舉個例子:我們在創建一個新的類 Money,在 Person類中創建一個 money對象,并將它作為一個字段。

//Money類package Demo3;public class Money {public int m = 10;
}//Person類package Demo3;public class Person implements Cloneable{public String name;public Money money = new Money();public Person(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

現在,我們再去克隆,接著修改person2對象當中的money對象的m,看看結果如何。

package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();System.out.println("person1的m:" + person1.money.m);System.out.println("修改前的person2的m:" + person2.money.m);person2.money.m = 100;System.out.println("==================");System.out.println("person1的m:" + person1.money.m);System.out.println("修改后的person2的m:" + person2.money.m);}
}//運行結果
person1的m:10
修改前的person2的m:10
==================
person1的m:100
修改后的person2的m:100

我們可以發現,person1與person2公用一個 money.m,因為person1和person2的money引用指向同一個對象。對于它的過程,我們依舊可以用畫圖的方式表示:

4. 深拷貝?

?在淺拷貝的例子里,我們會發現 person1和 person2公用一個 money.m,原因是它們的 money引用中儲存的是同一個money對象的地址。現在我們不想它們公用一個 money.m,那么我們該怎么做呢?這里我們就需要實現深拷貝。

想要實現深拷貝,要做兩件事:

  1. 要進行拷貝的類都要重寫 clone方法和實現 Cloneable接口,例如作為 Person類字段的 Money類也要重寫clone方法。
  2. 修改主要的類的重寫的clone方法,例如 上述例子中的 Person類,它是主要的類,因為Money類是它的字段。

?在Money類中重寫 clone方法和實現 Cloneable接口。


package Demo3;public class Money implements Cloneable{public int m = 10;//重寫 clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

修改 Person類中的重寫的 clone方法。

package Demo3;public class Person implements Cloneable{public String name;public Money money = new Money();public Person(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {Person temp = (Person) super.clone();   temp.money = (Money) this.money.clone();return temp;}
}//說明:這里先創建一個 Person類型的變量 temp,用來接收克隆的person對象,
//接著讓temp.money去接收克隆的money對象,最后返回 temp變量。

現在,我們再去克隆,接著修改person2對象當中的money對象的m,看看結果如何。

package Demo3;public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person("小明");Person person2 = (Person) person1.clone();System.out.println("person1的m:" + person1.money.m);System.out.println("修改前的person2的m:" + person2.money.m);person2.money.m = 100;System.out.println("==================");System.out.println("person1的m:" + person1.money.m);System.out.println("修改后的person2的m:" + person2.money.m);}
}//運行結果
person1的m:10
修改前的person2的m:10
==================
person1的m:10
修改后的person2的m:100

顯然,此時修改 person2的 money.m也不會影響person1,因為person2的 money對象是克隆person1的,不再是與person1公用了。對于它的過程,我們用圖畫表示如下:

到此,本文完。本文若有不對的地方,還請各位看官指出,多謝!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/907263.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/907263.shtml
英文地址,請注明出處:http://en.pswp.cn/news/907263.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python學習(3) ----- Python的函數定義及其使用

Python 中函數是組織好的、可重復使用的代碼塊,用于實現單一或相關聯的功能。下面是函數定義和使用的完整說明: 📌 一、函數定義語法 def 函數名(參數1, 參數2默認值, *args, **kwargs):"""函數說明文檔"""函…

vue2使用el-tree實現兩棵樹間節點的拖拽復制

原文鏈接&#xff1a;兩棵el-tree的節點跨樹拖拽實現 參照這篇文章&#xff0c;把它做成組件&#xff0c;新增左側樹&#xff08;可拖出&#xff09;被拖節點變灰提示&#xff1b; 拖拽中&#xff1a; 拖拽后&#xff1a; TreeDragComponent.vue <template><!-- …

智變與重構:AI 賦能基礎教育教學的范式轉型研究報告

一、研究背景與核心價值 &#xff08;一&#xff09;技術驅動下的教育轉型浪潮 在全球數字化轉型加速的背景下&#xff0c;人工智能作為核心技術力量&#xff0c;正重塑基礎教育生態。據《人工智能賦能未來教育研究報告》指出&#xff0c;我國教育數字化戰略行動已推動超 70…

Go語言中Print、Printf和Println的區別及使用場景詳解

在Go語言的fmt包中&#xff0c;Print、Printf和Println是三個基礎但功能各異的輸出函數。本文將從多個維度進行詳細對比分析&#xff0c;并給出具體的使用建議。 1. 核心區別深度解析 1.1. 函數簽名與基本行為 func Print(a ...interface{}) (n int, err error) func Printf…

高端制造行業 VMware 替代案例合集:10+ 頭部新能源、汽車、半導體制造商以國產虛擬化支持 MES、PLM 等核心應用系統

在“中國制造 2025”政策的推動下&#xff0c;國內的新能源、汽車制造、半導體、高端裝備等高端制造產業迎來了蓬勃發展&#xff0c;成為全球制造業版圖中舉足輕重的力量。訂單數量的激增與國產化轉型的趨勢&#xff0c;也為高端制造企業的 IT 基礎設施帶來了新的挑戰&#xff…

Spring Ai | 從零帶你一起走進AI項目(中英)

目錄 Thinking Study question pox.xml Maven Gradle Configure API Key Use the AI Client Question Thinking 讓數據變得更加貼近用戶的想法 Study question null pox.xml 添加依賴 Maven <dependencies><dependency><groupId>org.springfram…

LiveGBS作為下級平臺GB28181國標級聯2016|2022對接海康大華宇視華為政務公安內網等GB28181國標平臺查看級聯狀態及會話

LiveGBS作為下級平臺GB28181國標級聯2016|2022對接海康大華宇視華為政務公安內網等GB28181國標平臺查看級聯狀態及會話 1、GB/T28181級聯概述2、搭建GB28181國標流媒體平臺3、獲取上級平臺接入信息3.1、向下級提供信息3.2、上級國標平臺添加下級域3.3、接入LiveGBS示例 4、配置…

卸載 Office PLUS

Office PLUS作為微軟官方推出的智能辦公提效工具&#xff0c;自2015年問世以來&#xff0c;憑借其豐富的模板資源和便捷的智能功能&#xff0c;迅速贏得了廣大職場人士和學生的青睞。本文將全面介紹Office PLUS的發展歷程、核心功能、可能帶來的使用問題&#xff0c;以及如何徹…

影響沉金價格的因素如何體現在多層電路板制造上?

隨著科技的不斷發展&#xff0c;電子產品越來越普及&#xff0c;對電路板的需求也越來越大。多層電路板作為電子產品的核心部件&#xff0c;其性能和質量直接影響到整個產品的穩定性和可靠性。在多層電路板的生產過程中&#xff0c;沉金工藝是一種常用的表面處理方法&#xff0…

擴展摩爾投票法:找出出現次數超過 n/3 的元素

文章目錄 問題描述關鍵洞察算法原理Java 實現算法演示投票階段驗證階段 復雜度分析算法關鍵點通用化公式實際應用場景邊界情況處理總結 標簽&#xff1a;LeetCode 169, 摩爾投票法, 多數元素, 算法擴展, 數組處理 在解決多數元素問題時&#xff0c;我們學習了經典的摩爾投票法處…

Git:現代軟件開發的基石——原理、實踐與行業智慧·優雅草卓伊凡

Git&#xff1a;現代軟件開發的基石——原理、實踐與行業智慧優雅草卓伊凡 一、Git的本質與核心原理 1. 技術定義 Git是一個分布式版本控制系統&#xff08;DVCS&#xff09;&#xff0c;由Linus Torvalds在2005年為管理Linux內核開發而創建。其核心是通過快照&#xff08;Sna…

程序人生-hello’s P2P

計算機系統 大作業 題 目 程序人生-hello’s P2P 專 業 計算機與電子通信類 學   號 2023111990 班   級 23L0514 學 生 袁騁 指 導 教 師 史…

Java設計模式之設計原則

Java設計模式 Java設計模式主要原則是開閉原則&#xff0c;即對擴展開放&#xff0c;對修改關閉。由此衍生出5大原則&#xff1a;單一職責原則&#xff0c;里式替換原則&#xff0c;迪米特原則&#xff0c;接口隔離職責&#xff0c;依賴倒置原則。1、開閉原則 開閉原則&#x…

使用 ssld 提取CMS 簽名并重簽名

拿SpringBoard的cms簽名和entitlements.xml&#xff0c;對tihook.dylib進行重簽名 工具來源&#xff1a;https://github.com/eksenior/ssld

WebFuture:測試郵件發送失敗

問題描述&#xff1a;測試郵件發送失敗 問題分析&#xff1a; 查看報錯是模擬發送郵件請將systemsettings.json中的EnabledMail設為false&#xff01; 解決方案&#xff1a; 網站根目錄找到Configuration&#xff0c;如下圖所示&#xff0c;將systemsettings.json中的Enabled…

LiveNVR 直播流拉轉:Onvif/RTSP/RTMP/FLV/HLS 支持海康宇視天地 SDK 接入-視頻廣場頁面集成與視頻播放說明

LiveNVR直播流拉轉&#xff1a;Onvif/RTSP/RTMP/FLV/HLS支持海康宇視天地SDK接入-視頻廣場頁面集成與視頻播放說明 一、視頻頁面集成1.1 關閉接口鑒權1.2 視頻廣場頁面集成1.2.1 隱藏菜單欄1.2.2 隱藏播放頁面分享鏈接 1.3 其它頁面集成 二、播放分享頁面集成2.1 獲取 iframe 代…

12. CSS 布局與樣式技巧

在前端開發中&#xff0c;CSS 是控制頁面樣式和布局的核心技術。本文總結了 CSS 布局中的關鍵概念和實用技巧&#xff0c;包括 overflow 屬性、背景圖片處理、精靈圖技術、display 屬性、浮動布局以及清除浮動的方法。 一、overflow 屬性詳解 overflow 屬性用于控制當元素內容…

OpenCV---Canny邊緣檢測

一、基本概念與核心作用 Canny邊緣檢測是計算機視覺中最經典的邊緣檢測算法之一&#xff0c;由John Canny于1986年提出。其核心目標是在噪聲圖像中提取精確、單像素寬、連續的邊緣&#xff0c;廣泛應用于&#xff1a; 目標檢測預處理&#xff08;如Robomaster中燈條、裝甲板的…

提效-點擊跳轉到源碼

1、localhost項目 例如【鯨島】這個中臺項目啟動地址是localhost。 使用chrome中的【click-to-react-component 】擴展&#xff0c; alt 鼠標左鍵 選擇dom后跳轉到對應文件。 click-to-react-component的原理&#xff08;來自ai&#xff09; click-to-react-component 的工作…

FeignClient發送https請求時的證書驗證原理分析

背景 微服務之間存在調用關系&#xff0c;且部署為 SSL 協議時&#xff0c;Feignt 請求報異常&#xff1a; Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find vali…