<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          String 為什么不可變?不可變有什么好處?

          共 3159字,需瀏覽 7分鐘

           ·

          2022-05-23 18:14

          前言

          說到String的不可變性,我猜肯定有同學(xué)要說可以通過反射來修改。所以我們在分享之前,在這邊先出一個(gè)反射的題目,大家看看能不能答對。


          題目

          String name = "jionghui";Field field = String.class.getDeclaredField("value");field.setAccessible(true);char[] value = (char[]) field.get(name);value[0] = 'a';System.out.println("jionghui" == name);

          ?

          大家可以思考一下這個(gè)題目,我會在文末給出答案和解析。


          不可變是什么意思

          不可變類(immutable):類的實(shí)例一旦創(chuàng)建后,其內(nèi)容(狀態(tài))就不可改變。

          簡單理解就是:一個(gè)對象一旦被創(chuàng)建后,整個(gè)對象就是不可變的。包括屬任何性和狀態(tài)。

          可能有同學(xué)會拿下面這段代碼來說,這不是變了嗎?

          public void testFinal() {    String str = "程序員囧輝";    str = "屌絲囧輝";}

          ?

          我們看下第2行代碼,這行代碼中有兩部分組成。

          等號左邊:一個(gè)局部變量 str,類型是 String,這個(gè)變量是放在棧上的。

          等號右邊:一個(gè)字符串對象,放在堆中。

          b5cffa222c46198640b61c506ff16778.webp

          我們說的不可變,指的是字符串對象。

          我們通過第3行代碼,將這個(gè) str 變量賦值為另一個(gè)字符串,對原來的字符串對象是沒有任何改變的。


          final修飾value數(shù)組?

          3207d8d56ac9ef6fb6419ed22218daf3.webp

          我猜有不少同學(xué)在回答這個(gè)問題的時(shí)候,會答說是因?yàn)閟tring底層的這個(gè)value 數(shù)組被 final 修飾,所以 String 不可變,這個(gè)說法其實(shí)不正確。

          我們來看一個(gè)例子:

          43fa28acaabe41b40491b110ba11ae87.webp

          這個(gè)例子中,我們的 demo 變量使用了 final 修飾,但是我們?nèi)匀桓淖兞似鋬?nèi)容。所以,final 并不能保證對象的一個(gè)不可變性。


          final修飾變量的含義

          基礎(chǔ)數(shù)據(jù)類型:一旦初始化,便不能改變其值。

          引用類型:一旦初始化,便不能改變其引用,也就是不能指向一個(gè)新的對象,但是仍然可以修改引用指向的對象內(nèi)容。


          為什么不可變?

          1)value使用final修飾

          3207d8d56ac9ef6fb6419ed22218daf3.webp

          保證value一旦被初始化,就不可改變其引用。


          2)沒有暴露成員變量

          9a6063412b8f11cd16625c06f4df1bc0.webp

          成員變量的訪問權(quán)限為 private,同時(shí)沒有提供方法將字段暴露出來,想要修改只能通過 String 提供的方法。


          3)內(nèi)部方法不會改動(dòng) value

          ce125e3ac872b2e3363ca76bd26816f4.webp

          一旦初始化之后,String 類中的方法就不會去改動(dòng) value 中的元素,需要的話都是直接新建一個(gè) String 對象。


          4)類使用final修飾,不可繼承

          b99f7c93f739d1b0e4b74f2a2a2ba10d.webp

          這個(gè)設(shè)計(jì)主要是避免有人定義一個(gè)子類繼承 String,然后重寫 String 的方法,將這個(gè)子類設(shè)計(jì)成可變對象。我們知道在 java 中,有父類引用指向子類對象這種用法,這種情況下,我們需要一個(gè)String 對象,可能返回的是String 子類的對象,這會導(dǎo)致 String 看起來是可變的。所以? java 直接將 String定義成不可繼承,避免出現(xiàn)這種情況。


          不只是 String 類,其實(shí)所有的不可變類大致的設(shè)計(jì)思想都是按這四步來。后續(xù)如果我們自己想要設(shè)計(jì)一個(gè)不可變類,也可以按這四點(diǎn)來設(shè)計(jì)。


          不可變的好處?為什么這么設(shè)計(jì)?

          1)安全性

          String 是 Java 中最基礎(chǔ)也是最長使用的類,經(jīng)常用于存儲一些敏感信息,例如用戶名、密碼、網(wǎng)絡(luò)連接等。因此,String 類的安全性對于整個(gè)應(yīng)用程序至關(guān)重要。

          我們來看下面這個(gè)例子:

          private static void dangerousOperation(MyString myString) throws InterruptedException {    if (!securityCheck(myString)) {        System.out.println("校驗(yàn)失敗");        return;    }    // 一些七的八的操作    doSomething();    // 執(zhí)行危險(xiǎn)操作    dangerous(myString);}

          ?我們通過一個(gè)方法來模擬一個(gè)危險(xiǎn)的一個(gè)系統(tǒng)操作。

          首先在這個(gè)方法的入口會進(jìn)行一個(gè)安全檢查。如果檢查失敗,會直接返回。

          然后接著我們會最終去執(zhí)行這個(gè)比較危險(xiǎn)的操作。

          如果此時(shí)這個(gè)方法的參數(shù)是可變對象,那么它可能在通過安全檢查的時(shí)候,是一個(gè)合法的入?yún)ⅰ5钱?dāng)最終執(zhí)行到下面的危險(xiǎn)操作時(shí),他可能被調(diào)用方給修改了,變成一個(gè)不合法的參數(shù)。但是這個(gè)時(shí)候他已經(jīng)通過檢查了,所以我們沒辦法對他進(jìn)行攔截,最終可能會導(dǎo)致我們的系統(tǒng)被攻擊或者存在安全隱患。


          2)節(jié)省空間——字符串常量池

          通過使用常量池,內(nèi)容相同的字符串可以使用同一個(gè)對象,從而節(jié)省內(nèi)存空間。如果 String 是可變的,試想一下,當(dāng)字符串常量池中的某個(gè)字符串對象被很多地方引用時(shí),此時(shí)修改了這個(gè)對象,則所有引用的地方都會改變,這可能會導(dǎo)致預(yù)期之外的情況。

          典型的使用字符串常量池的場景:json 工具類,fastjson、jackson 等。


          3)線程安全

          String 對象是不可修改的,如果線程嘗試修改 String 對象,會創(chuàng)建新的 String,所以不存在并發(fā)修改同一個(gè)對象的問題。


          4)性能

          String 被廣泛應(yīng)用于 HashMap、HashSet 等哈希類中,當(dāng)對這些哈希類進(jìn)行操作時(shí),例如 HashMap 的 get/put,hashCode 會被頻繁調(diào)用。

          由于不可變性,String 的 hashCode 只需要計(jì)算1次后就可以緩存起來,因此在哈希類中使用 String 對象可以提升性能。


          Java 之父的觀點(diǎn)

          7a4dd787a4e80ec8714c23f6a3f8b8eb.webp

          對于不可變性,Java 之父詹姆斯高斯林在一次采訪中談過這個(gè)話題,他表示:只要可以,他就會使用不可變性。可以看出他對不可變性的評價(jià)非常高。

          至于不可變的好處,高斯林主要談到了幾個(gè)觀點(diǎn):

          1)不可變對象往往更不容易出問題;

          2)安全性問題;

          3)緩存。

          這三點(diǎn)在我們之前的內(nèi)容里也基本都提到了,原文如下,有興趣的可以去看一下。

          https://www.artima.com/articles/james-gosling-on-java-may-2001#part13


          題目答案

          文章開頭題目的答案是 true。

          解釋:當(dāng)這段代碼被編譯之后,這兩個(gè)被雙引號修飾的 jionghui 字符串字面量,由于它們的值是相同的,所以它們會指向同一個(gè)符號引用。

          當(dāng)這個(gè)符號引用被解析時(shí),我們會在字符串常量池中創(chuàng)建一個(gè) jionghui 字符串。最終這兩個(gè)字符串字面量都會指向我們字符串常量池里面的這個(gè) jionghui,所以他們其實(shí)指向的是同一個(gè)字符串對象。

          因?yàn)樗麄兊囊檬窍嗤模赃@個(gè)地方輸出結(jié)果的是 true。

          看過我上一個(gè)文章/視頻的同學(xué)應(yīng)該不難理解。如果你不理解,或者說你對字符串常量池、符號引用有一些疑問,你可以去看一下我的上一個(gè)文章/視頻。我在上一個(gè)文章/視頻里有詳解介紹字符串常量池和符號引用的相關(guān)內(nèi)容。


          推薦閱讀

          小白也看得懂的 I/O 多路復(fù)用解析

          外包公司能去嗎?進(jìn)了外包如何翻盤?

          全網(wǎng)最實(shí)用的 IDEA Debug 調(diào)試技巧(超詳細(xì)案例)

          Java 基礎(chǔ)高頻面試題(2021年最新版)

          Java 集合框架高頻面試題(2021年最新版)

          面試必問的 Spring,你懂了嗎?

          面試必問的 MySQL,你懂了嗎?

          最近我將面試:阿里、字節(jié)、美團(tuán)、快手、拼多多等大廠的高頻面試整理出來,并按大廠的標(biāo)準(zhǔn)給出自己的解析。

          群里有不少同學(xué)看完拿下了阿里、美團(tuán)等大廠 Offer,希望能助你一臂之力,早日拿下大廠 Offer。

          獲取方式:關(guān)注公眾號回復(fù)【面試】即可領(lǐng)取,更多大廠面試真題解析 PDF 整理中。

          09f31124def86b09469f6041abfc2f2d.webp

          瀏覽 29
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲无 码A片在线 | 男人天堂资源网 | 思瑞与土豪国产一区二区 | 日本高清无码在线 | 色婷婷在线视频精品免费 |