Java漫游笔记-02-02-运算符

加减乘除

+ - * / 很好理解。

除法里面,整型除以 0 ,会抛异常,合情合理。

浮点数除以 0,就有点奇葩了:
正浮点数除以 0 ,结果为正无穷大;
负浮点数除以 0 ,结果为负无穷大;
浮点数 0 除以 0 ,结果为 NaN;

这些知道就好,保证不除 0 ,才是关键

还有 % 表示取余数,有时候会用到。
例如:判断奇偶数。这种情况,建议“判偶不判奇”
因为:
1 % 2 = 1 是奇数,-1 % 2 = -1,也是奇数。
不如 i % 2 == 0 判断条件简单,且不易出错。

自增和自减

看代码就很好理解了:

1
2
3
4
5
int c = 1;
assertThat(c++).isEqualTo(1);
assertThat(c).isEqualTo(2);
assertThat(++c).isEqualTo(3);
assertThat(c).isEqualTo(3);

即便如此,当我们看到这样的代码:

1
i = c++ + ++c + c++;

还是会忍不住想问候一下对方的。
并且,由于编译机制等因素的不同,同一个表达式,在不同的程序语言环境下运行,还可能会产生不一样的结果。
因此,如果你需要使用它,应尽可能独立使用,保持简单,易于理解

关于这个运算符,还有一个老梗。

有人嘲讽 c++ ,说这种语言的名字本身就存在着 bug。
因为只有对它改进后,我们才有可能使用它。
如果当初命名为 ++c 应该就不会出现这样的问题了 :-D

三元运算符

当我们需要使用 if-else 的时候,不妨先考虑一下它吧。

1
2
3
public int bigger(int x, int y) {
return x > y ? x : y;
}

关系运算符

需要注意的是它的“短路”特性
我们经常会利用这种特性来编写代码,例如:

1
if(string != null && string.length() != 0){}

这样就确保不会产生空指针异常。

位运算符

boolean 类型的位运算(&|)和关系运算(&&||)的结果一致,但是运算过程并不相同,不会“短路”,而是会计算所有的值

利用 ^ (异或操作)交换元素,现在已经不是什么新鲜事。
这种操作的可逆性还是挺有趣的,并且相对于加法交换,还不会有溢出的风险。

1
2
3
4
5
6
7
int a = 0;
int b = 1;
a ^= b;
b ^= a;
a ^= b;
assertThat(a).isEqualTo(1);
assertThat(b).isEqualTo(0);

使用 <<>> 实现 2 的 n 次方相乘或相除操作,也不是什么特别高深的手段。

1
2
3
int m = 2;
int n = 1;
assertThat(m << n).isEqualTo(4);

如果结果溢出,或者不是整数,只会使问题更复杂一些。
因此,位运算符虽然高效,看起来取巧,但是总的来说,不直观,不好理解,易于出错,除非对运算性能要求很高,否则,尽量不要使用它们

运算符的优先级

我只知道:乘除高于加减。其余的我都不记,因为我会使用 ()

结语

最后再强调一下,保证参与运算的数值类型一致,可以避免很多莫名其妙的错误。
因为,当我们使用两个数值进行二元操作的时候,即使我们自己不进行手动的类型转换,编译器也会自动地把两个操作数转换成一致的类型,再做运算。
举一个“简单的”小例子:

1
2
3
4
5
byte b = 1;
b = b + 1; // 编译出错,因为 1 是 int 类型,因此,会先把 b 也转换成 int 类型。
b = (byte) (b + 1); // OK
b += 1; // Ok ,因为 += 自带强制类型转换技能!!相当于:b = (byte) ((int)b + 1);
`