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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

可变对象 不可变对象区别_对象应该是不可变的

發(fā)布時(shí)間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 可变对象 不可变对象区别_对象应该是不可变的 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

可變對(duì)象 不可變對(duì)象區(qū)別

在面向?qū)ο蟮木幊讨?#xff0c;如果對(duì)象的狀態(tài)在創(chuàng)建后無(wú)法修改,則它是不可變的 。

在Java中,不可變對(duì)象的一個(gè)??很好的例子是String 。 創(chuàng)建后,我們無(wú)法修改其狀態(tài)。 我們可以要求它創(chuàng)建新的字符串,但是它自己的狀態(tài)永遠(yuǎn)不會(huì)改變。

但是,JDK中沒(méi)有那么多不可變的類。 以類Date為例。 可以使用setTime()修改其狀態(tài)。

我不知道為什么JDK設(shè)計(jì)師決定以不同的方式來(lái)制作這兩個(gè)非常相似的類。 但是,我認(rèn)為可變Date的設(shè)計(jì)有許多缺陷,而不變的String則更多地體現(xiàn)了面向?qū)ο蠓独木瘛?

而且,我認(rèn)為在一個(gè)完美的面向?qū)ο蟮氖澜?/strong>中所有類都應(yīng)該是不變的 。 不幸的是,有時(shí)由于JVM的限制在技術(shù)上是不可能的。 盡管如此,我們應(yīng)該始終追求最佳。

這是支持不變性的參數(shù)的不完整列表:

  • 不變的對(duì)象更易于構(gòu)造,測(cè)試和使用
  • 真正不可變的對(duì)象始終是線程安全的
  • 它們有助于避免時(shí)間耦合
  • 它們的使用無(wú)副作用(無(wú)防御性副本)
  • 避免了身份變異性問(wèn)題
  • 他們總是有失敗原子性
  • 它們更容易緩存
  • 他們防止NULL引用, 這是不好的

讓我們一一討論最重要的論點(diǎn)。

線程安全

第一個(gè)也是最明顯的論點(diǎn)是,不可變對(duì)象是線程安全的。 這意味著多個(gè)線程可以同時(shí)訪問(wèn)同一對(duì)象,而不會(huì)與另一個(gè)線程發(fā)生沖突。

如果沒(méi)有對(duì)象方法可以修改其狀態(tài),那么無(wú)論它們有多少個(gè)以及被調(diào)用的頻率是多少,它們都將在自己的堆棧內(nèi)存空間中工作。

Goetz等。 在非常著名的《 Java Concurrency in Practice》 (強(qiáng)烈推薦)一書中,更詳細(xì)地介紹了不可變對(duì)象的優(yōu)點(diǎn)。

避免時(shí)間耦合

這是時(shí)間耦合的示例(代碼發(fā)出兩個(gè)連續(xù)的HTTP POST請(qǐng)求,其中第二個(gè)包含HTTP正文):

Request request = new Request("http://example.com"); request.method("POST"); String first = request.fetch(); request.body("text=hello"); String second = request.fetch();

此代碼有效。 但是,您必須記住,在配置第二個(gè)請(qǐng)求之前,應(yīng)該先配置第一個(gè)請(qǐng)求。 如果我們決定從腳本中刪除第一個(gè)請(qǐng)求,則將刪除第二行和第三行,并且不會(huì)從編譯器中得到任何錯(cuò)誤:

Request request = new Request("http://example.com"); // request.method("POST"); // String first = request.fetch(); request.body("text=hello"); String second = request.fetch();

現(xiàn)在,該腳本已損壞,盡管它編譯時(shí)沒(méi)有錯(cuò)誤。 這就是時(shí)間耦合的意義所在—代碼中總是有一些程序員必須記住的隱藏信息。 在此示例中,我們必須記住,第一個(gè)請(qǐng)求的配置也用于第二個(gè)請(qǐng)求。

我們必須記住,第二個(gè)請(qǐng)求應(yīng)始終保持在一起,并在第一個(gè)請(qǐng)求之后執(zhí)行。

如果Request類是不可變的,則第一個(gè)代碼片段將一開(kāi)始就無(wú)法使用,并且將被重寫為:

final Request request = new Request(""); String first = request.method("POST").fetch(); String second = request.method("POST").body("text=hello").fetch();

現(xiàn)在,這兩個(gè)請(qǐng)求沒(méi)有耦合。 我們可以安全地刪除第一個(gè),第二個(gè)仍然可以正常工作。 您可能會(huì)指出存在代碼重復(fù)。 是的,我們應(yīng)該擺脫它,然后重新編寫代碼:

final Request request = new Request(""); final Request post = request.method("POST"); String first = post.fetch(); String second = post.body("text=hello").fetch();

看,重構(gòu)沒(méi)有破壞任何東西,而且我們?nèi)匀粵](méi)有時(shí)間耦合。 可以安全地從代碼中刪除第一個(gè)請(qǐng)求,而不會(huì)影響第二個(gè)請(qǐng)求。

我希望這個(gè)例子能證明操作不可變對(duì)象的代碼更具可讀性和可維護(hù)性,因?yàn)樗鼪](méi)有時(shí)間耦合。

避免副作用

讓我們嘗試在新方法中使用我們的Request類(現(xiàn)在它是可變的):

public String post(Request request) {request.method("POST");return request.fetch(); }

讓我們嘗試發(fā)出兩個(gè)請(qǐng)求-第一個(gè)請(qǐng)求使用GET方法,第二個(gè)請(qǐng)求使用POST:

Request request = new Request("http://example.com"); request.method("GET"); String first = this.post(request); String second = request.fetch();

