之前一直想写博客,因为各种各样的原因未能实践,苦恼万分,现在刚刚进入2016,新的一年给自己定好了计划,其中之一便是每周至少写一篇的博客,记录自己遇到的问题,解决问题的思路,总结与反思。
先写下最近这两天项目里面遇到的问题,是与异常有关的。首先获取数据库里面的某个字段值,它是某种特定格式的字符串,按照约定的格式进行解析,然后返回相对于的中文含义,结果是现在出现了一种和之前约定不同的格式字符串,导致解析该字符串的方法抛出了异常,导致了整个接口服务都返回失败。
下面是出现问题的代码:
public static MapgetPlayCrowdMap(String playCrowd) { if (null == playCrowd || playCrowd.trim().length() == 0) { return null; } Map map = new HashMap (); String[] ss = playCrowd.split("\\|"); if (null != ss && ss.length > 0) { for (String str : ss) { StringBuffer sb = new StringBuffer(); String[] splitArray = str.split(";"); if (null != splitArray && splitArray.length > 0) { Integer type = Integer.parseInt(splitArray[0]); String typeName = ""; switch (type) { case 1: typeName = "成人"; break; case 2: typeName = "儿童"; break; case 3: typeName = "婴儿"; break; case 4: typeName = "学生"; break; case 5: typeName = "老人"; break; default: break; } sb.append(typeName); // 起始年龄 if (splitArray[1].trim().length() != 0) { sb.append("(").append(splitArray[1].trim()).append("岁"); } // 最大年龄 if (splitArray[2].trim().length() != 0) { sb.append("-").append(splitArray[2].trim()).append("岁").append(")"); } } map.put(str, sb.toString()); sb = null; } } return map; }
那这段代码 可能会出现什么问题?
1.数组splitArray ,在获取起始年龄和最大年龄的代码中,没有对数组splitArray 的大小做判定,若此时splitArray 的大小只为1,那splitArray[1]和splitArray[2],就会出现 IndexOutOfBoundsException 数组越界异常,这个是由于逻辑不严谨导致的。
2.整个方法getPlayCrowdMap(String str),并没有对非受检异常(运行时异常)做任何的防备,比如上面程序段里用了Integer.parseInt(splitArray[0]),在程序运行时,不能保证splitArray[0]一定是一个数字的字符串,若不是数字字符串就会发生NumberFormatException数值转化异常,换句话说,就是 一旦该方法出现了异常,运行时抛出了XXXExeception,该异常的影响范围就会扩散到调用改方法的主方法上面去,如果主方法在调用该方法之后还有后续的逻辑要处理,会由于这个异常提前终止执行,导致后续的逻辑无法正常执行。
针对上面对的情况,改善这段代码由下面2条着手:
1.对数组取值之前,要先判断数组是否为空,要取值的下标是否在0到(数组size-1)之间。这一步可防止出现空指针异常、数组下标越界异常,完善程序逻辑。
2.将可能出现异常的代码块(主要是针对非受检异常,即运行时异常),用try...catch块包裹一下,这样即使出现了运行时异常,那么该方法的影响范围也仅限定在本方法内,不会扩散到调用改方法的方法。
完善之后的代码如下:
public static MapgetPlayCrowdMap(String playCrowd){ if(null == playCrowd || playCrowd.trim().length() == 0){ return null; } Map map = new HashMap (); try { String[] ss = playCrowd.split("\\|"); if(null != ss && ss.length > 0){ for(String str:ss){ StringBuffer sb = new StringBuffer(); String[] splitArray = str.split(";"); if(null != splitArray && splitArray.length > 0){ Integer type = Integer.parseInt(splitArray[0]); String typeName =""; switch (type) { case 1: typeName ="成人"; break; case 2: typeName ="儿童"; break; case 3: typeName ="婴儿"; break; case 4: typeName ="学生"; break; case 5: typeName ="老人"; break; default: break; } sb.append(typeName); //1. 在取值数组splitArray之前,对取值下标做了判断,防止出现数组下标越界异常 if (splitArray.length >= 3) { //起始年龄 if(splitArray[1].trim().length() != 0){ sb.append("(").append(splitArray[1].trim()).append("岁"); } //最大年龄 if(splitArray[2].trim().length() != 0){ sb.append("-").append(splitArray[2].trim()).append("岁").append(")"); } } } map.put(str, sb.toString()); sb = null; } } } catch (Exception e) { //2. 这里用try...catch...块,捕获运行时异常,可将有可能发生的异常限定在该方法内部,不会传播到外部,保证程序主流程的顺利执行 LOG.error("getPlayCrowdMap error {}",e); } return map; }
实际上,在措施2中 用try..catch...块捕获异常,但是并没有做额外的处理(仅仅是将异常信息记录在日志中,以备出现异常时检查异常信息),这种做法并不推荐,但是为了防止由于该方法抛出运行时异常而导致主程序提前终止也只能这样做。