無聊系列 – 教你怎麼正確處理異常

在工作中,常遇見亂處理Exception的情況:

  1. 要麼吞掉異常,不打印任何日誌;
  2. 要麼記錄日誌時,日誌級別不對、或者把重要的出錯堆棧信息幹掉,在做生產問題排查時,簡直讓人抓狂。

我這篇博文,也是對記錄的一個開源組件,對異常自行K掉,造成我排查耗費了好久的時間–。https://www.cnblogs.com/chongsha/p/11931109.html

下面我們用一段代碼對1進行舉例,該代碼是網上隨便搜的,原作者請勿見怪。

 1  /*   2    * 加密   3    * 1.構造密鑰生成器   4    * 2.根據ecnodeRules規則初始化密鑰生成器   5    * 3.產生密鑰   6    * 4.創建和初始化密碼器   7    * 5.內容加密   8    * 6.返回字符串   9    */  10     public static String AESEncode(String encodeRules,String content){  11         try {  12             //1.構造密鑰生成器,指定為AES算法,不區分大小寫  13             KeyGenerator keygen=KeyGenerator.getInstance("AES");  14             //2.根據ecnodeRules規則初始化密鑰生成器  15             //生成一個128位的隨機源,根據傳入的位元組數組  16             keygen.init(128, new SecureRandom(encodeRules.getBytes()));  17               //3.產生原始對稱密鑰  18             SecretKey original_key=keygen.generateKey();  19               //4.獲得原始對稱密鑰的位元組數組  20             byte [] raw=original_key.getEncoded();  21             //5.根據位元組數組生成AES密鑰  22             SecretKey key=new SecretKeySpec(raw, "AES");  23               //6.根據指定算法AES自成密碼器  24             Cipher cipher=Cipher.getInstance("AES");  25               //7.初始化密碼器,第一個參數為加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二個參數為使用的KEY  26             cipher.init(Cipher.ENCRYPT_MODE, key);  27             //8.獲取加密內容的位元組數組(這裡要設置為utf-8)不然內容中如果有中文和英文混合中文就會解密為亂碼  28             byte [] byte_encode=content.getBytes("utf-8");  29             //9.根據密碼器的初始化方式--加密:將數據加密  30             byte [] byte_AES=cipher.doFinal(byte_encode);  31           //10.將加密後的數據轉換為字符串  32             //這裡用Base64Encoder中會找不到包  33             //解決辦法:  34             //在項目的Build path中先移除JRE System Library,再添加庫JRE System Library,重新編譯後就一切正常了。  35             String AES_encode=new String(new BASE64Encoder().encode(byte_AES));  36           //11.將字符串返回  37             return AES_encode;  38         } catch (NoSuchAlgorithmException e) {  39             e.printStackTrace();  40         } catch (NoSuchPaddingException e) {  41             e.printStackTrace();  42         } catch (InvalidKeyException e) {  43             e.printStackTrace();  44         } catch (IllegalBlockSizeException e) {  45             e.printStackTrace();  46         } catch (BadPaddingException e) {  47             e.printStackTrace();  48         } catch (UnsupportedEncodingException e) {  49             e.printStackTrace();  50         }  51  52         //如果有錯就返加nulll  53         return null;  54     }

該段代碼主要的問題是:

  1. 吃掉了異常,因為是公共類,連日誌記錄都沒有
  2. 出現異常後,仍然返回了一個null值。

這個方法在我們平時使用時,如果不讀源碼,直接使用,第一直覺是,返回正確結果,如果不正確,那就會拋出異常。但是這段代碼卻返回了null,使用者遇到時,會抓狂,這是什麼情況啊,為啥不對,明明沒有報錯,萬般無奈,進代碼一看。。。原來是把異常給幹掉了。

對此代碼做出的改進建議是:

  1. 在方法上聲明throws是
  2. 如果你覺得1方案不爽,可以直接一個大的catch Exception,然後throw new RuntimeException(e.getMessage(), e);
  3. 出錯了就是出錯了,不能把錯誤自己幹掉,然後返回一個null。

要麼記錄日誌時,日誌級別不對、或者把重要的出錯堆棧信息幹掉,在做生產問題排查時,簡直讓人抓狂。

在用log4j記錄日誌時,請正確使用logger.error()來記錄日誌,請注意該方法的重載,不要使用 Exception的getMessage()方法只記錄異常的消息,而把異常的錯誤堆棧給拋棄,異常的錯誤堆棧是很有用的信息,會告訴你在哪行代碼出錯了,這樣你可以快速的定位錯誤。

 1 package com.demo;   2   3 public class Test {   4   5     public static void main(String[] args) {   6         try {   7             int a = 0;   8             int b = 1;   9  10             System.out.println(b / a);  11         } catch (Exception e) {  12             e.printStackTrace();  13         }  14     }  15  16 }

 

這段代碼的錯誤堆棧信息:

java.lang.ArithmeticException: / by zero
at com.demo.Test.main(Test.java:10)

這行錯誤信息at com.demo.Test.main(Test.java:10)標明了出錯位置,可以快速定位是在什麼地方。所以在記錄日誌的時候,請不要把錯誤堆棧信息幹掉了。