方法post()具有“副作用”,它對(duì)可變對(duì)象request進(jìn)行了更改。 在這種情況下,這些更改并不是真正預(yù)期的。 我們希望它發(fā)出POST請(qǐng)求并返回其主體。 我們不想閱讀其文檔只是為了發(fā)現(xiàn)它還在后臺(tái)修改了我們作為參數(shù)傳遞給它的請(qǐng)求。

不用說(shuō),這種副作用會(huì)導(dǎo)致錯(cuò)誤和可維護(hù)性問(wèn)題。 使用不可變的Request會(huì)更好:

public String post(Request request) {return request.method("POST").fetch(); }

在這種情況下,我們可能沒(méi)有任何副作用。 任何人都不能修改我們的request對(duì)象,無(wú)論它在何處使用以及方法調(diào)用傳遞給調(diào)用堆棧的深度如何:

Request request = new Request("http://example.com").method("GET"); String first = this.post(request); String second = request.fetch();

此代碼是絕對(duì)安全且無(wú)副作用的。

避免身份變異

通常,如果對(duì)象的內(nèi)部狀態(tài)相同,我們希望它們相同。 Date類是一個(gè)很好的例子:

Date first = new Date(1L); Date second = new Date(1L); assert first.equals(second); // true

有兩個(gè)不同的對(duì)象。 但是,它們彼此相等,因?yàn)樗鼈兊姆庋b狀態(tài)相同。 通過(guò)自定義的equals()和hashCode()方法的重載實(shí)現(xiàn),可以實(shí)現(xiàn)這一點(diǎn)。

這種方便的方法與可變對(duì)象一起使用的結(jié)果是,每次我們修改對(duì)象的狀態(tài)時(shí),它都會(huì)更改其身份:

Date first = new Date(1L); Date second = new Date(1L); first.setTime(2L); assert first.equals(second); // false

在您開(kāi)始將可變對(duì)象用作地圖中的鍵之前,這看起來(lái)很自然:

Map<Date, String> map = new HashMap<>(); Date date = new Date(); map.put(date, "hello, world!"); date.setTime(12345L); assert map.containsKey(date); // false

修改date狀態(tài)對(duì)象時(shí),我們不希望它更改其身份。 我們不希望僅因?yàn)槠滏I的狀態(tài)已更改而在映射中丟失條目。 但是,這正是以上示例中發(fā)生的情況。

當(dāng)我們向地圖添加對(duì)象時(shí),其hashCode()返回一個(gè)值。 HashMap使用此值將條目放置到內(nèi)部哈希表中。 當(dāng)我們調(diào)用containsKey()時(shí),對(duì)象的哈希碼是不同的(因?yàn)樗谄鋬?nèi)部狀態(tài)),并且HashMap在內(nèi)部哈希表中找不到它。

調(diào)試可變對(duì)象的副作用非常煩人且困難。 不可變的對(duì)象完全避免了它。

失效原子性

這是一個(gè)簡(jiǎn)單的示例:

public class Stack {private int size;private String[] items;public void push(String item) {size++;if (size > items.length) {throw new RuntimeException("stack overflow");}items[size] = item;} }

顯然,如果Stack類在溢出時(shí)引發(fā)運(yùn)行時(shí)異常,則該對(duì)象將處于斷開(kāi)狀態(tài)。 它的size屬性將增加,而items將不會(huì)獲得新元素。

不變性可以防止此問(wèn)題。 對(duì)象永遠(yuǎn)不會(huì)處于損壞狀態(tài),因?yàn)樗臓顟B(tài)僅在其構(gòu)造函數(shù)中被修改。 構(gòu)造函數(shù)將失敗,拒絕對(duì)象實(shí)例化,或者成功,則將生成有效的固態(tài)對(duì)象,該對(duì)象永遠(yuǎn)不會(huì)更改其封裝狀態(tài)。

有關(guān)此主題的更多信息,請(qǐng)閱讀Joshua Bloch撰寫的有效Java,第二版 。

反對(duì)不變性的爭(zhēng)論

有許多反對(duì)不變性的論點(diǎn)。

  • “不可遷移性不適用于企業(yè)系統(tǒng)”。 我經(jīng)常聽(tīng)到人們說(shuō)不變性是一種奇特的功能,而在真正的企業(yè)系統(tǒng)中絕對(duì)不可行。 作為反駁,我只能顯示一些僅包含不可變Java對(duì)象的實(shí)際應(yīng)用程序示例: jcabi-http , jcabi-xml , jcabi-github , jcabi-s3 , jcabi-dynamo , jcabi-simpledb所有僅與不可變類/對(duì)象一起使用的Java庫(kù)。 netbout.com和stateful.co是僅與不可變對(duì)象一起使用的Web應(yīng)用程序。
  • “更新現(xiàn)有對(duì)象比創(chuàng)建新對(duì)象便宜”。 Oracle 認(rèn)為 :“對(duì)象創(chuàng)建的影響通常被高估了,并且可以被與不可變對(duì)象相關(guān)的某些效率所抵消。 其中包括由于垃圾收集而減少的開(kāi)銷,以及消除了保護(hù)可變對(duì)象免受損壞所需的代碼。” 我同意。
  • 如果您還有其他論點(diǎn),請(qǐng)?jiān)谙旅姘l(fā)表,我將嘗試發(fā)表評(píng)論。

    翻譯自: https://www.javacodegeeks.com/2014/09/objects-should-be-immutable.html

    可變對(duì)象 不可變對(duì)象區(qū)別

    總結(jié)

    以上是生活随笔為你收集整理的可变对象 不可变对象区别_对象应该是不可变的的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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