欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JAVA不可變類(immutable)機(jī)制與String的不可變性(推薦)

 更新時(shí)間:2016年08月01日 11:12:15   作者:JohnZheng  
這篇文章主要介紹了JAVA不可變類(immutable)機(jī)制與String的不可變性(推薦)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

一、不可變類簡(jiǎn)介

不可變類:所謂的不可變類是指這個(gè)類的實(shí)例一旦創(chuàng)建完成后,就不能改變其成員變量值。如JDK內(nèi)部自帶的很多不可變類:Interger、Long和String等。
可變類:相對(duì)于不可變類,可變類創(chuàng)建實(shí)例后可以改變其成員變量值,開(kāi)發(fā)中創(chuàng)建的大部分類都屬于可變類。

二、不可變類的優(yōu)點(diǎn)

說(shuō)完可變類和不可變類的區(qū)別,我們需要進(jìn)一步了解為什么要有不可變類?這樣的特性對(duì)JAVA來(lái)說(shuō)帶來(lái)怎樣的好處?

1.線程安全

不可變對(duì)象是線程安全的,在線程之間可以相互共享,不需要利用特殊機(jī)制來(lái)保證同步問(wèn)題,因?yàn)閷?duì)象的值無(wú)法改變??梢越档筒l(fā)錯(cuò)誤的可能性,因?yàn)椴恍枰靡恍╂i機(jī)制等保證內(nèi)存一致性問(wèn)題也減少了同步開(kāi)銷。

2.易于構(gòu)造、使用和測(cè)試

3....

三、不可變類的設(shè)計(jì)方法

對(duì)于設(shè)計(jì)不可變類,個(gè)人總結(jié)出以下原則:

1. 類添加final修飾符,保證類不被繼承。

如果類可以被繼承會(huì)破壞類的不可變性機(jī)制,只要繼承類覆蓋父類的方法并且繼承類可以改變成員變量值,那么一旦子類以父類的形式出現(xiàn)時(shí),不能保證當(dāng)前類是否可變。

2. 保證所有成員變量必須私有,并且加上final修飾

通過(guò)這種方式保證成員變量不可改變。但只做到這一步還不夠,因?yàn)槿绻菍?duì)象成員變量有可能再外部改變其值。所以第4點(diǎn)彌補(bǔ)這個(gè)不足。

3. 不提供改變成員變量的方法,包括setter

避免通過(guò)其他接口改變成員變量的值,破壞不可變特性。

4.通過(guò)構(gòu)造器初始化所有成員,進(jìn)行深拷貝(deep copy)

如果構(gòu)造器傳入的對(duì)象直接賦值給成員變量,還是可以通過(guò)對(duì)傳入對(duì)象的修改進(jìn)而導(dǎo)致改變內(nèi)部變量的值。例如:

public final class ImmutableDemo { 
private final int[] myArray; 
public ImmutableDemo(int[] array) { 
this.myArray = array; // wrong 
} 
}

這種方式不能保證不可變性,myArray和array指向同一塊內(nèi)存地址,用戶可以在ImmutableDemo之外通過(guò)修改array對(duì)象的值來(lái)改變myArray內(nèi)部的值。

為了保證內(nèi)部的值不被修改,可以采用深度copy來(lái)創(chuàng)建一個(gè)新內(nèi)存保存?zhèn)魅氲闹?。正確做法:

public final class MyImmutableDemo { 
private final int[] myArray; 
public MyImmutableDemo(int[] array) { 
this.myArray = array.clone(); 
} 
}

5. 在getter方法中,不要直接返回對(duì)象本身,而是克隆對(duì)象,并返回對(duì)象的拷貝

這種做法也是防止對(duì)象外泄,防止通過(guò)getter獲得內(nèi)部可變成員對(duì)象后對(duì)成員變量直接操作,導(dǎo)致成員變量發(fā)生改變。

四、String對(duì)象的不可變性

string對(duì)象在內(nèi)存創(chuàng)建后就不可改變,不可變對(duì)象的創(chuàng)建一般滿足以上5個(gè)原則,我們看看String代碼是如何實(shí)現(xiàn)的。

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
....
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length); // deep copy操作
}
...
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
...
} 

如上代碼所示,可以觀察到以下設(shè)計(jì)細(xì)節(jié):

1.String類被final修飾,不可繼承

2.string內(nèi)部所有成員都設(shè)置為私有變量

3.不存在value的setter

4.并將value和offset設(shè)置為final。

5.當(dāng)傳入可變數(shù)組value[]時(shí),進(jìn)行copy而不是直接將value[]復(fù)制給內(nèi)部變量.

6.獲取value時(shí)不是直接返回對(duì)象引用,而是返回對(duì)象的copy.

這都符合上面總結(jié)的不變類型的特性,也保證了String類型是不可變的類。

五、String對(duì)象的不可變性的優(yōu)缺點(diǎn)

從上一節(jié)分析,String數(shù)據(jù)不可變類,那設(shè)置這樣的特性有什么好處呢?我總結(jié)為以下幾點(diǎn):

1.字符串常量池的需要.

字符串常量池可以將一些字符常量放在常量池中重復(fù)使用,避免每次都重新創(chuàng)建相同的對(duì)象、節(jié)省存儲(chǔ)空間。但如果字符串是可變的,此時(shí)相同內(nèi)容的String還指向常量池的同一個(gè)內(nèi)存空間,當(dāng)某個(gè)變量改變了該內(nèi)存的值時(shí),其他遍歷的值也會(huì)發(fā)生改變。所以不符合常量池設(shè)計(jì)的初衷。

2. 線程安全考慮。

同一個(gè)字符串實(shí)例可以被多個(gè)線程共享。這樣便不用因?yàn)榫€程安全問(wèn)題而使用同步。字符串自己便是線程安全的。

