从一个实际案例说起
国庆长假前一个礼拜,老大给我分配了这么一个bug,就是打印出来的报表数量为整数的,有的带小数位,有的不带,毫无规律。
根据短短的两个多月的工作经验以及猜测,最终把范围缩小到以下这段代码块(伪代码)
String output(double num){//double类型的参数num由DAO层提供 String result=null; if(num等于num的整数部分)//例如12.0000等于12,13.0001不等于13 result=(将num的小数位全部删去,返回相应的字符串,例如12.0000经过处理后返回12); else result=(将num的小数位保留4位,返回相应的字符串,例如12.0010经过处理后返回12.0010); return result;//字符串类型的返回值将直接输出到报表 }
double类型的传参让我想起了老大一直在向我强调说不要在存储过程中使用double类型的参数,因为可能会出现12.000000001这样的不精确数值,所以我在想问题应该是出现在这里。可是由于本地的数据没有出现这样的问题,所以我无法重现这个故障。于是就我将上面的代码做了小小的修改,问题就算是解决,更新到客户那里之后故障就再也没有出现过了。
String output(double num){//double类型的参数num由DAO层提供 String result=null; num=(num取四位小数四舍五入返回,例如12.00000001返回12.0000) if(num等于num的整数部分)//例如12.0000等于12,13.0001不等于13 result=(将num的小数位全部删去,返回相应的字符串,例如12.0000经过处理后返回12); else result=(将num的小数位保留4位,返回相应的字符串,例如12.0010经过处理后返回12.0010); return result;//字符串类型的返回值将直接输出到报表 }
这就说明了工作经验的重要性。如果没有老大之前的提醒,我恐怕很难想到原因是出在这里(尤其是在故障无法在本地重现,用不了断点测试错误数据的情况下)。年轻人嘛,作为新手,好好积累经验,以后做起工作来就会越来越得心应手。同时,如果花了很多时间都没能解决某个问题,就要适当地问问自己的导师或者经验比自己丰富的同事,向他们求助。
过了几天任务没那么紧张闲了下来,翻开买了很久的《Effective Java》,惊奇地发现里面有这样的内容:
第48条:如果需要精确的答案,请避免使用float和double
书中写道:“float和doubble类型主要是为了科学计算和工程计算而设计的。它们执行二进制浮点运算(binary floating-point arithmetic),这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合。float和double类型尤其不适合用于货币计算,因为要让一个float或者double精确地表示0.1(或者10的任何其他负数次方值)是不可能的。”
假设我们口袋里有一块五,花掉三毛两分之后剩多少钱呢?写个程序试试看
工作经验需要一天天得积累,急不来,就算急也没有用。但是,我们可以通过看书,照样可以一定程度上弥补作为新人工作经验不足的短处。
看书的时候做实验会很有趣
(第51条)字符串连接操作符(+,string concatenation operator)是把多个字符串合并为一个字符串的便利途径。要想产生单独一行的输出,或者构造一个字符串来表示一个较小的、大小固定的对象,使用连接操作符是非常合适的,但是它不适合运用在大规模的场景中。为连接n个字符串而重复地使用字符串连接操作符,需要n的平方级的时间。这是由于字符串不可变而导致的不幸结果。当两个字符串被连接在一起时,它们的内容都要被copy。我们先来看一段代码
1 private static String statement() { 2 String result = ""; 3 for (int i = 0; i < 20000; i++) 4 result += i; 5 return result; 6 }
如果运行的场景使得运算规模更大,这个方法执行时间就难以估算了。为了保证性能,该书作者建议我们使用StringBuilder替代String,来存储建造中的字符串。我们可以做一个实验看看
1 package org; 2 3 public class Testing { 4 public static void main(String[] args) { 5 long startMili=System.currentTimeMillis();//开始时刻对应的毫秒数 6 statement(); 7 long endMili = System.currentTimeMillis();//结束时刻对应的毫秒数 8 System.out.println("实验1所用的时间是:"+(endMili-startMili)+"毫秒"); 9 10 long startMili2=System.currentTimeMillis(); 11 statement2(); 12 long endMili2 = System.currentTimeMillis(); 13 System.out.println("实验2所用的时间是:"+(endMili2-startMili2)+"毫秒"); 14 15 } 16 17 private static String statement() { 18 String result = ""; 19 for (int i = 0; i < 20000; i++) 20 result += i; 21 return result; 22 } 23 24 private static String statement2() { 25 StringBuilder result = new StringBuilder(); 26 for (int i = 0; i < 20000; i++) 27 result.append(i); 28 return result.toString(); 29 } 30 31 }
运行结果如下(对比相当惊人):
实验1所用的时间是:1627毫秒 实验2所用的时间是:2毫秒
想象一下,如果工作中遇到这样的问题,恰好被所学过的知识解决了,这是一件多么痛快的事情。不仅大量地节省摸索的时间,还能够激励你花更多的精力投入到自学当中去。
(如果喜欢我写的东西,那就关注我的个人微信公众账号“华工小y”。每天都有好玩有趣的内容与你分享)