`

一个“诡异”的NumberFormatException

 
阅读更多

前几天线上一个功能莫名其妙没有正常执行,而且奇怪的这个功能模块以及很久没有改动了,而且一直运行的好好的,从没出问题,今天排查看日志,发现是程序在进行一段内存排序时,抛NumberFormatException了。

Exception in thread "main" java.lang.NumberFormatException: For input string: "30d"

 

这我就纠结了,因为内存排序是通过把从数据库去取出来的String转化成BigDecimal来进行排序的,而且NumberFormatException就是抛在这句代码上:

 

		BigDecimal bg = new BigDecimal(answer);

 因为answer是从DB里取出来的,这个answer是用户通过页面提交的,而且在存到DB之前,一个同事专门写了个方法来判断用户提交的答案是不是能转换成数字,如果不能转换成数字,则会拒绝用户的这次提交。既然已经过滤掉了非法的答案,那为什么还会把30d存入到DB呢?

因此,我去看过滤掉非法答案的非法是如何来过滤的?

		try{
			Float floatAnswer = Float.valueOf(answer);
		}catch (NumberFormatException e) {
			// TODO: handle exception
		}

  如果上述代码没有抛出异常,就认为输入的答案在格式上时合法的。一看到这个Float.valueOf(answer),我就知道问题是出在这里了, Float.valueOf(answer)和new BigDecimal(answer)在判断是一个string能否正确转换成数字上采取的是不同的标准,先来看一下Float.value(String s)方法:

 

    public static Float valueOf(String s) throws NumberFormatException {
	return new Float(FloatingDecimal.readJavaFormatString(s).floatValue());
    }

 

  FloatingDecimal.readJavaFormatString(s)方法是sun.misc.FloatingDecimal里面,这个类下载open jdk的源码后可以看到,通过截取readJavaFormatString方法的一段代码就可以知道为什么30d这个数字会通过校验被存进DB了

if ( i < l &&
    ((i != l - 1) ||
    (in.charAt(i) != 'f' &&
     in.charAt(i) != 'F' &&
     in.charAt(i) != 'd' &&
     in.charAt(i) != 'D'))) {
    break parseNumber; // go throw exception

  上述代码段可以看出,数字后面加上字母D、d、F、f是合法的。

而BigDecimal(String val)在进行校验的时候,则不允许字母出现在最后,但这样的字符串"2e9"是合法的,因为可以理解成科学计算法,因为代码过长,所以就不具体分析了。

至此,这个诡异的NumberFormatException已经分析清楚了,虽然问题的原因已经弄清楚了,但我认为从这个问题中可以总结出两点:

1.对于一些常用API方法,需要彻底弄清出原理,如果实在不清楚其具体原理,也需要用一些边界值去测试

2.对于任何参数,如果该参数使用的时候存在某种风险,那都要采取怀疑态度,不要因为某些地方已经对这个参数做过校验了就完全相信(就像上面的例子里的情况)

 

一点小总结,

分享到:
评论
1 楼 1234abc 2016-03-20  
谢谢分享,很好的实例,赞一个

相关推荐

Global site tag (gtag.js) - Google Analytics