3. 類加載器要用到字符串,不可變性提供了安全性,以便正確的類被加載。譬如你想加載java.sql.Connection類,而這個(gè)值被改成了myhacked.Connection,那么會(huì)對(duì)你的數(shù)據(jù)庫(kù)造成不可知的破壞。

4. 支持hash映射和緩存。

因?yàn)樽址遣豢勺兊?,所以在它?chuàng)建的時(shí)候hashcode就被緩存了,不需要重新計(jì)算。這就使得字符串很適合作為Map中的鍵,字符串的處理速度要快過(guò)其它的鍵對(duì)象。這就是HashMap中的鍵往往都使用字符串。

缺點(diǎn):

1.如果有對(duì)String對(duì)象值改變的需求,那么會(huì)創(chuàng)建大量的String對(duì)象。

六、String對(duì)象的是否真的不可變

雖然String對(duì)象將value設(shè)置為final,并且還通過(guò)各種機(jī)制保證其成員變量不可改變。但是還是可以通過(guò)反射機(jī)制的手段改變其值。例如:

//創(chuàng)建字符串"Hello World", 并賦給引用s
String s = "Hello World"; 
System.out.println("s = " + s); //Hello World
//獲取String類中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改變value屬性的訪問(wèn)權(quán)限
valueFieldOfString.setAccessible(true);
//獲取s對(duì)象上的value屬性的值
char[] value = (char[]) valueFieldOfString.get(s);
//改變value所引用的數(shù)組中的第5個(gè)字符
value[5] = '_';
System.out.println("s = " + s); //Hello_World 

打印結(jié)果為:

s = Hello World
s = Hello_World 

發(fā)現(xiàn)String的值已經(jīng)發(fā)生了改變。也就是說(shuō),通過(guò)反射是可以修改所謂的“不可變”對(duì)象的

總結(jié)

不可變類是實(shí)例創(chuàng)建后就不可以改變成員遍歷的值。這種特性使得不可變類提供了線程安全的特性但同時(shí)也帶來(lái)了對(duì)象創(chuàng)建的開(kāi)銷,每更改一個(gè)屬性都是重新創(chuàng)建一個(gè)新的對(duì)象。JDK內(nèi)部也提供了很多不可變類如Integer、Double、String等。String的不可變特性主要為了滿足常量池、線程安全、類加載的需求。合理使用不可變類可以帶來(lái)極大的好處。

以上所述是小編給大家介紹的JAVA不可變類(immutable)機(jī)制與String的不可變性(推薦),希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Maven中dependencyManagement管理項(xiàng)目依賴項(xiàng)

    Maven中dependencyManagement管理項(xiàng)目依賴項(xiàng)

    在開(kāi)發(fā)?Java?項(xiàng)目時(shí),管理和協(xié)調(diào)依賴項(xiàng)的版本號(hào)是一項(xiàng)重要而繁瑣的任務(wù),本文主要介紹了Maven中dependencyManagement管理項(xiàng)目依賴項(xiàng),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Java通過(guò)索引值實(shí)現(xiàn)約瑟夫環(huán)算法

    Java通過(guò)索引值實(shí)現(xiàn)約瑟夫環(huán)算法

    這篇文章主要介紹了Java通過(guò)索引值實(shí)現(xiàn)約瑟夫環(huán),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • 使用springboot開(kāi)發(fā)的第一個(gè)web入門程序的實(shí)現(xiàn)

    使用springboot開(kāi)發(fā)的第一個(gè)web入門程序的實(shí)現(xiàn)

    這篇文章主要介紹了使用springboot開(kāi)發(fā)的第一個(gè)web入門程序的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 通過(guò)Java組合問(wèn)題看透回溯法

    通過(guò)Java組合問(wèn)題看透回溯法

    今天給大家分享一道LeetCode算法題,題目不是很困難,但是從這到簡(jiǎn)單的題目我們可以分析出回溯算法的幾個(gè)核心要點(diǎn),感興趣的可以了解一下
    2022-09-09
  • windows下使用 intellij idea 編譯 kafka 源碼環(huán)境

    windows下使用 intellij idea 編譯 kafka 源碼環(huán)境

    這篇文章主要介紹了使用 intellij idea 編譯 kafka 源碼的環(huán)境,本文是基于windows下做的項(xiàng)目演示,需要的朋友可以參考下
    2021-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)順序表的詳細(xì)講解

    Java數(shù)據(jù)結(jié)構(gòu)順序表的詳細(xì)講解

    大家好,今天給大家?guī)?lái)的是順序表,我覺(jué)得順序表還是有比較難理解的地方的,于是我就把這一塊的內(nèi)容全部整理到了一起,希望能夠給剛剛進(jìn)行學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的人帶來(lái)一些幫助,或者是已經(jīng)學(xué)過(guò)這塊的朋友們帶來(lái)更深的理解,我們現(xiàn)在就開(kāi)始吧
    2022-05-05
  • Spring Boot Actuator自定義健康檢查教程

    Spring Boot Actuator自定義健康檢查教程

    這篇文章主要介紹了Spring Boot Actuator自定義健康檢查教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java實(shí)現(xiàn)短信通信的完整教程

    java實(shí)現(xiàn)短信通信的完整教程

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)短信通信的完整教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構(gòu)建)

    springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構(gòu)建)

    這篇文章主要介紹了springboot-2.3.x最新版源碼閱讀環(huán)境搭建(基于gradle構(gòu)建),需要的朋友可以參考下
    2020-08-08
  • SpringBoot重寫addResourceHandlers映射文件路徑方式

    SpringBoot重寫addResourceHandlers映射文件路徑方式

    這篇文章主要介紹了SpringBoot重寫addResourceHandlers映射文件路徑方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評(píng)論