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

          7 個(gè) JDK 中的坑,千萬(wàn)不要踩!

          共 5208字,需瀏覽 11分鐘

           ·

          2021-06-11 20:14

          作者:Yrion

          來(lái)源:cnblogs.com/wyq178/p/13520745.html

          JDK 作為我們每天必備的調(diào)用類庫(kù),里面大量提供了基礎(chǔ)類供我們使用.可以說(shuō)離開JDK,我們的 Java 代碼寸步難行, JDK 帶給我們的便利可謂是不勝枚舉,但同時(shí)這些方法在使用起來(lái)也存在一些坑,如果不注意就很容易掉入到陷阱里面,導(dǎo)致程序拋出錯(cuò)誤。

          JDK 中的很多方法都不會(huì)做非 null 判斷,可能設(shè)計(jì) JDK 的作者默認(rèn)開發(fā)者已經(jīng)處理好null值了.不過(guò)這個(gè)設(shè)計(jì)可能會(huì)造成很嚴(yán)重的后果,實(shí)在是暗藏殺機(jī)。

          比如今天早上我們查了一筆訂單沒有退款,查了一早上最終才發(fā)現(xiàn)是同事寫的代碼的BigDecimal 的 subtract 方法的值沒有做非 null 判斷處理導(dǎo)致程序拋出了空指針異常,看似簡(jiǎn)單的異常卻直接無(wú)法讓很多訂單退款,是在是小問(wèn)題造成大事故。

          而要修補(bǔ)退款這個(gè)問(wèn)題,要耗費(fèi)很多時(shí)間去修補(bǔ),出錯(cuò)的成本太高,本期我們就來(lái)看看 JDK 中那些坑你沒商量的方法,這些方法很常見,相信你一定遇到過(guò)。

          一. String.valueOf()方法的陷阱

          「案發(fā)現(xiàn)場(chǎng)」:某個(gè)鳥語(yǔ)花香的早上,我們?cè)陂_心的敲著代碼,突然客戶群有人投訴反映,我們發(fā)給用戶的短信有部分是「尊敬的 "null" 你好, xx等」

          開發(fā)第一時(shí)間看了代碼,覺的沒有問(wèn)題啊,為什么短信內(nèi)容會(huì)出現(xiàn)用戶名為null呢,不是經(jīng)過(guò)了非空判斷的嗎?String.valueOf()是String提供的一個(gè)類型轉(zhuǎn)換的方法,我們來(lái)看一下(代碼簡(jiǎn)化過(guò)后的):

          // 調(diào)用用戶服務(wù)根據(jù)用戶id獲取用戶信息
          Map<String, Object> userInfo = userService.getUserInfoById(userId);
          Object userNameObject = userInfo.get("name");
          String userName = String.valueOf(userNameObject);
          // 判空
          if(userName!=null&&userName.length()>0) {
              String message = getMessage(userName);
              smsService.send(message);
          }

          這段代碼是簡(jiǎn)化過(guò)的,主要作用就是通過(guò)用戶服務(wù)根據(jù)id獲取用戶信息發(fā)送短信,后來(lái)經(jīng)過(guò)定位發(fā)現(xiàn)了問(wèn)題所在:首先用戶的名字里有特殊的emoji符號(hào),數(shù)據(jù)庫(kù)寫入的時(shí)候有部分寫入失敗,因?yàn)楫?dāng)時(shí)的

          數(shù)據(jù)庫(kù)字符格式并無(wú)法兼容emoji,而獲取的時(shí)候因?yàn)檫@個(gè)問(wèn)題值為null了,接下來(lái)是重點(diǎn):

          這里是重點(diǎn),也是最大的坑人之處,注意這里返回了一個(gè)"null"的字符串,而不是null。這兩個(gè)是有很大區(qū)別的,當(dāng)進(jìn)行非空判斷的時(shí)候,返回的是ture。也就是這個(gè)"null"的字符串它是符合判空條件的!

          正確的姿勢(shì)是在String.valueOf方法前必須判空:

          二. Integer.parseInt()方法很「矯情」

          事故現(xiàn)場(chǎng):一次業(yè)務(wù)場(chǎng)景為拉取訂單,打出訂單列表記錄,財(cái)務(wù)人員需要拉出對(duì)賬,結(jié)果總是發(fā)現(xiàn)很奇怪的一個(gè)現(xiàn)象,每次拉取少很多數(shù)據(jù)。

          還好財(cái)務(wù)發(fā)現(xiàn)了,要不然和第三方財(cái)務(wù)對(duì)賬就會(huì)虧很多錢...最終發(fā)現(xiàn)是訂單的一個(gè)字段值轉(zhuǎn)Integer出錯(cuò)了,那個(gè)訂單下的字段值是120.0通過(guò)Integer.parseInt()直接報(bào)錯(cuò)了,恰好開發(fā)人員認(rèn)為這段開發(fā)肯定沒問(wèn)題,因此就沒有catch異常,最后找了很久才發(fā)現(xiàn)(因?yàn)樯婕暗降谌?還讓別人查了半天...). 知道真相的我們都有點(diǎn)汗顏,這么丁點(diǎn)的錯(cuò)誤排查了很久,實(shí)在是不應(yīng)該啊。

          Integer.parseInt()方法用于將字符串轉(zhuǎn)化為Integer類型的方法,此方法的適用方向就顯得比較窄,因?yàn)槭荢tring類型的參數(shù),沒有任何限定,當(dāng)在傳入一些比如50.0、20L、30d、40f這類數(shù)據(jù)的情況下,

          我們來(lái)看一個(gè)栗子:

          會(huì)拋出異常NumberFormatException:

          事實(shí)上對(duì)于這樣的數(shù)據(jù),比如小數(shù)、浮點(diǎn)數(shù)據(jù)、long型數(shù)據(jù)它都可以自動(dòng)轉(zhuǎn)換,而不是給我們拋出煩人的報(bào)錯(cuò)信息,如果預(yù)先知道是整數(shù)或者小數(shù),可以用Bigdecimal轉(zhuǎn)換(注意此方法不適用于double和float、Long類型的數(shù)據(jù),比如10d,20L)

          對(duì)于浮點(diǎn)類型、long類型的數(shù)據(jù)可以用以下方法來(lái)處理:

          推薦使用hutool的NumberUtil.parseInt()方法,充分考慮到了浮點(diǎn)、long、小數(shù)等類型數(shù)據(jù)可能帶來(lái)的解析異常的問(wèn)題,hutool是一個(gè)國(guó)人開源的工具類庫(kù),這里實(shí)名推薦,容錯(cuò)性和處理異常能力很強(qiáng),可以自行百度搜索使用。

          三. Bigdecimal的除法坑你沒商量

          眾所周知,BigDecimal是處理金額最有效的數(shù)據(jù)類型,一般進(jìn)行財(cái)務(wù)報(bào)表計(jì)算的時(shí)候?yàn)榱朔乐菇痤~出現(xiàn)錯(cuò)誤,一般情況下都會(huì)采用Bigdecimal,而double、float都會(huì)存在些許的誤差。你開開心心的用Bigdecimal進(jìn)行了計(jì)算,而最終的結(jié)果返回卻有問(wèn)題,我們來(lái)看一個(gè)栗子:

          常見的除法用起來(lái)沒有任何絲毫的問(wèn)題,妥妥的沒毛病.但是一旦程序中的數(shù)據(jù)出現(xiàn)以下情況,如果用Bigdecimal來(lái)接受前端的參數(shù),而前端的參數(shù)是用戶輸入不確定的,一旦出現(xiàn)如下的數(shù)據(jù),我們來(lái)看看結(jié)果:

          執(zhí)行結(jié)果一看,居然報(bào)錯(cuò)了哎:

          這就是BidDecimal的坑,一旦返回的結(jié)果是無(wú)限循環(huán)小數(shù),就會(huì)拋出ArithmeticException。因此在進(jìn)行Bigdecimal除法的時(shí)候,需要進(jìn)行保留小數(shù)的處理,正確的處理姿勢(shì):

          四. Collections.emptyList()此list非彼list

          我們先來(lái)看一個(gè)sample:

          public List<String> getUserNameList(String userId) {
                
                List<String> resultList = Collections.emptyList();
                try {
                    resultList = userDao.getUserName(userId);
                } catch (Exception ex) {
                    logger.info(ex);
                }
                return resultList;
            }

          這樣會(huì)拋出錯(cuò)誤,主要問(wèn)題在于Collections.emptyList()并非我們平時(shí)看到的List,此list不支持add、remove方法,否則會(huì)拋出operationNotSupportException:

          結(jié)果拋出異常:

          原因是:Collections.emptyList返回的并不是我們平時(shí)認(rèn)識(shí)的那個(gè)list,它是一個(gè)內(nèi)部常量類:

          public static final List EMPTY_LIST = new EmptyList<>();

          這個(gè)list并不具有add、remove元素的能力,我猜想是因?yàn)閖dk設(shè)計(jì)之初的想法是將這個(gè)list作為一種只讀的list,并不提供數(shù)據(jù)的寫入能力,因此它僅可作為一種 空值返回,無(wú)法進(jìn)行刪除、添加操作。

          五. list 可以一邊刪除一邊遍歷嗎?

          答案是肯定可以的,要不然的話list怎么刪除數(shù)據(jù)呢,不過(guò)要注意遍歷的姿勢(shì),我們?cè)賮?lái)看一個(gè)簡(jiǎn)單的例子:

          很不幸,又雙叒叕報(bào)錯(cuò)了:


          仔細(xì)翻閱源碼會(huì)發(fā)現(xiàn),每次remove之前會(huì)檢查元素的條數(shù),如果發(fā)現(xiàn)預(yù)期的modCount和當(dāng)前的modCount不一致就會(huì)拋出這個(gè)異常.modCount是list中用來(lái)記錄修改次數(shù)的一個(gè)屬性,當(dāng)對(duì)元素進(jìn)行統(tǒng)計(jì)的時(shí)候就會(huì)對(duì)該元素加1,而當(dāng)對(duì)list邊遍歷邊刪除的話,就會(huì)造成

          excepted與modCount不一致,從而拋出異常。

          正確的刪除姿勢(shì)就是使用Iterator.remove進(jìn)行遍歷刪除,可以規(guī)避這個(gè)問(wèn)題。

          List<Integer> list = new ArrayList<>();
          list.add(1);
          list.add(2);
          list.add(3);
          list.add(4);
          Iterator<Integer> iterator = list.iterator();
          while (iterator.hasNext()) {
              Integer integer = iterator.next();
              if (integer == 2) {
                  iterator.remove();
              }
          }

          六. 總結(jié)

          JDK 的設(shè)計(jì)者有兩個(gè)很大的特點(diǎn):

          \1. 大多不會(huì)做非null判斷

          \2. 出現(xiàn)錯(cuò)誤直接throw new Exception,容錯(cuò)性很差。

          在實(shí)際開發(fā)中,面對(duì)jdk一定要謹(jǐn)慎使用,jdk提供了便利的同時(shí),也有一些我們使用上的盲區(qū),應(yīng)該養(yǎng)成多看源碼,多注意錯(cuò)誤性處理,防止在小問(wèn)題上栽大跟頭。

          回到最開始說(shuō)的那個(gè)subtract方法的問(wèn)題,因?yàn)檫@個(gè)問(wèn)題等需要我處理完之后用戶才能收到退款,這直接造成了用戶體驗(yàn)直線下降,而部分用戶還直接打電話投訴。

          同事一個(gè)小小的不謹(jǐn)慎和馬虎就給公司造成了很多負(fù)面影響, 「技術(shù)問(wèn)題雖然不大但是帶來(lái)的業(yè)務(wù)影響范圍很嚴(yán)重」。所以我們必須防微杜漸,小小的問(wèn)題都得細(xì)細(xì)的打磨,才能避免很多問(wèn)題的產(chǎn)生。

          七. 補(bǔ)充

          「7.1 Bigdecimal在比較的時(shí)候,最好使用compareTo方法,不要使用equals方法」,如下案例,雖然Bigdecimal重寫了equals方法,但是使用會(huì)存在問(wèn)題:

          1和1.0在比較的時(shí)候返回了false,這是因?yàn)樵趀quals的源碼中進(jìn)行了數(shù)據(jù)的scale(也就是精度)的比較,如果不一致就會(huì)返回false,如果使用compareTo方法就不存在這個(gè)問(wèn)題

          「7.2 mysql的減法計(jì)算如果有null值結(jié)果就為null」

          select 5-null  結(jié)果會(huì)返回null,所以在進(jìn)行mysql計(jì)算的時(shí)候,對(duì)于有可能出現(xiàn)null值的列一定要進(jìn)行ifnull(field,0)的轉(zhuǎn)換,將null值轉(zhuǎn)化為0,否則就會(huì)出現(xiàn)一些意想不到的數(shù)據(jù)錯(cuò)誤和空指針問(wèn)題:


          正確的姿勢(shì):


          「7.3.String的split方法在進(jìn)行||分割的時(shí)候需要進(jìn)行轉(zhuǎn)義,否則結(jié)果會(huì)有問(wèn)題」

          推薦閱讀:

          世界的真實(shí)格局分析,地球人類社會(huì)底層運(yùn)行原理

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺(tái)實(shí)踐】華為大數(shù)據(jù)中臺(tái)架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實(shí)施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細(xì)280頁(yè)Docker實(shí)戰(zhàn)文檔!開放下載

          華為大數(shù)據(jù)解決方案(PPT)


          瀏覽 26
          點(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>
                  日本wwwwww | 成人在线做爱 | 无码区一区二区 | 激情4438| ww免费视频 |