本文介绍数据运算的溢出、类型转换和运算符优先级等知识。
溢出
Java中的几种数据类型都有其能表示的范围。
public class NumOverflow {
public static void main(String[] args) {
int intMin = Integer.MIN_VALUE;
int intMax = Integer.MAX_VALUE;
long longMin = Long.MIN_VALUE;
long longMax = Long.MAX_VALUE;
System.out.println("int 的范围为 " + intMin + " ~ " + intMax);
System.out.println("long 的范围为 " + longMin + " ~ " + longMax);
}
}
复制代码
输出的结果为:
int 的范围为 -2147483648 ~ 2147483647
long 的范围为 -9223372036854775808 ~ 9223372036854775807
复制代码
思考一个问题,如果在一个int类型的变量加上某一个值后,超过了int能表示的最大值,那么结果是什么呢,程序会报错吗?
public class NumOverflow {
public static void main(String[] args) {
int intMin = Integer.MIN_VALUE;
int intMax = Integer.MAX_VALUE;
int a = intMax + 10;
int b = intMin - 10;
// -2147483639
System.out.println(a);
// 2147483638
System.out.println(b);
}
}
复制代码
实际上程序并没有报错,它仍然会正常计算,只不过符号位改变了,导致结果不符合我们的预期。
2147483647: 0111 1111 1111 1111 1111 1111 1111 1111
+10: 0000 0000 0000 0000 0000 0000 0000 1010
————————————————————————————————————————————————————
-2147483639: 1000 0000 0000 0000 0000 0000 0000 1001
复制代码
溢出问题看似是小问题,但往往会成为各大公司的研发团队的绊脚石。
比如,在公司发展的初期,可能把用户id、商品id等字段定义为int类型,或者是前后端定义的数据类型不一致,随着数据量的持续增长,数据发生了溢出,造成线上故障。这时候,研发团队就会花一些精力去排查各个产线的数据溢出隐患。
整数运算在除数为0
时会报错,而浮点数运算在除数为0
时,不会报错,但会返回几个特殊值:
NaN
表示Not a NumberInfinity
表示无穷大-Infinity
表示负无穷大
例如:
public class FloatOverflow {
public static void main(String[] args) {
// NaN
double d = 0.0 / 0;
// Infinity
double e = 10.0 / 0;
// -Infinity
double f = -10.0 / 0;
System.out.println(e);
System.out.println(Double.isNaN(d));
System.out.println(Double.isFinite(e));
System.out.println(Double.isInfinite(f));
}
}
复制代码
Double
类提供了三个方法来判断浮点数是否是无限大,是否不是一个数字。
类型转换
两个数进行运算时,会进行隐式的转换,把其中一种类型转换为另一种类型,两种相同类型的变量再进行操作。
隐式转换的规则是向箭头指向的方向进行转换。
- 有double操作数,另一个数就转double;
- 有float,就转float;
- 有long,就转long;
- 其他情况都转int;
上图中,橙色箭头是无损精度的转换,黑色箭头是有损精度的转换。
除了隐式转换外,Java运行强制类型转换。比如:
public class NumberCast {
public static void main(String[] args) {
double f = 89.98;
// a = 89
int a = (int) f;
long b = 900L;
a = (int) b;
// 向上转换,不需要强制转换
long c = a;
}
}
复制代码
运算符优先级
类别 | 操作符 | 关联性 |
---|---|---|
后缀 | () [] . (点操作符) | 左到右 |
一元 | expr++ expr– | 从左到右 |
一元 | ++expr –expr + – ~ ! | 从右到左 |
乘性 | * / % | 左到右 |
加性 | + – | 左到右 |
移位 | >> >>> << | 左到右 |
关系 | > >= < <= | 左到右 |
相等 | == != | 左到右 |
按位与 | & | 左到右 |
按位异或 | ^ | 左到右 |
按位或 | ||
逻辑与 | && | 左到右 |
逻辑或 | ||
条件 | ?: | 从右到左 |
赋值 | = += -= *= /= %= >>= <<= = ^= |= | 从右到左 |
记住几个关键点:
- 括号优先级最高,赋值运算符最低;
- 加减乘除四则运算符合数学中的优先级定义;
- 自增自减高于算术运算符;
- 算术运算符高于移位和关系运算符。
在写程序的时候,如果不确定运算符的结合性和优先级,可以加入括号使代码可读性更高,也减少出错的可能。
总结
- 溢出可能造成运行结果不符合预期,写代码时要注意。
- 类型转换分隐式转换和强制转换。隐式转换会自动向上转换。
- 运算符区分优先级,不确定时加括号。
Info
以上代码托管在github上。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END