课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > Java程序:基本类型的4个建议
  • Java程序:基本类型的4个建议

    发布:昆明Java培训      来源:达内新闻      时间:2016-09-28

  • 昆明Java培训机构的老师今天给大家讲改善Java程序的4个建议。

    不积跬步,无以至千里;

    不积小流,无以成江海。

    ———荀子《劝学篇》

    建议1:用偶判断,不用奇判断

    判断一个数是奇数还是偶数是小学里的基本知识,能够被2整除的整数是偶数,不能被2整除的数是奇数,这规则简单明了,还有什么可考虑的?昆明Java培训机构的老师带大家来看一个例子,代码如下:

    1 import java.util.Scanner;

    2

    3 public class Client21 {

    4    public static void main(String[] args) {

    5        //接收键盘输入参数

    6        Scanner input = new Scanner(System.in);

    7        System.out.println("输入多个数字判断奇偶:");

    8        while (input.hasNextInt()) {

    9            int i = input.nextInt();

    10            String str = i + "-->" + (i % 2 == 1 ? "奇数" : "偶数");

    11            System.out.println(str);

    12

    13        }

    14    }

    15 }

    输入多个数字,然后判断每个数字的奇偶性,不能被2整除的就是奇数,其它的都是偶数,完全是根据奇偶数的定义编写的程序,我们来看看打印的结果:

    输入多个数字判断奇偶:1 2 0 -1 -2    1-->奇数   2-->偶数   0-->偶数    -1-->偶数      -2-->偶数

    前三个还很靠谱,第四个参数-1怎么可能是偶数呢,这Java也太差劲了吧。如此简单的计算也会出错!别忙着下结论,我们先来了解一下Java中的取余(%标识符)算法,模拟代码如下:

    //模拟取余计算,dividend被除数,divisor除数

    public static int remainder(int dividend, int divisor) {

    return dividend - dividend / divisor * divisor;

    }

    看到这段程序,大家都会心的笑了,原来Java这么处理取余计算的呀,根据上面的模拟取余可知,当输入-1的时候,运算结果为-1,当然不等于1了,所以它就被判定为偶数了,也就是我们的判断失误了。问题明白了,修正也很简 ,改为判断是否是偶数即可。代码如下:    i % 2 == 0 ? "偶数" : "奇数";

    注意:对于基础知识,我们应该"知其然,并知其所以然"。

    建议2:用整数类型处理货币

    在日常生活中,最容易接触到的小数就是货币,比如,你付给售货员10元钱购买一个9.6元的零食,售货员应该找你0.4元,也就是4毛钱才对,我们来看下面的程序:

    public class Client22 {

    public static void main(String[] args) {

    System.out.println(10.00-9.60);

    }

    }

    我们的期望结果是0.4,也应该是这个数字,但是打印出来的却是:0.40000000000000036,这是为什么呢?

    这是因为在计算机中浮点数有可能(注意是有可能)是不准确的,它只能无限接近准确值,而不能完全精确。为什么会如此呢?这是由浮点数的存储规则所决定的,我们先来看看0.4这个十进制小数如何转换成二进制小数,使用" 2取整,顺序排列"法(不懂,这就没招了,这太基础了),我们发现0.4不能使用二进制准确的表示,在二进制数世界里它是一个无限循环的小数,也就是说,"展示" 都不能"展示",更别说在内存中存储了(浮点数的存储包括三 部分:符号位、指数位、尾数,具体不再介绍),可以这样理解,在十进制的世界里没有办法唯一准确表示1/3,那么在二进制的世界里当然也无法准确表示1/5(如果二进制也有分数的话倒是可以表示),在二进制的世界里1/5是一个无 限循环的小数。

    大家可能要说了,那我对结果取整不就对了吗?代码如下

    public class Client22 {

    public static void main(String[] args) {

    NumberFormat f = new DecimalFormat("#.##");

    System.out.println(f.format(10.00-9.60));

    }

    }

    打印出的结果是0.4,看似解决了。但是隐藏了一个很深的问题。我们来思考一下金融行业的计算方法,会计系统一般记录小数点后的4为小数,但是在汇总、展现、报表中、则只记录小数点后的2位小数,如果使用浮点数来计算货 币,想想看,在大批量加减乘除后结果会有很大的差距(其中还涉及到四舍五入的问题)!会计系统要求的就是准确,但是因为计算机的缘故不准确了,那真是罪过,要解决此问题有两种方法:

    (1)、使用BigDecimal

    BigDecimal是专门为弥补浮点数无法精确计算的缺憾而设计的类,并且它本身也提供了加减乘除的常用数学算法。特别是与数据库Decimal类型的字段映射时,BigDecimal是最优的解决方案。

    (2)、使用整型

    把参与运算的值扩大100倍,并转为整型,然后在展现时再缩小100倍,这样处理的好处是计算简单,准确,一般在非金融行业(如零售行业)应用较多。此方法还会用于某些零售POS机,他们输入和输出的全部是整数,那运算就更简单 了.

    建议3:不要让类型默默转换

    我们做一个小学生的题目,光速每秒30万公里,根据光线的旅行时间,计算月球和地球,太阳和地球之间的距离。代码如下:

    1 public class Client23 {

    2    //光速是30万公里/秒,常量

    3    public static final int LIGHT_SPEED = 30 * 10000 * 1000;

    4

    5    public static void main(String[] args) {

    6        System.out.println("题目1:月球照射到地球需要一秒,计算月亮和地球的距离。");

    7        long dis1 = LIGHT_SPEED * 1;

    8        System.out.println("月球与地球的距离是:" + dis1 + "米");

    9        System.out.println("-------------------------------");

    10        System.out.println("题目2:太阳光照射到地球需要8分钟,计算太阳到地球的距离.");

    11        //可能要超出整数范围,使用long型

    12        long dis2 = LIGHT_SPEED * 60 * 8;

    13        System.out.println("太阳与地球之间的距离是:" + dis2 + "米");

    14    }

    15 }

    估计有人鄙视了,这种小学生的乘法有神么可做的,不错,就是一个乘法运算,我们运行之后的结果如下:

    题目1:月球照射到地球需要一秒,计算月亮和地球的距离。

    月球与地球的距离是:300000000米

    -------------------------------

    题目2:太阳光照射到地球需要8分钟,计算太阳到地球的距离.

    太阳与地球之间的距离是:-2028888064米

    太阳和地球的距离竟然是负的,诡异。dis2不是已经考虑到int类型可能越界的问题,并使用了long型吗,怎么还会出现负值呢?

    那是因为Java是先运算然后进行类型转换的,具体的说就是因为dis2的三个运算参数都是int型,三者相乘的结果虽然也是int型,但是已经超过了int的最大值,所以其值就是负值了(为什么是负值,因为过界了就会重头开始),再转换 long型,结果还是负值。

    问题知道了,解决起来也很简单,只要加个小小的L即可,代码如下:

    long dis2 = LIGHT_SPEED * 60L * 8;

    60L是一个长整型,乘出来的结果也是一个长整型的(此乃Java的基本转换规则,向数据范围大的方向转换,也就是加宽类型),在还没有超过int类型的范围时就已经转换为long型了,彻底解决了越界问题。在实际开发中,更通用的 做法是主动声明类型转化(注意,不是强制类型转换)代码如下:

    long dis2 = 1L * LIGHT_SPEED * 60L * 8

    既然期望的结果是long型,那就让第一个参与的参数也是Long(1L)吧,也就说明"嗨"我已经是长整型了,你们都跟着我一块转为长整型吧。

    注意:基本类型转换时,使用主动声明方式减少不必要的Bug.

    建议4:边界还是边界

    某商家生产的电子产品非常畅销,需要提前30天预订才能抢到手,同时还规定了一个会员可拥有的最多产品数量,目的是为了防止囤积压货肆意加价。会员的预订过程是这样的:先登录官方网站,选择产品型号,然后设置需要预 订的数量,提交,符合规则即提示下单成功,不符合规则提示下单失败,后台的处理模拟如下:

    1 import java.util.Scanner;

    2

    3 public class Client24 {

    4    //一个会员拥有产品的最多数量

    5    public final static int LIMIT = 2000;

    6

    7    public static void main(String[] args) {

    8        //会员当前用有的产品数量

    9        int cur = 1000;

    10        Scanner input = new Scanner(System.in);

    11        System.out.println("请输入需要预定的数量:");

    12        while (input.hasNextInt()) {

    13            int order = input.nextInt();

    14            if (order > 0 && order + cur <= LIMIT) {

    15                System.out.println("你已经成功预定:" + order + "个产品");

    16            } else {

    17                System.out.println("超过限额,预定失败!");

    18            }

    19        }

    20

    21    }

    22 }

    这是一个简单的订单处理程序,其中cur代表的是会员当前拥有的产品数量,LIMIT是一个会员最多拥有的产品数量(现实中,这两个参数当然是从数据库中获得的,不过这里是一个模拟程序),如果当前预订数量与拥有数量之和超 过了最大数量,则预订失败,否则下单成功。业务逻辑很简单,同时在web界面上对订单数量做了严格的校验,比如不能是负值、不能超过最大数量等,但是人算不如天算,运行不到两小时数据库中就出现了异常数据:某会员拥有 的产品数量与预定数量之和远远大于限额。怎么会这样呢?程序逻辑上不可能有问题呀,这如何产生的呢?我们来模拟一下,第一次输入:

    请输入需要预定的数量:800   你已经成功预定800个产品

    这完全满足条件,没有任何问题,继续输入:

    请输入需要预定的数量:2147483647  你已经成功预定2147483647个产品

    看到没有,这个数字已经远远超过了2000的限额,但是竟然预定成功了,真实神奇!

    看着2147483647这个数字很眼熟?那就对了,这个数字就是int类型的最大值,没错,有人输入了一个最大值,使校验条件失败了,Why?我们来看程序,order的值是2147483647那再加上1000就超出int的范围了,其结果是-2147482649,那当然是 于正数2000了!一句归其原因:数字越界使校验条件失效。

    在单元测试中,有一项测试叫做边界测试(也叫临界测试),如果一个方法接收的是int类型的参数,那么以下三个值是必须测试的:0、正最大、负最小,其中正最大、负最小是边界值,如果这三个值都没有问题,方法才是比较安全 靠的。我们的例子就是因为缺少边界测试,致使生产系统产生了严重的偏差。

    也许你要疑惑了,Web界面已经做了严格的校验,为什么还能输入2147483647 这么大的数字呢?是否说明Web校验不严格?错了,不是这样的,Web校验都是在页面上通过JavaScript实现的,只能限制普通用户(这里的普通用户是指不懂html ,不懂HTTP,不懂Java的简单使用者),而对于高手,这些校验基本上就是摆设,HTTP是明文传输的,将其拦截几次,分析一下数据结构,然后写一个模拟器,一切前端校验就成了浮云!想往后台提交个什么数据还不是信手拈来!

    学Java开发就到昆明达内Java培训班!了解详情请登陆昆明达内Java培训官网(km.Java.tedu.cn)!

    推荐文章

上一篇:JavaWeb基础:BaseDao

下一篇:java程序员:改善java程序的建议

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56