02-10条基本类型高质量准则

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

Java中8种基本类型:byte / char / short / int / long / float / double / boolean

1. 用偶判断,不用奇判断

1
String result = inputNum % 2 == 1 ? "奇数" : "偶数";

输入数字:1 2 0 -1 -2

结果:-1:奇数,2:偶数,0:偶数,-1:偶数,-2:偶数

问题来了,-1 怎么会是偶数呢?

我们模拟取余计算:

1
2
3
4
// 模拟取余计算,dividend被除数,divisor除数
public static int remainder(int dividend, int divisor) {
return dividend - dividend / divisor * divisor;
}

因此我们需要修正为 判断偶数 而不是判断奇数:

1
String result = inputNum % 2 == 0 ? "偶数" : "奇数";

结果验证均正确了。

2. 用整数类型处理货币

因为计算机中浮点数的二进制存储规则导致在 Java 中普通的浮点数会无限接近,而不是准确浮点值。

1
System.out.println(10.0 - 9.60); // 输出:0.4000000000000000036

解决方案:

  1. 使用 BigDecimal 类:专门弥补浮点数无法精确计算的类,最优方案
  2. 使用整型:在运算时将浮点值扩大100倍,并转为整型,在展现时将该值再缩小100倍

3. 不要让类型默默转换

1
2
3
4
// 光速 30万公里/秒,常量
public static final int LIGHT_SPEED = 30*10000*1000;
// 太阳光找到地球需要8分钟,计算太阳到地球的距离
long dis = LIGHT_SPEED * 60 * 8; // 输出:-2028888064

在此案例中,虽然使用 long 类型的值来接收计算结果,但是在 = 右侧参与运算的均为 int 类型。

Java中先运算,后进行类型转换。

解决方案:

  • 在实际开发中,更通用的做法是主动声明式类型转化(不是强制类型转换,而是自动类型提升
1
long dis = 1L * LIGHT_SPEED * 60 * 8; // 输出:144000000000

4. 边界,边界,边界!

数字越界会使检验条件失效:

1
2
3
4
5
6
7
8
9
10
private static final int LIMIT = 2000;
public static void main(String[] args) throws Exception {
// 会员当前拥有产品数量
int cur = 1000;
int order = 2147483647;
System.out.println(order + cur); // -2147482649
if (order > 0 && order+cur <= LIMIT) {
System.out.println("预定成功,预定商品数量:" + order); // 预定成功,预定商品数量:2147483647
}
}

在单元测试中,有一项测试叫边界测试:

如果一个方法接收的是 int 类型的参数,那以下三个值是必测的:0、正最大、负最小。

三个值都没有问题,方法才是比较安全可靠的。

解决方案:

  • 增加临界值判断 或 使用 long 类型

5. 不要让四舍五入亏了一方

银行家舍入的近似算法规则:

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

在Java5以上的版本使用银行家的舍入法,只需要直接使用 RoundingMode 类提供的 Round 模式即可。

1
2
3
4
5
6
// 存款
BigDecimal d = new BigDecimal(888888);
// 月利率,乘3计算季利率
BigDecimal r = new BigDecimal(0.001875 * 3);
// 计算利息,精确 2 位小数
BigDecimal i = d.multiply(r).setScale(2, RoundingMode.HALF_EVEN);

使用了 BigDecimal 类的 setScale 方法设置了精度,BigDecimal 与 RoundingMode 是绝配。

  • RoundingMode.HALF_UP 最近数字舍入(4舍5入)

  • RoundingMode.HALF_DOWN 最近数字舍弃(5舍6入)

  • RoundingMode.HALF_EVEN 银行家算法

6. 提防包装类型的 null 值

包装类做运算时,要注意做 非空判断。

7. 谨慎包装类型的大小比较

包装类做比较时,使用 a.compareTo(b) 方法,返回值含义:

  • 0:a == b
  • 1:a > b
  • -1:a < b
1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws Exception {
Integer i = 127;
Integer j = 127;
System.out.println(i == j); // true (valueOf()方法判断 -127~127 缓冲区)

Integer x = 128;
Integer y = 128;
System.out.println(x == y); // false (valueOf()方法 new 出来的)

System.out.println(i.compareTo(j)); // 0 相等;1 左>右;-1 左<右
}

8. 优先使用整型池

通过包装类的 valueOf() 静态方法生成一个类的实例,在缓冲区中的常用数 -127~127 之间可以显著提高空间和时间的性能。

9. 优先选择基本类型

基本类型自动类型提升的话(短到长的提升),不会报错;

包装类型没有自动类型提升(如 Integer 的值赋值 Long 变量),会直接报错:不兼容的类型。

10. 不要随便设置随机种子

Java中随机数的产生取决于种子,随机数和种子之间的关系原则:

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

若非必要,不要设置随机数种子。


02-10条基本类型高质量准则
https://janycode.github.io/2016/04/28/02_编程语言/01_Java/05_高质量代码/02-10条基本类型高质量准则/
作者
Jerry(姜源)
发布于
2016年4月28日
许可协议