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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

实现函数克隆_哪个更好的选择:克隆或复制构造函数?

發(fā)布時間:2023/12/3 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 实现函数克隆_哪个更好的选择:克隆或复制构造函数? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

實現(xiàn)函數(shù)克隆

這就是我開始撰寫本文的方式。 我已經(jīng)讀過很多次這樣的聲明: “當(dāng)對象引用可變的最終字段時,克隆變得很困難。” 每次我在Google上搜索它時,都要了解它的確切含義,并且在此過程中也忘了它。 因此以為我會將其寫在博客上,以便作為我的直接參考。

克隆對象(我可以從我的研究生課程的OOP課程中回憶到)正在創(chuàng)建對象的類似副本,該副本基本上應(yīng)符合以下規(guī)則:

  • x.clone()!= x
  • x.clone()。getClass()== x.getClass()
  • x.clone()。equals(x)
  • 注意,在所有情況下都必須始終滿足條件(1)。 盡管條件(2)和(3)并不是絕對要求,但最好以這樣的方式設(shè)計克隆方法,使其保持良好狀態(tài)。 在繼續(xù)討論之前,這是Object類中clone方法的方法簽名:

    protected native Object clone() throws CloneNotSupportedException;

    因此,正如您注意到protected修飾符一樣,我們不可能直接在任何對象上調(diào)用clone()方法。 我們必須重寫此方法作為公共方法,并在我們的類中提供對其的實現(xiàn)才能訪問它。 如果不需要特定的實現(xiàn),我們可以返回super.clone()。 由于在Java 5之后可以進行協(xié)變返回,因此我們可以修改clone的返回值以返回類的對象。 因此,如果我們正在編寫員工類,則這是clone()方法的方式:

    @Override public Employee clone() throws CloneNotSupportedException {return (Employee) super.clone(); }

    但是請注意,Object類中的clone方法檢查我們的類是否實現(xiàn)Cloneable接口。 如果未實現(xiàn),則拋出CloneNotSupportedException。 否則,它將創(chuàng)建一個新副本。 但是請注意,克隆方法從不調(diào)用構(gòu)造函數(shù)來創(chuàng)建對象的副本。 因此,如果您想通過增加構(gòu)造函數(shù)內(nèi)部的靜態(tài)計數(shù)器來跟蹤為類創(chuàng)建的實例數(shù)量,則此方法將不會起作用,因為永遠不會調(diào)用構(gòu)造函數(shù)。 相反,clone方法從對象內(nèi)存中逐字段復(fù)制實例屬性,然后將其返回給調(diào)用方。 因此,如果類必須提供一個克隆其選項而不導(dǎo)致CloneNotSupportedException的類,則必須實現(xiàn)一個標(biāo)記接口Cloneable。 但是請注意,調(diào)用clone()的代碼應(yīng)處理此異常。 否則會導(dǎo)致編譯器錯誤。 是的,這是一個痛點,對此受到批評。

    現(xiàn)在讓我們舉一個例子: 案例(1)

    public class Employee implements Cloneable{private String name;private String identifier;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getIdentifier() {return identifier;}public void setIdentifier(String identifier) {this.identifier = identifier;}@Overridepublic Employee clone() throws CloneNotSupportedException {return (Employee)super.clone();}public void print() {System.out.println(Objects.toStringHelper(this).add("name:", name).add("id:", identifier).toString());}public static void main(String[] args) throws CloneNotSupportedException {Employee employee1 = new Employee();employee1.setName("Ram");employee1.setIdentifier("1");System.out.println("1: "+employee1);employee1.print();Employee employee2 = employee1.clone();System.out.println("2: "+employee2);employee2.print();} }

    這是此的輸出:

    1: com.pramati.test.Employee@19821f Employee{name:=Ram, id:=1} 2: com.pramati.test.Employee@de6ced Employee{name:=Ram, id:=1}

    從上面的示例可以看出,clone()方法創(chuàng)建了一個新Employee,其值是從現(xiàn)有對象中復(fù)制的。 這很簡單,并且可以正常工作,因為Employee類中沒有對象引用。 讓我們這樣修改類: 案例(2):

    public class PayPackDetails{private double basicSalary = 500000d;private double incentive = 50000d;public double getSalary() {return getBasicSalary()+getIncentive();}public double getBasicSalary() {return basicSalary;}public double getIncentive() {return incentive;}public void setBasicSalary(double basicSalary) {this.basicSalary = basicSalary;}public void setIncentive(double incentive) {this.incentive = incentive;} }public class Employee implements Cloneable {private String name;private String identifier;private PayPackDetails packDetails;public Employee(String name, String identifier, PayPackDetails packDetails) {this.name = name;this.identifier = identifier;this.packDetails = packDetails;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getIdentifier() {return identifier;}public void setIdentifier(String identifier) {this.identifier = identifier;}public PayPackDetails getPackDetails() {return packDetails;}@Overridepublic Employee clone() throws CloneNotSupportedException {return (Employee)super.clone();}public void print() {System.out.println(Objects.toStringHelper(this).add("name:", name).add("id:", identifier).add("package:", packDetails.getSalary()).toString());}public static void main(String[] args) throws CloneNotSupportedException {Employee employee1 = new Employee("Ram","1",new PayPackDetails());System.out.println("1: "+employee1);employee1.print();Employee employee2 = employee1.clone();System.out.println("2: "+employee2);employee2.print();} }

    在運行main方法時,我們將得到以下結(jié)果:

    1: com.pramati.clone.Employee@addbf1 Employee{name:=Ram, id:=1, package:=550000.0} 2: com.pramati.clone.Employee@de6ced Employee{name:=Ram, id:=1, package:=550000.0}

    這可以。 現(xiàn)在說,我們修改了我們的主要方法,如下: 案例(3):

    public static void main(String[] args) throws CloneNotSupportedException {Employee employee1 = new Employee("Ram","1",new PayPackDetails());Employee employee2 = employee1.clone();employee2.setName("Krish"); employee2.setIdentifier("2");employee2.getPackDetails().setBasicSalary(700000d);employee1.print();employee2.print(); }

    現(xiàn)在您認為employee1的薪水是多少? 隨著我們增加了克隆員工的薪水,我們自然希望為他增加薪水。 但是這里出乎意料的轉(zhuǎn)折是,employee1的薪水也增加了。 這是輸出或此:

    Employee{name:=Ram, id:=1, package:=750000.0} Employee{name:=Krish, id:=2, package:=750000.0}

    請注意,當(dāng)我們克隆對象時,不會調(diào)用構(gòu)造函數(shù)。 寧愿對原始對象的地址位置中存在的所有成員變量進行逐域復(fù)制。 現(xiàn)在,當(dāng)有對象引用時,該引用將被復(fù)制,而不是原始對象。 因此,原始對象和克隆對象都指向同一成員對象。 因此,對一個對象所做的更改將自動對另一對象可見。 那么如何解決這個問題呢?

    最簡單的解決方案是也為PayPackDetails實現(xiàn)克隆方法,并從Employee的克隆方法中調(diào)用它。 情況(4):

    @Override public Employee clone() throws CloneNotSupportedException {Employee employee = (Employee)super.clone();employee.packDetails = packDetails.clone();return employee; }

    現(xiàn)在運行main()方法,它將按預(yù)期給出正確的結(jié)果:

    Employee{name:=Ram, id:=1, package:=550000.0} Employee{name:=Krish, id:=2, package:=750000.0}

    但是,如果PayPackDetails由其他對象引用組成,則我們也必須重寫該對象的克隆方法,并在PayPackDetails內(nèi)部調(diào)用其克隆方法。 同樣,當(dāng)我們在PayPackDetails中組成新對象時,除了在新組成的對象中實現(xiàn)clone()方法外,我們還必須在PayPackDetails中修改clone方法。 組合對象類還應(yīng)該實現(xiàn)Cloneable接口。 與往常一樣,我們還必須處理CloneNotSupportedException。

    現(xiàn)在考慮將PayPackDetails聲明為final的另一種情況,這會使情況更加糟糕: 情況(5):

    public class Employee implements Cloneable {private String name;private String identifier;private final PayPackDetails packDetails;// -- Rest of the methods }

    由于該字段被聲明為final,因此我們無法在clone方法中為其分配新值,因為該字段被聲明為final。 那么如何處理呢? 解決方案如下:使用復(fù)制構(gòu)造函數(shù)并從克隆中返回新實例。

    public class Employee implements Cloneable {private String name;private String identifier;private final PayPackDetails packDetails;public Employee(String name, String identifier, PayPackDetails packDetails) {this.name = name;this.identifier = identifier;this.packDetails = packDetails;}protected Employee(Employee emp) throws CloneNotSupportedException{name = emp.name;identifier = emp.identifier;packDetails = emp.packDetails.clone();}@Overridepublic Employee clone() throws CloneNotSupportedException {return new Employee(this);}public void print() {System.out.println(Objects.toStringHelper(this).add("name:", name).add("id:", identifier).add("package:", packDetails.getSalary()).toString());} }

    請注意,復(fù)制構(gòu)造函數(shù)訪問修飾符受到保護。 現(xiàn)在問題來了:為什么我們也不能將復(fù)制構(gòu)造函數(shù)用于PayPackDetails而不是克隆方法? 答案是:是的,我們可以使用它。 情況(6):

    public class PayPackDetails {private double basicSalary = 500000d;private double incentive = 50000d;public PayPackDetails(PayPackDetails details){basicSalary = details.getBasicSalary();incentive = details.getIncentive();}public static void main(String[] args) {Employee employee1 = new Employee("Ram","1",new PayPackDetails());employee1.print();Employee employee2 = new Employee(employee1);employee2.print();} } public class Employee {private String name;private String identifier;private final PayPackDetails packDetails;protected Employee(Employee emp) {name = emp.name;identifier = emp.identifier;packDetails = new PayPackDetails(emp.packDetails);}// .. Other methods}

    到目前為止,這是最好的情況,這是該程序的輸出:

    Employee{name:=Ram, id:=1, package:=550000.0} Employee{name:=Ram, id:=1, package:=550000.0}

    實際上,這是最好的方法,因為它解決了有缺陷的克隆方法的許多問題:

    1.沒有一個類必須實現(xiàn)標(biāo)記接口Cloneable
    2.由于不需要克隆,因此無需捕獲CloneNotSupportedException
    3.由于不需要克隆,因此無需在調(diào)用super.clone()時對對象進行類型轉(zhuǎn)換。

    但是問題來了:假設(shè)您有一個PayPackDetails的子類。 案例(7):

    public class AdvancedPayPackDetails extends PayPackDetails {private double flexiblePayPercent = 10d;public AdvancedPayPackDetails(AdvancedPayPackDetails details) {super(details);flexiblePayPercent = details.getFlexiblePayPercentage();}@Overridepublic double getSalary() {return super.getSalary()+(getBasicSalary()*getFlexiblePayPercentage()/100);}public double getFlexiblePayPercentage() {return flexiblePayPercent;}public void setFlexiblePayPercent(double flexiblePayPercent) {this.flexiblePayPercent = flexiblePayPercent;}public static void main(String[] args) throws CloneNotSupportedException {Employee employee1 = new Employee("Ram","1",new AdvancedPayPackDetails());employee1.print();Employee employee2 = employee1.clone();employee2.print();}}

    現(xiàn)在運行main方法,它將為我們提供輸出:

    Employee{name:=Ram, id:=1, package:=600000.0} Employee{name:=Ram, id:=1, package:=550000.0}

    原因很明顯。 Employee的副本構(gòu)造函數(shù)不知道創(chuàng)建的這個新類(AdvancedPayPackDetails)。 實際上,我們可以修改Employee構(gòu)造函數(shù)以包括對PayPackDetails的instanceOf檢查,但這不是正確的方法。 相反,最好回到先前的解決方案,即在使用final字段的情況下使用copy構(gòu)造函數(shù),并對具有繼承層次結(jié)構(gòu)的類使用clone方法(案例(5)的解決方案)。

    結(jié)束語:正如我們在本文中一直看到的那樣,以正確的方式實現(xiàn)克隆方法非常復(fù)雜。 因此最好盡量遠離克隆。 只要復(fù)制的對象沒有任何繼承層次結(jié)構(gòu),最好使用復(fù)制構(gòu)造函數(shù)。

    參考: 哪個更好的選擇:克隆或復(fù)制構(gòu)造函數(shù)? 從我們的JCG合作伙伴 Prasanth Gullapalli在prasanthnath博客上獲得。

    翻譯自: https://www.javacodegeeks.com/2014/01/which-is-better-option-cloning-or-copy-constructors.html

    實現(xiàn)函數(shù)克隆

    總結(jié)

    以上是生活随笔為你收集整理的实现函数克隆_哪个更好的选择:克隆或复制构造函数?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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