<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>

          包裝類(lèi)這顆語(yǔ)法糖,其實(shí)并不甜

          共 5972字,需瀏覽 12分鐘

           ·

          2022-06-25 12:30

          往期熱門(mén)文章:

          1、換掉UUID,NanoID更快更安全!
          2、新來(lái)的后端整的接口文檔看著真不優(yōu)雅!
          3、如何優(yōu)雅的寫(xiě) Controller 層代碼?
          4、SpringBoot+Nacos+Kafka簡(jiǎn)單實(shí)現(xiàn)微服務(wù)流編排
          5、幾行代碼,搞定 SpringBoot 接口惡意刷新和暴力請(qǐng)求!

          包裝類(lèi)在Java 5中和泛型一起引入,引入包裝類(lèi)的原因有兩點(diǎn):
          1. 解決無(wú)法創(chuàng)建基本類(lèi)型泛型集合的問(wèn)題
          2. 加入對(duì)基本類(lèi)型為null這個(gè)語(yǔ)義的支持
          并提供boxingunboxing的語(yǔ)法糖,讓編譯器支持基本類(lèi)型和包裝類(lèi)的自動(dòng)轉(zhuǎn)化,減少開(kāi)發(fā)者的工作量。但是經(jīng)常有同學(xué)因?yàn)檎`用包裝類(lèi)導(dǎo)致慘烈的線上問(wèn)題,在使用包裝類(lèi)的時(shí)候務(wù)必需要注意以下四點(diǎn):
          1. 與基礎(chǔ)類(lèi)截然不同的==equals語(yǔ)義
          2. 糟糕的性能
          3. 不易察覺(jué)的NPE問(wèn)題
          4. 令人疑惑的API設(shè)計(jì)

          1. 相等還是不相等?這是個(gè)問(wèn)題

          比如以下代碼片段
          class Biziclop {
              public static void main(String[] args) {
                  System.out.println(new Integer(5) == new Integer(5)); // false
                  System.out.println(new Integer(500) == new Integer(500)); // false

                  System.out.println(Integer.valueOf(5) == Integer.valueOf(5)); // true
                  System.out.println(Integer.valueOf(500) == Integer.valueOf(500)); // false
              }
          }
          第一個(gè)和第二個(gè)語(yǔ)句返回false是比較容易理解的,因?yàn)閷?duì)于Java中的對(duì)象調(diào)用=其實(shí)是在比較對(duì)象在堆上的地址,由于兩個(gè)對(duì)象都是新建的,所以地址肯定不等,返回false。比較令人疑惑的是第三個(gè)語(yǔ)句,按照我們前面的分析,應(yīng)該也返回false才對(duì),但其實(shí)Integer.valueOf(5) == Integer.valueOf(5)比較的結(jié)果是true,這是因?yàn)?/span>JVM緩存了-128-127的整數(shù),所以當(dāng)數(shù)值在這個(gè)區(qū)間的時(shí)候,返回的對(duì)象都是同一個(gè)的。第四個(gè)語(yǔ)句因?yàn)閿?shù)值已經(jīng)不在-128-127的區(qū)間范圍,所以返回了false。
          上面的這幾個(gè)例子都是比較經(jīng)典的例子,大家比較熟悉,一般也比較難掉坑里,但是下面的幾個(gè)例子就比較有迷惑性了
          class Biziclop {
               public static void main(String[] args) {
                  List<Long> list = new ArrayList<>();
                  list.add(Long.valueOf(200));
                  System.out.println(list.contains(200)); // false
                  
                  Long temp = 0L;
              System.out.println(temp.equals(0)); // false
                 System.out.orintln(0==0L); // true
           }
          }
          原因在于
          public boolean equals(Object obj) {
              if (obj instanceof Long) {
                return value == ((Long)obj).longValue();
              }
              return false;
          }
          包裝類(lèi)重寫(xiě)了equals方法,導(dǎo)致包裝類(lèi)即便是調(diào)用equals方法比較大小,也會(huì)和基本類(lèi)型出現(xiàn)不一致的結(jié)果。與基礎(chǔ)類(lèi)截然不同的==equals語(yǔ)義經(jīng)常會(huì)導(dǎo)致代碼走到非期望的分支,再配上JVM對(duì)數(shù)字獨(dú)特的緩存策略,極容易出現(xiàn)測(cè)試環(huán)境和正式環(huán)境不一樣的運(yùn)行結(jié)果。

          2. 糟糕的性能

          Effective Java》中有如下的例子:
          public static void main(String[] args) {
              Long sum = 0L// uses Long, not long
              for (long i = 0; i <= Integer.MAX_VALUE; i++) {
                  sum += i;
              }
              System.out.println(sum);
          }
          這段代碼的耗時(shí)比使用基本類(lèi)型long的版本慢6倍(聲明變量sum 類(lèi)型為 Long 的耗時(shí)是43秒, 如果聲明變量sum為基本類(lèi)型long,則耗時(shí)6.8秒)。導(dǎo)致這樣的原因是包裝類(lèi)要經(jīng)過(guò)在堆上開(kāi)辟內(nèi)存空間,初始化,內(nèi)存尋址以及數(shù)據(jù)載入寄存器的過(guò)程,性能差也就不足為奇了。因此Joshua Bloch對(duì)開(kāi)發(fā)者的建議是:Avoid creating unnecessary objects.
          在經(jīng)典的JMH workbench上跑的包裝類(lèi)和基礎(chǔ)類(lèi)性能對(duì)比如下圖所示:

          可以看到與基礎(chǔ)類(lèi)相比,包裝類(lèi)普遍要慢不少。
          圖片地址:https://www.baeldung.com/java-primitives-vs-objects

          3. 不易察覺(jué)的NPE

          不同于基本類(lèi)型,作為對(duì)象的包裝類(lèi)是可能為null的,這就意味著一個(gè)指向null的包裝類(lèi)unboxing的時(shí)候會(huì)拋出NPE異常,比如以下代碼:
          Integer in = null;
          ...
          ...
          int i = in; // NPE at runtime
          這段代碼也是比較明顯的,但是如果包裝類(lèi)遇到三元運(yùn)算符,則會(huì)出現(xiàn)更復(fù)雜的NPE
          class Biziclop {
              public static void main(String[] args) {
                Boolean b = true ? returnsNull() : false// NPE on this line.
                System.out.println(b);
              }
             public static Boolean returnsNull() {
               return null;
            }
          }
          這跟Java中三元運(yùn)算符類(lèi)型的判定有關(guān)系,有一條判定規(guī)則是,
          如果三元運(yùn)算符的第二個(gè)或者第三個(gè)參數(shù)是基本類(lèi)型T,并且另一個(gè)是相應(yīng)的包裝類(lèi)型的話(huà),那么三元運(yùn)算符的返回類(lèi)型就是這個(gè)基本類(lèi)型T
          所以在上面的代碼中,returnsNull的返回值還要進(jìn)行一次unboxing,因此拋出了NPE.

          4. 令人疑惑的API

          Long這個(gè)類(lèi)中,有一個(gè)apigetLong,其聲明如下:

              /**
               * Determines the {@code long} value of the system property
               * with the specified name.
               */

              public static Long getLong(String nm) {
                  return getLong(nm, null);
              }
          這個(gè)api的作用是獲取JVM中的屬性值的,并且轉(zhuǎn)換為Long 類(lèi)型,比如:
          class Biziclop {
               public static void main(String[] args) {
                  System.setProperty("22""22");
                  System.setProperty("23""hello world!");
                  System.out.println(Long.getLong("22")); // 22
                  System.out.println(Long.getLong("23")); // null
                  System.out.println(Long.getLong("24")); // null
            }
          }
          這個(gè)api的設(shè)計(jì)妥妥是一個(gè)反例,經(jīng)常有同學(xué)誤用,把它當(dāng)成Long.valueOf或者是Long.parseLong,結(jié)果返回不符合期望的值。

          5. 最佳實(shí)踐

          《阿里巴巴Java編程手冊(cè)》對(duì)包裝類(lèi)的使用有以下三條建議:
          1. 所有POJO類(lèi)屬性使用包裝類(lèi)
          2. RPC方法的返回值和參數(shù)使用包裝類(lèi)
          3. 所有的局部變量使用基本數(shù)據(jù)類(lèi)型
          說(shuō)明:POJO類(lèi)屬性沒(méi)有初值是提醒使用在需要使用時(shí),必須自己顯式的進(jìn)行賦值,任何NPE問(wèn)題,或者入庫(kù)檢查,都有使用者來(lái)保證。
          正例:數(shù)據(jù)庫(kù)的查詢(xún)結(jié)果可能是null,因?yàn)樽詣?dòng)拆箱,用基本數(shù)據(jù)類(lèi)型接受有NPE的風(fēng)險(xiǎn)
          反例:某業(yè)務(wù)的交易報(bào)表上顯示成交額漲跌情況,即x%,x為基本數(shù)據(jù)類(lèi)型,調(diào)用的HSF服務(wù),調(diào)用不成功時(shí),返回的是默認(rèn)值,頁(yè)面展示0%,這是不合理的,應(yīng)該展示成中劃線-,所以包裝類(lèi)的null值,能夠表示額外的信息,如:遠(yuǎn)程調(diào)用失敗,異常退出。
          往期熱門(mén)文章:

          1、MySQL 暴跌!
          2、超越 Xshell!號(hào)稱(chēng)下一代 Terminal 終端神器,用完愛(ài)不釋手!
          3、IDEA 官宣全新默認(rèn) UI,太震撼了!!
          4、讓你直呼「臥槽」的 GitHub 項(xiàng)目!
          5、Kafka又笨又重,為啥不選Redis?
          6、50多個(gè)高頻免費(fèi) API 接口分享
          7、IDEA公司再發(fā)新神器!超越 VS Code 騷操作!
          8、我懷疑這是 IDEA 的 BUG,但是我翻遍全網(wǎng)沒(méi)找到證據(jù)!
          9、Spring MVC 中的 Controller 是線程安全的嗎?
          10、Gitee 倒下了???

          瀏覽 23
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  网络红人思瑞视频在线观看 | 啊v在线色 | 日韩精品视频在线观看免费 | 大吊操色逼 | 国产乱码一区 |