不积跬步,无以至千里;不积小流,无以成江海。 --荀子《劝学篇》

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

判断一个数是奇数还是偶数是小学里的基本知识,能够被2整除的整数是偶数,不能被2整除的数是奇数,这规则简单明了,还有什么可考虑的?好,我们来看一个例子,代码如下:
import java.util.Scanner; public class Client21 { public static void
main(String[] args) { // 接收键盘输入参数 Scanner input = new Scanner(System.in);
System.out.println("输入多个数字判断奇偶:"); while (input.hasNextInt()) { int i =
input.nextInt(); String str = i + "-->" + (i % 2 == 1 ? "奇数" : "偶数");
System.out.println(str); } } }
输入多个数字,然后判断每个数字的奇偶性,不能被2整除的就是奇数,其它的都是偶数,完全是根据奇偶数的定义编写的程序,我们开看看打印的结果:

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

前三个还很靠谱,第四个参数-1怎么可能是偶数呢,这Java也太差劲了吧。

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

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

建议22:用证书类型处理货币

在日常生活中,最容易接触的小数就是货币,比如,你付给售货员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。
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是最优的解决方案。

代码实例

声明BigDecimal对象的时候一定要使用它构造参数为String的类型的构造器。

2、使用整型

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

注:乘2取整,顺序排列

十进制小数转换成二进制小数采用"乘2取整,顺序排列"法。具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,此时0或1为二进制的最后一位。或者达到所要求的精度为止。
 
0.96875 X 2 1.9375 取1后变成 0.9375 (以下省略不写了) X 2 1.875 取1 X 2 1.75 取1 X 2 1.5 取1
X 2 1.0 取1后变成 0,结束转换 0.96875=(0.11111)2
建议23:不要让类型默默转换

我们做一个小学生的题目,光速每秒30万公里,根据光线的旅行时间,计算月球和地球,太阳和地球之间的距离。代码如下: 
public class Client23 { // 光速是30万公里/秒,常量 public static final int LIGHT_SPEED =
30 * 10000 * 1000; public static void main(String[] args) {
System.out.println("题目1:月球照射到地球需要一秒,计算月亮和地球的距离。"); long dis1 = LIGHT_SPEED * 1;
System.out.println("月球与地球的距离是:" + dis1 + " 米 ");
System.out.println("-------------------------------");
System.out.println("题目2:太阳光照射到地球需要8分钟,计算太阳到地球的距离."); // 可能要超出整数范围,使用long型 long
dis2 = LIGHT_SPEED * 60 * 8; System.out.println("太阳与地球之间的距离是:" + dis2 + " 米");
} }

 

Java是先运算然后再进行类型转换的,具体的说dis2的三个运算参数都是int型,三者相乘结果也是int型,但是超出了int的最大值,就变成负值了,越界了重新开始。

而dis3中int60变为了long60L,长整型,(Java基本转换规则,向数据范围大的方向转换,也叫加宽类型),在还没有超出int型范围的时候就已经转化成long型了,解决了越界问题,实际开发中,更通用的做法是主动声明类型转化,而不是强制类型转换,

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

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

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

建议24:边界还是边界

某商家生产的电子产品非常畅销,需要提前30天预订才能抢到手,同时还规定了一个会员可拥有的最多产品数量,目的是为了防止囤积压货肆意加价。会员的预订过程是这样的:先登录官方网站,选择产品型号,然后设置需要预订的数量,提交,符合规则即提示下单成功,不符合规则提示下单失败,后台的处理模拟如下:
public class DynamicCompileDemo { // 一个会员拥有产品的最多数量 public final static int
LIMIT = 2000; public static void main(String[] args) { // 会员当前用有的产品数量 int cur =
1000; Scanner input = new Scanner(System.in);
System.out.println("请输入需要预定的数量:"); while (input.hasNextInt()) { int order =
input.nextInt(); if (order > 0 && order + cur <= LIMIT) { cur = cur + order;
System.out.println("你已经成功预定:" + cur + " 个产品"); } else {
System.out.println("超过限额,预定失败!"); } } } }

竟然预定了-2147481829 个产品,真的是奇葩啊。

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

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

建议25:不要让四舍五入亏了一方
System.out.println("10.5近似值: "+Math.round(10.5));
System.out.println("-10.5近似值: "+Math.round(-10.5));
输出结果为:10.5近似值: 11       -10.5近似值: -10

银行家舍入(Banker's Round)的近似算法:

四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

我们举例说明,取2位精度;
round(10.5551)  =  10.56   round(10.555)  =  10.56   round(10.545)  =  10.54 
注意:根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失。

建议26:提防包装类型的null值

包装类型参与运算时,要做null值校验。

建议27:谨慎包装类型额大小比较

建议28:优先使用整形池

1、valueOf()返回的是原对象

2、valueOf()方法返回的是现在到1970年1月1日00:00:00的数值类型的毫秒数

3、包装对象的valueOf()方法返回该包装对象对应的原始值

4、装箱动作是通过valueOf方法实现的

通过valueOf产生包装对象时,如果int参数在-128到127之间,直接从整型池中获取对象,不在范围内的int类型则通过new生成包装类型。

127的包装对象直接从整型池中获得,而128、555超出了整型池范围,是通过new产生的一个新对象,地址不同,==对比就不同了。

整型池的存在提高了系统性能,节约了内存空间,也就是在生成包装对象的时候使用valueOf生成,而不是通过构造函数来生成的原因。

顺便说一下,在判断对象释放相等的时候,最好使用equals方法,避免使用==产生非预期的效果。

这个的实际意思是不是就是:

使用Integer.valueOf(1)比使用Integer i = new Interger(1);

系统性能好,更节约内存空间。

建议29:优先选择基本类型

建议30:不要随便设置随机种子

1、什么是随机种子?

随机种子是基于随机数的计算机专业术语,随机数是以随机种子作为初始条件,然后通过一定的算法不停迭代产生的。

在Java中,随机数的产生取决于种子,随机数与种子之间的关系:

① 种子不同,产生不同的随机数

② 种子相同,即使实例不同也会产生相同的随机数。

2、设置随机种子的两种方法:
若设置随机种子则相同的种子,产生的随机数相同。若不设置则每次随机的不同。 Random rnd = new Random();
rnd.setSeed()用于设置种子。 rnd.nextInt() 用于产生随机数。 rnd.nextInt(10); // 0-10之间。 Random
r = new Random(1000);//1000就是一个随机种子 for(int i=1; i<=4; i++){
System.out.println("第"+i+"次:"+r.nextInt()); }
3、Java中获取随机数的两种方式

java.util.Random类获得随机数和Math.random()。

技术
©2019-2020 Toolsou All rights reserved,
uni-app中使用 async + await 实现异步请求同步化Dart中的Isolate十分钟掌握Pytorch搭建神经网络的流程利用python对monkey日志完成自动化分析二进制模2除法(CRC循环冗余检验)vue实现pc端的自适应,rem适配希尔排序Unity面试经验(两天面六家,四个offer,济南)C++实现《走迷宫》小游戏VR、AR和MR这些技术的区别