一文詳解Java中的可變對(duì)象(Mutable)與不可變對(duì)象(Immutable)
如何在 Java 中創(chuàng)建不可變對(duì)象?我以前以為所有對(duì)象都是不可變的,因?yàn)槿绻愀淖円粋€(gè) String 實(shí)例的內(nèi)容,它總是會(huì)創(chuàng)建一個(gè)新的 String 對(duì)象并指向該對(duì)象。但后來(lái)我發(fā)現(xiàn),String 是一個(gè)特殊的類,它被特別設(shè)計(jì)為Immutable,因?yàn)樗?jīng)常被cache。顯然,你不能緩存任何不恒定的東西,這就是為什么 String 在 Java 中是不可變的原因。但這鼓勵(lì)我學(xué)習(xí)更多有關(guān) Java 中Immutable和Mutable類的知識(shí),以及如何在 Java 中創(chuàng)建自定義的Immutable。
在本文中,我不僅將分享在 Java 中Immutable的步驟,還將討論可變對(duì)象與不可變對(duì)象及其優(yōu)缺點(diǎn)。這也是一個(gè)常見(jiàn)的 String 面試問(wèn)題 ,Java 開(kāi)發(fā)人員也應(yīng)該意識(shí)到這一點(diǎn)。
Java 中的可變類和不可變類是什么?
在 Java 中,可變類和不可變類的概念指的是對(duì)象創(chuàng)建后其狀態(tài)是否可以更改??勺冾愂侵笇?shí)例創(chuàng)建后可以修改的類,而不可變類一旦創(chuàng)建就不能改變其狀態(tài)。
可變對(duì)象的狀態(tài)可以通過(guò)修改其字段或?qū)傩缘姆椒▉?lái)改變。例如,StringBuilder 和 ArrayList 都是可變類。例如,你可以add、delete或modify StringBuilder 或 ArrayList 中的元素。
StringBuilder mutableString = new StringBuilder("Hello"); mutableString.append(", World!"); // Mutable operation System.out.println(mutableString.toString()); // Outputs: Hello, World!
不可變類是指實(shí)例創(chuàng)建后不可修改的類。不可變對(duì)象的狀態(tài)在創(chuàng)建過(guò)程中就已設(shè)定,創(chuàng)建后無(wú)法更改。例如,String 和 Integer 就是不可變類的例子。一旦創(chuàng)建了 String 對(duì)象,就不能更改其中包含的字符。
String immutableString = "Hello"; // Immutable operation - returns a new String, // but doesn't modify the original immutableString.concat(", World!"); System.out.println(immutableString); // Outputs: Hello
因此,你現(xiàn)在應(yīng)該知道,不可變對(duì)象就是其內(nèi)容不可以更改的對(duì)象。所有字段都是final字段的類,或者所有字段都是private字段且沒(méi)有構(gòu)造器的類就是幾個(gè)例子。由于這些字段都是final字段或private字段,因此永遠(yuǎn)無(wú)法從外部更改。這使得它們不可變。
另一方面,可變類允許更改其內(nèi)容。例如,帶有非final字段或帶有構(gòu)造器的private字段的類。由于外部代碼可以更改類的內(nèi)容,因此該類是可變的。
不可變類(如 String)也可以被緩存,在 Java 中,String 被緩存在一個(gè)特殊的 String 池中,這主要是為了節(jié)省內(nèi)存,并允許重復(fù)使用 String 字面量:
如何在 Java 中創(chuàng)建不可變類?
既然我們已經(jīng)知道什么是 Java 中的可變類和不可變類,那么現(xiàn)在就來(lái)了解一下如何編寫不可變類,以及編寫不可變類與創(chuàng)建可變類有什么不同。唯一的區(qū)別在于如何編寫。
下面是一個(gè)不可變類的示例:
public class CustomImmutableClass { public final String customString = ""; //field is final, so it cannot be changed private int customInt = 0; //field is private and has no setter, so it cannot be changed public int getCustomInt() { return customInt; //CustomInt can be retrieved, but not set. } }
這是可變類的示例:
public class CustomMutableClass { public String customString = ""; //field is NOT final, so it CAN be changed private int customInt = 0; //field is private and has a setter, so it CAN be changed public int getCustomInt() { return customInt; //CustomInt can be retrieved } public void setCustomInt(int customInt) { this.customInt = customInt; //customInt can be set } }
不可變類應(yīng)被標(biāo)記為 final 類,這樣它們就不能被擴(kuò)展,但僅僅使類成為 final 類并不能使其成為不可變類,尤其是當(dāng)它可能泄漏狀態(tài)(如返回一個(gè)非 final 的對(duì)象和狀態(tài)的一部分)時(shí)。此外,擁有公共的 final 字段也是一種不好的形式。
字符串是不可變的,而大多數(shù)對(duì)象不是。無(wú)論何時(shí)使用突變器方法(setSomething 或 addSomething)
返回 void 的對(duì)象很可能是可變的。一個(gè)突出的例子就是 ArrayList。
要使對(duì)象不可變,請(qǐng)確保它們只有非數(shù)組的final字段(在 Java 中數(shù)組總是可變的),并且所有字段類型也只有final字段。如果不允許訪問(wèn)/更改字段,就可以使用非final字段,但這并不容易推理(但大多數(shù)情況下比較容易)。
Java 中不可變對(duì)象與可變對(duì)象的區(qū)別
以下是 Java 中可變類和不可變類之間的一些主要區(qū)別:
1. 修改
可變對(duì)象在創(chuàng)建后可以修改,但不可變對(duì)象在創(chuàng)建后不能修改。
2. 線程安全可變對(duì)象不是線程安全的,如果在多線程環(huán)境中使用,可能需要同步以避免數(shù)據(jù)損壞。另一方面,不可變對(duì)象通常是線程安全的,因?yàn)闋顟B(tài)不能更改并且可以在多個(gè)線程之間安全共享。
3. 狀態(tài)更改可變對(duì)象允許更改狀態(tài),但不可變對(duì)象的狀態(tài)在創(chuàng)建時(shí)是固定的。
4. 使用案例當(dāng)你需要經(jīng)常修改對(duì)象,或想表示狀態(tài)會(huì)隨時(shí)間變化的實(shí)體時(shí),可變對(duì)象就派上用場(chǎng)了。而不可變對(duì)象則適用于需要確保對(duì)象狀態(tài)保持不變或需要線程安全的情況。
5. 性能由于直接修改狀態(tài),可變對(duì)象在某些場(chǎng)景下可以具有更好的性能,但不可變對(duì)象可能涉及創(chuàng)建新對(duì)象,可能會(huì)影響性能,但在安全性和簡(jiǎn)單性方面具有優(yōu)勢(shì)。
總結(jié)
這就是Java 中的不可變類和可變類的全部?jī)?nèi)容。 本文不僅介紹了什么是可變類和不可變類,還介紹了它們之間的區(qū)別。在可變類和不可變類之間做出選擇,取決于程序的具體要求和所需對(duì)象的特性。不可變類通常是并發(fā)或多線程環(huán)境中的首選,可以簡(jiǎn)化對(duì)對(duì)象狀態(tài)的推理。
以上就是一文詳解Java中的可變對(duì)象(Mutable)與不可變對(duì)象(Immutable)的詳細(xì)內(nèi)容,更多關(guān)于Java可變對(duì)象與不可變對(duì)象的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java源碼解析ArrayList及ConcurrentModificationException
今天小編就為大家分享一篇關(guān)于Java源碼解析ArrayList及ConcurrentModificationException,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01Java線程Dump分析工具jstack解析及使用場(chǎng)景
這篇文章主要介紹了Java線程Dump分析工具jstack解析及使用場(chǎng)景,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01maven打包上傳到私有倉(cāng)庫(kù)的實(shí)現(xiàn)步驟
這篇文章主要介紹了maven打包上傳到私有倉(cāng)庫(kù)的實(shí)現(xiàn)步驟,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01使用@PropertySource讀取配置文件通過(guò)@Value進(jìn)行參數(shù)注入
這篇文章主要介紹了使用@PropertySource讀取配置文件通過(guò)@Value進(jìn)行參數(shù)注入,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Java數(shù)據(jù)類型之細(xì)講char類型與編碼關(guān)系
這幾天一直在復(fù)習(xí)Java基礎(chǔ)知識(shí),特地寫了一篇文章來(lái)做一下筆記,文中有非常詳細(xì)的圖文示例,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05Eclipse2020安裝了最新版本的JDK卻無(wú)法打開(kāi)的問(wèn)題
這篇文章主要介紹了Eclipse2020安裝了最新版本的JDK卻無(wú)法打開(kāi),提示版本太老的完美解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Java動(dòng)態(tài)初始化數(shù)組,元素默認(rèn)值規(guī)則詳解
動(dòng)態(tài)初始化數(shù)組涉及先定義數(shù)組長(zhǎng)度,后填充具體數(shù)據(jù),適用于數(shù)據(jù)量已知但具體值未定的情況,這種初始化方式允許程序運(yùn)行過(guò)程中賦值,并會(huì)根據(jù)數(shù)據(jù)類型設(shè)定默認(rèn)值,如整型為0,字符串為null,動(dòng)態(tài)初始化與靜態(tài)初始化格式不能混用2024-10-10Kotlin基礎(chǔ)教程之Run,標(biāo)簽Label,函數(shù)Function-Type
這篇文章主要介紹了Kotlin基礎(chǔ)教程之Run,標(biāo)簽Label,函數(shù)Function-Type的相關(guān)資料,需要的朋友可以參考下2017-05-05