<del id="nnjnj"></del><track id="nnjnj"></track>

<p id="nnjnj"></p>

<address id="nnjnj"></address>

    <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

      <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>

      • 自動秒收錄
      • 軟件:1973
      • 資訊:57513|
      • 收錄網站:224722|

      IT精英團

      99%的Java程序員都會踩的6個坑

      99%的Java程序員都會踩的6個坑

      瀏覽次數:
      評論次數:
      編輯: 澤洋
      信息來源: ITPUB
      更新日期: 2022-07-18 17:59:08
      摘要

      前言作為Java程序員的你,不知道有沒有踩過一些基礎知識的坑。有時候,某個bug,你查了半天,最后發現竟然是一個非常低級的錯誤。有時候,某些代碼,這一批數據功能正常,但換了一批數據就出現異常了。有時候

      • 正文開始
      • 相關閱讀
      • 推薦作品

      前言

      作為一個Java程序員,不知道有沒有踩到一些基礎知識坑。

      有時候,你查了很久一個bug,最后發現是很低級的錯誤。

      有時候,在某些代碼中,這一批數據功能正常,但更改一批數據后就異常了。

      有時候,你可能會看著某一行代碼,想知道:為什么這一行代碼出錯了?

      今天就來說說99%的Java程序員都踩過或者即將踩過的六個坑。

      1.坑比==

      不知道大家有沒有在項目里看到過。有同事用==號來比較兩個整型的參數是否相等?

      反正我見過,那么這種用法對嗎?

      我的回答是看具體場景,不能說一定是對或錯。

      一些狀態字段,例如:orderStatus,有五種狀態:-1(未訂購)、0(已訂購)、1(已付款)、2(已完成)、3(已取消)。

      這時,如果用==來判斷它們是否相等:

      integerorderstatus 1=new integer(1);

      integerorderstatus 2=new integer(1);

      System.out.println(訂單狀態1==訂單狀態2);

      結果會是真的嗎?

      答案:假的。

      有的同學可能會反駁,不是有一個緩存的范圍是:-128-127的整數嗎?

      為什么假?

      先看整數的構造方法:

      它實際上不使用緩存。

      那么緩存用在哪里呢?

      在方法的答案值中:

      如果上面的判斷換成這樣:

      stringorderstatus 1=new string(' 1 ');

      stringorderstatus 2=new string(' 1 ');

      system . out . println(integer . value of(order status 1)==integer . value of(order status 2));

      結果會是真的嗎?

      回答:是真的。

      要養成良好的編碼習慣,盡量少用==來判斷兩個整數類型的數據是否相等,只有在上述非常特殊的場景下才相等。

      相反,使用equals方法來判斷:

      integerorderstatus 1=new integer(1);

      integerorderstatus 2=new integer(1);

      System.out.println(訂單狀態1.equals(訂單狀態2));

      結果是真的。

      2.物體的坑。等于

      假設有一個需要判斷當前登錄的用戶,如果是我們指定的系統管理員就發郵件。系統管理員沒有特殊的字段ID,他的用戶id=888,這個值在開發、測試和生產環境中都是一樣的。

      這個要求真的太容易實現了:

      userinfo userinfo=current user . get userinfo();

      if(Objects.isNull(userInfo)){

        log.info("請先登錄");
         return;
      }

      if(Objects.equals(userInfo.getId(),888L)) {
         sendEmail(userInfo):
      }

      從當前登錄用戶的上下文中獲取用戶信息,判斷一下,如果用戶信息為空,則直接返回。

      如果獲取到的用戶信息不為空,接下來判斷用戶id是否等于888。

      • 如果等于888,則發送郵件。
      • 如果不等于888,則啥事也不干。

      當我們用id=888的系統管理員賬號登錄之后,做了相關操作,滿懷期待的準備收郵件的時候,卻發現收了個寂寞。

      后來,發現UserInfo類是這樣定義的:

      @Data
      public class UserInfo {
          private Integer id;
          private String name;
          private Integer age;
          private String address;
      }

      此時,有些小伙伴可能會說:沒看出什么問題呀。

      但我要說的是這個代碼確實有問題。

      什么問題呢?

      下面我們重點看看它的equals方法:

      public static boolean equals(Object a, Object b) {
          return (a == b) || (a != null && a.equals(b));
      }

      equals方法的判斷邏輯如下:

      1. 該方法先判斷對象a和b的引用是否相等,如果相等則直接返回true。
      2. 如果引用不相等,則判斷a是否為空,如果a為空則返回false。
      3. 如果a不為空,調用對象的equals方法進一步判斷值是否相等。

      這就要從Integerequals方法說起來了。

      它的equals方法具體代碼如下:

      public boolean equals(Object obj) {
          if (obj instanceof Integer) {
              return value == ((Integer)obj).intValue();
          }
          return false;
      }

      先判斷參數obj是否是Integer類型,如果不是,則直接返回false。如果是Integer類型,再進一步判斷int值是否相等。

      而上面這個例子中b是long類型,所以Integer的equals方法直接返回了false。

      也就是說,如果調用了Integer的equals方法,必須要求入參也是Integer類型,否則該方法會直接返回false。

      除此之外,還有Byte、Short、Double、Float、Boolean和Character也有類似的equals方法判斷邏輯。

      常見的坑有:

      1. Long類型和Integer類型比較,比如:用戶id的場景。
      2. Byte類型和Integer類型比較,比如:狀態判斷的場景。
      3. Double類型和Integer類型比較,比如:金額為0的判斷場景。

      如果你想進一步了解Objects.equals方法的問題,可以看看我的另一篇文章《Objects.equals有坑》。

      3. BigDecimal的坑

      通常我們會把一些小數類型的字段(比如:金額),定義成BigDecimal,而不是Double,避免丟失精度問題。

      使用Double時可能會有這種場景:

      double amount1 = 0.02;
      double amount2 = 0.03;
      System.out.println(amount2 - amount1);

      正常情況下預計amount2 - amount1應該等于0.01

      但是執行結果,卻為:

      .009999999999999998

      實際結果小于預計結果。

      Double類型的兩個參數相減會轉換成二進制,因為Double有效位數為16位這就會出現存儲小數位數不夠的情況,這種情況下就會出現誤差。

      常識告訴我們使用BigDecimal能避免丟失精度。

      但是使用BigDecimal能避免丟失精度嗎?

      答案是否定的。

      為什么?

      BigDecimal amount1 = new BigDecimal(0.02);
      BigDecimal amount2 = new BigDecimal(0.03);
      System.out.println(amount2.subtract(amount1));

      這個例子中定義了兩個BigDecimal類型參數,使用構造函數初始化數據,然后打印兩個參數相減后的值。

      結果:

      .0099999999999999984734433411404097569175064563751220703125

      不科學呀,為啥還是丟失精度了?

      JdkBigDecimal構造方法上有這樣一段描述:

      大致的意思是此構造函數的結果可能不可預測,可能會出現創建時為0.1,但實際是0.1000000000000000055511151231257827021181583404541015625的情況。

      由此可見,使用BigDecimal構造函數初始化對象,也會丟失精度。

      那么,如何才能不丟失精度呢?

      BigDecimal amount1 = new BigDecimal(Double.toString(0.02));
      BigDecimal amount2 = new BigDecimal(Double.toString(0.03));
      System.out.println(amount2.subtract(amount1));

      我們可以使用Double.toString方法,對double類型的小數進行轉換,這樣能保證精度不丟失。

      其實,還有更好的辦法:

      BigDecimal amount1 = BigDecimal.valueOf(0.02);
      BigDecimal amount2 = BigDecimal.valueOf(0.03);
      System.out.println(amount2.subtract(amount1));

      使用BigDecimal.valueOf方法初始化BigDecimal類型參數,也能保證精度不丟失。在新版的阿里巴巴開發手冊中,也推薦使用這種方式創建BigDecimal參數。

      4. Java8 filter的坑

      對于Java8中的Stream用法,大家肯定再熟悉不過了。

      我們通過對集合Stream操作,可以實現:遍歷集合、過濾數據、排序、判斷、轉換集合等等,N多功能。

      這里重點說說數據的過濾。

      在沒有Java8之前,我們過濾數據一般是這樣做的:

      public List<User> filterUser(List<User> userList) {
          if(CollectionUtils.isEmpty(userList)) {
              return Collections.emptyList();
          }
          
          List<User> resultList = Lists.newArrayList();
          for(User user: userList) {
              if(user.getId() > 1000 && user.getAge() > 18)   {
                 resultList.add(user);
              }
          }
          return resultList;
      }

      通常需要另一個集合輔助完成這個功能。

      但如果使用Java8的filter功能,代碼會變得簡潔很多,例如:

      public List<User> filterUser(List<User> userList) {
          if(CollectionUtils.isEmpty(userList)) {
              return Collections.emptyList();
          }
          
          return userList.stream()
          .filter(user -> user.getId() > 1000 && user.getAge() > 18)
          .collect(Collectors.toList());
      }

      代碼簡化了很多,完美。

      但如果你對過濾后的數據,做修改了:

      List<User> userList = queryUser();
      List<User> filterList = filterUser(userList);
      for(User user: filterList) {
         user.setName(user.getName() + "測試");
      }

      for(User user: userList) {
         System.out.println(user.getName());
      }

      你當時可能只是想修改過濾后的數據,但實際上,你會把元素數據一同修改了。

      意不意外,驚不驚喜?

      其根本原因是:過濾后的集合中,保存的是對象的引用,該引用只有一份數據。

      也就是說,只要有一個地方,把該引用對象的成員變量的值,做修改了,其他地方也會同步修改。

      如下圖所示:

      5. 自動拆箱的坑

      Java5之后,提供了自動裝箱自動拆箱的功能。

      自動裝箱是指:JDK會把基本類型,自動變成包裝類型。

      比如:

      Integer integer = 1;

      等價于:

      Integer integer = new Integer(1);

      而自動拆箱是指:JDK會把包裝類型,自動轉換成基本類型。

      例如:

      Integer integer = new Integer(2);
      int sum = integer + 5;

      等價于:

      Integer integer = new Integer(2);
      int sum = integer.intValue() + 5;

      但實際工作中,我們在使用自動拆箱時,往往忘記了判空,導致出現NullPointerException異常。

      5.1 運算

      很多時候,我們需要對傳入的數據進行計算,例如:

      public class Test2 {
          public static void main(String[] args) {
              System.out.println(add(new Integer(1), new Integer(2)));
          }

          private static Integer add(Integer a, Integer b) {
              return a + b;
          }
      }

      如果傳入了null值:

      System.out.println(add(null, new Integer(2)));

      則會直接報錯。

      5.2 傳參

      有時候,我們定義的某個方法是基本類型,但實際上傳入了包裝類,比如:

      public static void main(String[] args) {
          Integer a = new Integer(1);
          Integer b = null;
          System.out.println(add(a, b));
      }

      private static Integer add(int a, int b) {
          return a + b;
      }

      如果出現add方法報NullPointerException異常,你可能會懵逼,int類型怎么會出現空指針異常呢?

      其實,這個問題出在:Integer類型的參數,其實際傳入值為null,JDK字段拆箱,調用了它的intValue方法導致的問題。

      6. replace的坑

      很多時候我們在使用字符串時,想把字符串比如:ATYSDFA*Y中的字符A替換成字符B,第一個想到的可能是使用replace方法。

      如果想把所有的A都替換成B,很顯然可以用replaceAll方法,因為非常直觀,光從方法名就能猜出它的用途。

      那么問題來了:replace方法會替換所有匹配字符嗎?

      jdk的官方給出了答案。

      該方法會替換每一個匹配的字符串。

      既然replace和replaceAll都能替換所有匹配字符,那么他們有啥區別呢?

      replace有兩個重載的方法。

      • 其中一個方法的參數:char oldChar 和 char newChar,支持字符的替換。
      source.replace('A', 'B')
      • 另一個方法的參數是:CharSequence target 和 CharSequence replacement,支持字符串的替換。
      source.replace("A", "B")

      replaceAll方法的參數是:String regex 和 String replacement,即基于正則表達式的替換。

      例如對普通字符串進行替換:

      source.replaceAll("A", "B")

      使用正則表達替換(將*替換成C):

      source.replaceAll("\\*", "C")

      順便說一下,將*替換成C使用replace方法也可以實現:

      source.replace("*", "C")

      小伙們看到看到二者的區別了沒?使用replace方法無需對特殊字符進行轉義。

      不過,千萬注意,切勿使用如下寫法:

      source.replace("\\*", "C")

      這種寫法會導致字符串無法替換。

      還有個小問題,如果我只想替換第一個匹配的字符串該怎么辦?

      這時可以使用replaceFirst方法:

      source.replaceFirst("A", "B")

      說實話,這里內容都很基礎,但越基礎的東西,越容易大意失荊州,更容易踩坑。

      最后,統計一下,這些坑一個都沒踩過的同學,麻煩舉個手。

      以上文章來源于公眾號-蘇三說技術 ,作者蘇三呀  

      標簽:方法 類型 數據
      Linux網絡基礎和性能優化
      ? 上一篇 2022-07-18
      • Linux網絡基礎和性能優化
        0閱讀 0條評論 個贊
        網絡模型:常見的有OSI網絡模型、TCP/IP網絡模型、五層協議網絡模型:OSI七層網絡模型:將傳統的五層TCP模型中的應用層分為應用層、表示層、會話層應用層:負責為應用程序提供統一的接口……
      • K8S中的開發和運維對應用做了什么?
        0閱讀 0條評論 個贊
        在應用的整個生命周期里,開發和運維都和它密不可分。一個塑造它,一個保養它。如果應用需要部署到K8S中,開發和運維在其中都做了什么呢?開發側從開發側來說,我們的應用應該具備以下能力:具有健康檢測接口具有……
      • 大廠大數據平臺核心架構全層面圖解
        0閱讀 0條評論 個贊
        我們先來看看這張圖,這是某公司使用的大數據平臺架構圖,大部分公司應該都差不多:從這張大數據的整體架構圖上看來,大數據的核心層應該是:數據采集層、數據存儲與分析層、數據共享層、數據應用層,可能叫法有所不……
      • 微軟努力開發Linux 從紅帽挖走Systemd作者
        0閱讀 0條評論 個贊
        科技媒體Phoronix消息稱,Systemd的首席開發人員LennartPoettering已經離開紅帽并加入了微軟,以繼續他在systemd方面的工作。Lennart在紅帽工作了1……
      • 記住一個在線RPC超時故障排除和后續的GC調優想法
        1閱讀 0條評論 個贊
        本文記錄了一次線上RPC服務調用超時問題的排查流程,排查過程中涉及到JVM優化的過程與思路,包括JVMGC原理以及問題排查思路,分享出來希望對大家有所幫助。本文概要RPC服務異常和排查過程排……
      發表評論 共有條評論
      用戶名: 密碼:
      驗證碼: 匿名發表
      • 都是條件相同的mysql select語句 為什么他們讀的東西不一樣?
        0閱讀 0條評論 個贊
        以下文章來源于公眾號-小白debug,作者小白假設當前數據庫里有下面這張表。user表數據庫原始狀態老規矩,以下內容還是默認發生在innodb引擎的可重復讀隔離級別下。都是select結果卻不同大家可……
      • 為實戰構建數據倉庫指標體系!
        0閱讀 0條評論 個贊
        作者:曹雷來源:滴滴技術01指標體系1.痛點分析主要從業務、技術、產品三個視角來看:業務視角業務分析場景指標、維度不明確;頻繁的需求變更和反復迭代,數據報表臃腫,數據參差不齊;用戶分析具體業務問題找……
      • 如何構建自己的可引導Linux Live CD
        11閱讀 0條評論 個贊
        使用第三方工具或官方Linux操作系統,在Linux中創建DIY可啟動LiveCD很容易。需要幫忙?按照以下步驟在幾分鐘內創建LinuxLiveCD。LiveCD(或“liv……
      • 關于Linux之父你不知道的6件事
        0閱讀 0條評論 個贊
        如果讓你現在說出三個程序員的名字,Linus很可能就在其中。作為世界上最著名的電腦程序員、黑客之一,LinusBenedictTorvalds寫出了Linux內核1.0版,發起了開源運……
      • 用于自動監控磁盤使用的Linux —— Shell腳本
        0閱讀 0條評論 個贊
        如果在服務器上運行關鍵任務,那么監控和通知管理員磁盤使用情況很重要。本文介紹編寫一個腳本來自動監控并在達到閾值時將報告發送到自己的郵箱。在文章中,我們寫一個shell腳本,它在crontab中……
      • Docker入門終極指南 詳細版!別再說不會用Docker了!
        0閱讀 0條評論 個贊
        來源:jartto.wang/2020/07/04/learn-docker/富Web時代,應用變得越來越強大,與此同時也越來越復雜。集群部署、隔離環境、灰度發布以及動態擴容缺一不可,而容器化則成為中……
      • 一萬字 30張圖詳細講解操作系統總結!
        0閱讀 0條評論 個贊
        一、概述基本特征1.并發并發是指宏觀上在一段時間內能同時運行多個程序,而并行則指同一時刻能運行多個指令。并行需要硬件支持,如多流水線、多核處理器或者分布式計算系統。操作系統通過引入進程和線程,使得程……
      • Linux受開發者青睞的9大理由!
        0閱讀 0條評論 個贊
        編譯|張仕影出品|CSDN(ID:CSDNnews)Linux在GNU通用公共許可證(GPL)的推動下開發和發布,是一個可以免費使用的開源操作系統。使用者只要遵守許可條款,就可以運行、研……
      • Linux Foundation 3354IO全面介紹
        0閱讀 0條評論 個贊
        Linux-基礎IOLinux-基礎IO文件IO相關操作stdin&stdout&stderr系統文件I/O文件的宏觀理解:狹義理解:1.文件在磁盤里2.磁盤是永久性存儲介質……
      • Linux網絡基礎和性能優化
        0閱讀 0條評論 個贊
        網絡模型:常見的有OSI網絡模型、TCP/IP網絡模型、五層協議網絡模型:OSI七層網絡模型:將傳統的五層TCP模型中的應用層分為應用層、表示層、會話層應用層:負責為應用程序提供統一的接口……
      • 數據科學中10個重要概念和圖表的意義
        0閱讀 0條評論 個贊
        來源:DeepHubIMBA本文共1200字,建議閱讀5分鐘“當算法給你一條曲線時,一定要知道這個曲線的含義!”1、偏差-方差權衡這是一個總是在機器學習最重要理論中名列前茅的概念。機器學習中的幾乎所……
      • 舉例說明Kafka生產者初始化的核心過程
        0閱讀 0條評論 個贊
        今天這篇我們先來聊聊Kafka生產者初始化時用到的核心組件以及發送的核心流程,帶你梳理生產者初始化整體的源碼分析脈絡。認真讀完這篇文章,我相信你會對Kafka生產初始化源碼有更加深刻的理解。這……
      • 大數據項目可能出錯的五種方式
        0閱讀 0條評論 個贊
        我的大數據項目沒有按照我想要的方式進行?提出這個疑問的絕對不止你一個人。大數據項目的低成功率是過去10年中一個持續存在的問題,與之類似的是:人工智能項目中也出現了相同類型的問題。雖然100%的成功率不……
      • 全面提高數據檢索效率的12個黃金策略
        0閱讀 0條評論 個贊
        以下文章來源于公眾號-大魚的數據人生,作者討厭的大魚先生有朋友困惑于團隊報表取數太多的問題,跟我來探討提升的方法,這其實是大多數據團隊都會碰到的問題,原因無外乎以下幾個:第一、市場變化很快,取數要跟著……
      • C語言為什么不檢查數組下標?
        0閱讀 0條評論 個贊
        引言最近在查一個bug,查到最后發現是數組越界導致的。數組只有30個字節,代碼卻向這個數組填充了35個數據,這個bug還是偶現的,查到它確實廢了一番功夫。我就突然想到:C語言為什么不檢查數組下標呢??……
      • 大廠企業系列倉庫建設規劃(整理需要一周時間 建議收藏~)
        1閱讀 0條評論 個贊
        以下文章來源于公眾號-大數據兵工廠,作者大數據兵工廠大家好,推薦一下老兵抽空整理了企業級數倉建設方案。整體內容包括:數倉架構數倉分層規劃數據流向規劃數據同步策略數倉命名規范通篇內容緊貼企業級建設主題,……
      • 4年工作經驗 不能告訴你多線程之間的5種通信方式 你能相信嗎?
        0閱讀 0條評論 個贊
        問題有兩個線程,A線程向一個集合里面依次添加元素“abc”字符串,一共添加十次,當添加到第五次的時候,希望B線程能夠收到A線程的通知,然后B線程執行相關的業務操作。線程間通信的模型有兩種……
      • 微軟努力開發Linux 從紅帽挖走Systemd作者
        0閱讀 0條評論 個贊
        科技媒體Phoronix消息稱,Systemd的首席開發人員LennartPoettering已經離開紅帽并加入了微軟,以繼續他在systemd方面的工作。Lennart在紅帽工作了1……
      • 20個實用的以打字打的文件單行代碼匯總
        0閱讀 0條評論 個贊
        來源|https://blog.bitsrc.io/another-10-quick-typescript-one-liners-9f41713c158a在今天的文章中,我將與你分享20有用的T……
      • 整頓00后職場?成立了“00后部門”
        0閱讀 0條評論 個贊
        整理|于軒出品|程序人生(ID:coder_life)據教育部統計,2022屆高校應屆畢業生人數高達1076萬。同時,今年也是00后的第一個畢業季。隨著大批00后涌入職場,作為職場新人的他們會有……
      最近發布資訊
      更多
      警花高潮嗷嗷叫
      <del id="nnjnj"></del><track id="nnjnj"></track>

      <p id="nnjnj"></p>

      <address id="nnjnj"></address>

        <pre id="nnjnj"><pre id="nnjnj"></pre></pre>

          <noframes id="nnjnj"><ruby id="nnjnj"><ruby id="nnjnj"></ruby></ruby>