<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 02 String、Long 源碼解析和面試題 > 勞動是一切知識的源泉。 > ——陶鑄 ## 引導語 String 和 Long 大家都很熟悉,本小節主要結合實際的工作場景,來一起看下 String 和 Long 的底層源碼實現,看看平時我們使用時,有無需要注意的點,總結一下這些 API 都適用于哪些場景。 ## 1 String ### 1.1 不變性 我們常常聽人說,HashMap 的 key 建議使用不可變類,比如說 String 這種不可變類。這里說的不可變指的是類值一旦被初始化,就不能再被改變了,如果被修改,將會是新的類,我們寫個 demo 來演示一下。 ~~~java String s ="hello"; s ="world"; ~~~ 從代碼上來看,s 的值好像被修改了,但從 debug 的日志來看,其實是 s 的內存地址已經被修改了,也就說 s =“world” 這個看似簡單的賦值,其實已經把 s 的引用指向了新的 String,debug 的截圖顯示內存地址已經被修改,兩張截圖如下: ![圖片描述](https://img1.sycdn.imooc.com/5d5fc04a0001c6a508840096.png)![圖片描述](https://img1.sycdn.imooc.com/5d5fc06400019cc210540090.png)我們從源碼上查看一下原因: ~~~java public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; } ~~~ 我們可以看出來兩點: 1. String 被 final 修飾,說明 String 類絕不可能被繼承了,也就是說任何對 String 的操作方法,都不會被繼承覆寫; 2. String 中保存數據的是一個 char 的數組 value。我們發現 value 也是被 final 修飾的,也就是說 value 一旦被賦值,內存地址是絕對無法修改的,而且 value 的權限是 private 的,外部絕對訪問不到,String 也沒有開放出可以對 value 進行賦值的方法,所以說 value 一旦產生,內存地址就根本無法被修改。 以上兩點就是 String 不變性的原因,充分利用了 final 關鍵字的特性,如果你自定義類時,希望也是不可變的,也可以模仿 String 的這兩點操作。 因為 String 具有不變性,所以 String 的大多數操作方法,都會返回新的 String,如下面這種寫法是不對的: ~~~java String str ="hello world !!"; // 這種寫法是替換不掉的,必須接受 replace 方法返回的參數才行,這樣才行:str = str.replace("l","dd"); str.replace("l","dd"); ~~~ ### 1.2 字符串亂碼 在生活中,我們經常碰到這樣的場景,進行二進制轉化操作時,本地測試的都沒有問題,到其它環境機器上時,有時會出現字符串亂碼的情況,這個主要是因為在二進制轉化操作時,并沒有強制規定文件編碼,而不同的環境默認的文件編碼不一致導致的。 我們也寫了一個 demo 來模仿一下字符串亂碼: ~~~java String str ="nihao 你好 喬亂"; // 字符串轉化成 byte 數組 byte[] bytes = str.getBytes("ISO-8859-1"); // byte 數組轉化成字符串 String s2 = new String(bytes); log.info(s2); // 結果打印為: nihao ?? ?? ~~~ 打印的結果為??,這就是常見的亂碼表現形式。這時候有同學說,是不是我把代碼修改成`String s2 = new String(bytes,"ISO-8859-1");`就可以了?這是不行的。主要是因為 ISO-8859-1 這種編碼對中文的支持有限,導致中文會顯示亂碼。唯一的解決辦法,就是在所有需要用到編碼的地方,都統一使用 UTF-8,對于 String 來說,getBytes 和 new String 兩個方法都會使用到編碼,我們把這兩處的編碼替換成 UTF-8 后,打印出的結果就正常了。 ### 1.3 首字母大小寫 如果我們的項目被 Spring 托管的話,有時候我們會通過`applicationContext.getBean(className);`這種方式得到 SpringBean,這時 className 必須是要滿足首字母小寫的,除了該場景,在反射場景下面,我們也經常要使類屬性的首字母小寫,這時候我們一般都會這么做: `name.substring(0, 1).toLowerCase() + name.substring(1);`,使用 substring 方法,該方法主要是為了截取字符串連續的一部分,substring 有兩個方法: 1. `public String substring(int beginIndex, int endIndex)`beginIndex:開始位置,endIndex:結束位置; 2. `public String substring(int beginIndex)`beginIndex:開始位置,結束位置為文本末尾。 substring 方法的底層使用的是字符數組范圍截取的方法 :`Arrays.copyOfRange(字符數組, 開始位置, 結束位置);`從字符數組中進行一段范圍的拷貝。 相反的,如果要修改成首字母大寫,只需要修改成`name.substring(0, 1).toUpperCase() + name.substring(1)`即可。 ### 1.4 相等判斷 我們判斷相等有兩種辦法,equals 和 equalsIgnoreCase。后者判斷相等時,會忽略大小寫,近期看見一些面試題在問:如果讓你寫判斷兩個 String 相等的邏輯,應該如何寫,我們來一起看下 equals 的源碼,整理一下思路: ~~~java public boolean equals(Object anObject) { // 判斷內存地址是否相同 if (this == anObject) { return true; } // 待比較的對象是否是 String,如果不是 String,直接返回不相等 if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; // 兩個字符串的長度是否相等,不等則直接返回不相等 if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; // 依次比較每個字符是否相等,若有一個不等,直接返回不相等 while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } ~~~ 從 equals 的源碼可以看出,邏輯非常清晰,完全是根據 String 底層的結構來編寫出相等的代碼。這也提供了一種思路給我們:如果有人問如何判斷兩者是否相等時,我們可以從兩者的底層結構出發,這樣可以迅速想到一種貼合實際的思路和方法,就像 String 底層的數據結構是 char 的數組一樣,判斷相等時,就挨個比較 char 數組中的字符是否相等即可。 ### 1.5 替換、刪除 替換在工作中也經常使用,有 replace 替換所有字符、replaceAll 批量替換字符串、replaceFirst 替換遇到的第一個字符串三種場景。 其中在使用 replace 時需要注意,replace 有兩個方法,一個入參是 char,一個入參是 String,前者表示替換所有字符,如:`name.replace('a','b')`,后者表示替換所有字符串,如:`name.replace("a","b")`,兩者就是單引號和多引號的區別。 需要注意的是, replace 并不只是替換一個,是替換所有匹配到的字符或字符串哦。 寫了一個 demo 演示一下三種場景: ~~~java public void testReplace(){ String str ="hello word !!"; log.info("替換之前 :{}",str); str = str.replace('l','d'); log.info("替換所有字符 :{}",str); str = str.replaceAll("d","l"); log.info("替換全部 :{}",str); str = str.replaceFirst("l",""); log.info("替換第一個 l :{}",str); } //輸出的結果是: 替換之前 :hello word !! 替換所有字符 :heddo word !! 替換全部 :hello worl !! 替換第一個 :helo worl !! ~~~ 當然我們想要刪除某些字符,也可以使用 replace 方法,把想刪除的字符替換成 “” 即可。 ### 1.6 拆分和合并 拆分我們使用 split 方法,該方法有兩個入參數。第一個參數是我們拆分的標準字符,第二個參數是一個 int 值,叫 limit,來限制我們需要拆分成幾個元素。如果 limit 比實際能拆分的個數小,按照 limit 的個數進行拆分,我們演示一個 demo: ~~~java String s ="boo:and:foo"; // 我們對 s 進行了各種拆分,演示的代碼和結果是: s.split(":") 結果:["boo","and","foo"] s.split(":",2) 結果:["boo","and:foo"] s.split(":",5) 結果:["boo","and","foo"] s.split(":",-2) 結果:["boo","and","foo"] s.split("o") 結果:["b","",":and:f"] s.split("o",2) 結果:["b","o:and:foo"] ~~~ 從演示的結果來看,limit 對拆分的結果,是具有限制作用的,還有就是拆分結果里面不會出現被拆分的字段。 那如果字符串里面有一些空值呢,拆分的結果如下: ~~~java String a =",a,,b,"; a.split(",") 結果:["","a","","b"] ~~~ 從拆分結果中,我們可以看到,空值是拆分不掉的,仍然成為結果數組的一員,如果我們想刪除空值,只能自己拿到結果后再做操作,但 Guava(Google 開源的技術工具) 提供了一些可靠的工具類,可以幫助我們快速去掉空值,如下: ~~~java String a =",a, , b c ,"; // Splitter 是 Guava 提供的 API List<String> list = Splitter.on(',') .trimResults()// 去掉空格 .omitEmptyStrings()// 去掉空值 .splitToList(a); log.info("Guava 去掉空格的分割方法:{}",JSON.toJSONString(list)); // 打印出的結果為: ["a","b c"] ~~~ 從打印的結果中,可以看到去掉了空格和空值,這正是我們工作中常常期望的結果,所以推薦使用 Guava 的 API 對字符串進行分割。 合并我們使用 join 方法,此方法是靜態的,我們可以直接使用。方法有兩個入參,參數一是合并的分隔符,參數二是合并的數據源,數據源支持數組和 List,在使用的時候,我們發現有兩個不太方便的地方: 1. 不支持依次 join 多個字符串,比如我們想依次 join 字符串 s 和 s1,如果你這么寫的話`String.join(",",s).join(",",s1)`最后得到的是 s1 的值,第一次 join 的值被第二次 join 覆蓋了; 2. 如果 join 的是一個 List,無法自動過濾掉 null 值。 而 Guava 正好提供了 API,解決上述問題,我們來演示一下: ~~~java // 依次 join 多個字符串,Joiner 是 Guava 提供的 API Joiner joiner = Joiner.on(",").skipNulls(); String result = joiner.join("hello",null,"china"); log.info("依次 join 多個字符串:{}",result); List<String> list = Lists.newArrayList(new String[]{"hello","china",null}); log.info("自動刪除 list 中空值:{}",joiner.join(list)); // 輸出的結果為; 依次 join 多個字符串:hello,china 自動刪除 list 中空值:hello,china ~~~ 從結果中,我們可以看到 Guava 不僅僅支持多個字符串的合并,還幫助我們去掉了 List 中的空值,這就是我們在工作中常常需要得到的結果。 ## 2 Long ### 2.1 緩存 Long 最被我們關注的就是 Long 的緩存問題,Long 自己實現了一種緩存機制,緩存了從 -128 到 127 內的所有 Long 值,如果是這個范圍內的 Long 值,就不會初始化,而是從緩存中拿,緩存初始化源碼如下: ~~~java private static class LongCache { private LongCache(){} // 緩存,范圍從 -128 到 127,+1 是因為有個 0 static final Long cache[] = new Long[-(-128) + 127 + 1]; // 容器初始化時,進行加載 static { // 緩存 Long 值,注意這里是 i - 128 ,所以再拿的時候就需要 + 128 for(int i = 0; i < cache.length; i++) cache[i] = new Long(i - 128); } } ~~~ ## 3 面試題 ### 3.1 為什么使用 Long 時,大家推薦多使用 valueOf 方法,少使用 parseLong 方法 答:因為 Long 本身有緩存機制,緩存了 -128 到 127 范圍內的 Long,valueOf 方法會從緩存中去拿值,如果命中緩存,會減少資源的開銷,parseLong 方法就沒有這個機制。 ### 3.2 如何解決 String 亂碼的問題 答:亂碼的問題的根源主要是兩個:字符集不支持復雜漢字、二進制進行轉化時字符集不匹配,所以在 String 亂碼時我們可以這么做: 1. 所有可以指定字符集的地方強制指定字符集,比如 new String 和 getBytes 這兩個地方; 2. 我們應該使用 UTF-8 這種能完整支持復雜漢字的字符集。 ### 3.3 為什么大家都說 String 是不可變的 答:主要是因為 String 和保存數據的 char 數組,都被 final 關鍵字所修飾,所以是不可變的,具體細節描述可以參考上文。 ### 3.4 String 一些常用操作問題,如問如何分割、合并、替換、刪除、截取等等問題 答:這些都屬于問 String 的基本操作題目,考察我們平時對 String 的使用熟練程度,可以參考上文。 ## 總結 String 和 Long 在我們工作中使用頻率很高,在面試的過程中,考官也喜歡問一些關于實際操作的問題,來考察我們的使用熟練度,所以本文中列舉的一些 demo,大家可以試試手,完整的代碼可以去 GitHub 上面去拉取。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看