今天維護又反饋了一問題過來,查詢試卷時報數組越界異常:
1 2017-02-28 10:45:24,827[ERROR] HttpException[10.32.111.7:60446:2D07867BE98F56D5EFFA1B1A597776AC]:/WAserver/HS851020 [com.hundsun.hsacct.core.httpresult.HandlerExceptionResolver.printHttpLog(HandlerExceptionResolver.java:156)] 2 java.lang.ArrayIndexOutOfBoundsException: 1 3 at com.hundsun.hsacct.action.paper.QryEligPaperAction.qryEligPaper(QryEligPaperAction.java:69) 4 at sun.reflect.GeneratedMethodAccessor2434.invoke(Unknown Source) 5 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 6 at java.lang.reflect.Method.invoke(Method.java:606)
看了一下開發人員寫的代碼,其中有兩行用逗號來作為分隔符來分割字符串,基本上已經知道了問題原因。這時,心里默默想起了墨菲定律:任何事情只要存在出錯的可能性,那最后肯定會出錯,沒出錯只是因為時機未到。大師的理論果然厲害
String answer_content = map.get("answer_content"); if (StringUtils.isNotBlank(answer_content)) { //第一次分割,分隔符 ',' String[] stepOne = answer_content.split(","); Map<String,String> answer = new HashMap<String,String>(); for(int i = 0; i<stepOne.length; i++) { //第二次分割,分隔符'、' String[] stepTwo = stepOne[i].split("、"); //向HashMap中添加 if(answer.get(stepTwo[0])==null){ answer.put(stepTwo[0], stepTwo[1]); } else{ answer.put(stepTwo[0], stepTwo[1]+","+answer.get(stepTwo[0])); } } paper.put(Fields.ANSWER_CONTENT, answer); } else { paper.put(Fields.ANSWER_CONTENT, ""); }
其實之前審核代碼的時候無意瞟了一眼這里,隱約感覺這段代碼里面有坑,但是測試那邊沒測出問題,加上這個接口業務邏輯簡單,所以也沒當回事,結果最后快上生產了,問題還是暴漏出來了。
問題出來了,除了覺得開發人員經驗不足,還有更多的是對自己的自責,畢竟開發寫的代碼是通過了自己的審核,為什么輕易把問題放過去了呢?
在這里對問題做一下深入分析:
IndexOutOfBoundsException(數組越界異常) 可以說是Java代碼中最常見的異常之一,還有一個是NullPointerException(空指針異常)。出現的原因:對不可控的String進行split操作然后訪問其中的數據是最常見的原因。 什么叫“不可控String”:來自於我們自己的程序之外的String。什么是“可控String”:來自於我們系統內部的String,比如我們代碼里面定義的常量,我們系統使用的格式固定的配置文件等等。
類似的還有StringIndexOutOfBoundsException,當你對一個長度不夠的字符串進行substring操作就會拋出該異常。出現該異常的原因,還是在於使用不可控String。
作為一個有經驗的程序員,當看到String.split()還有String.substring()這種代碼時,一定要小心謹慎。很有可能這里已經埋下了一顆定時炸彈,暫時沒出現問題只是因為時機未到。
比較好的處理方式:當我們必須要對不可控String進行split()還有substring()操作時,應當進行一下檢查。如果字符串不符合格式要求,我們應當主動拋出異常,讓系統維護人員第一時間知道當前系統不滿足運行條件,需要進行檢修,從而讓故障損失降到最低。