Java的整数运算遵循四则运算规则,可以使用任意嵌套的小括号。四则运算规则和初等数学一致。例如:
1 2 3 4 5 6 7 8 9
| public class Main { public static void main(String[] args) { int i = (100 + 200) * (99 - 88); int n = 7 * (5 + (i - 9)); System.out.println(i); System.out.println(n); } }
|
整数的数值表示不但是精确的,而且整数运算永远是精确的,即使是除法也是精确的,因为两个整数相除只能得到结果的整数部分:
求余运算使用%:
特别注意:整数的除法对于除数为0时运行时将报错,但编译不会报错。
溢出
要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会报错 ,却会得到一个奇怪的结果:
1 2 3 4 5 6 7 8 9
| public class Main { public static void main(String[] args) { int x = 2147483640; int y = 15; int sum = x + y; System.out.println(sum); } }
|
要解释上述结果,我们把整数2147483640和15换成二进制做加法:
1 2 3 4
| 0111 1111 1111 1111 1111 1111 1111 1000 + 0000 0000 0000 0000 0000 0000 0000 1111 ----------------------------------------- 1000 0000 0000 0000 0000 0000 0000 0111
|
由于最高位计算结果为1,因此,加法结果变成了一个负数。
要解决上面的问题,可以把int换成long类型,由于long可表示的整型范围更大,所以结果就不会溢出:
1 2 3 4
| long x = 2147483640; long y = 15; long sum = x + y; System.out.println(sum);
|
还有一种简写的运算符,即+=,-=,*=,/=,它们的使用方法如下:
自增/自减
Java还提供了++运算和--运算,它们可以对一个整数进行加1和减1的操作:
1 2 3 4 5 6 7 8 9 10
| public class Main { public static void main(String[] args) { int n = 3300; n++; n--; int y = 100 + (++n); System.out.println(y); } }
|
注意++写在前面和后面计算结果是不同的,++n表示先加1再引用n,n++表示先引用n再加1。不建议把++运算混入到常规运算中,容易自己把自己搞懵了。
移位运算
在计算机中,整数总是以二进制的形式表示。例如,int类型的整数7使用4字节表示的二进制如下:
1
| 00000000 0000000 0000000 00000111
|
可以对整数进行移位运算。对整数7左移1位将得到整数14,左移两位将得到整数28:
1 2 3 4 5
| int n = 7; int a = n << 1; int b = n << 2; int c = n << 28; int d = n << 29;
|
左移29位时,由于最高位变成1,因此结果变成了负数。
类似的,对整数28进行右移,结果如下:
1 2 3 4
| int n = 7; int a = n >> 1; int b = n >> 2; int c = n >> 3;
|
如果对一个负数进行右移,最高位的1不动,结果仍然是一个负数:
1 2 3 4 5
| int n = -536870912; int a = n >> 1; int b = n >> 2; int c = n >> 28; int d = n >> 29;
|
还有一种无符号的右移运算,使用>>>,它的特点是不管符号位,右移后高位总是补0,因此,对一个负数进行>>>右移,它会变成正数,原因是最高位的1变成了0:
1 2 3 4 5
| int n = -536870912; int a = n >>> 1; int b = n >>> 2; int c = n >>> 29; int d = n >>> 31;
|
位运算
位运算是按位进行与、或、非和异或的运算。
与运算的规则是,必须两个数同时为1,结果才为1:
1 2 3 4
| n = 0 & 0; n = 0 & 1; n = 1 & 0; n = 1 & 1;
|
或运算的规则是,只要任意一个为1,结果就为1:
1 2 3 4
| n = 0 | 0; n = 0 | 1; n = 1 | 0; n = 1 | 1;
|
非运算的规则是,0和1互换:
异或运算的规则是,如果两个数不同,结果为1,否则为0:
1 2 3 4
| n = 0 ^ 0; n = 0 ^ 1; n = 1 ^ 0; n = 1 ^ 1;
|
对两个整数进行位运算,实际上就是按位对齐,然后依次对每一位进行运算。例如:
1 2 3 4 5 6 7 8
| public class Main { public static void main(String[] args) { int i = 167776589; int n = 167776512; System.out.println(i & n); } }
|
上述按位与运算实际上可以看作两个整数表示的IP地址10.0.17.77和10.0.17.0,通过与运算,可以快速判断一个IP是否在给定的网段内。
运算优先级
在Java的计算表达式中,运算优先级从高到低依次是:
()
! ~ ++ --
* / %
+ -
<< >> >>>
&
|
+= -= *= /=
记不住也没关系,只需要加括号就可以保证运算的优先级正确。
类型自动提升与强制转型
在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型。例如,short和int计算,结果总是int,原因是short首先自动被转型为int:
1 2 3 4 5 6 7 8 9
| public class Main { public static void main(String[] args) { short s = 1234; int i = 123456; int x = s + i; short y = s + i; } }
|
也可以将结果强制转型,即将大范围的整数转型为小范围的整数。强制转型使用(类型),例如,将int强制转型为short:
1 2
| int i = 12345; short s = (short) i;
|
要注意,超出范围的强制转型会得到错误的结果,原因是转型时,int的两个高位字节直接被扔掉,仅保留了低位的两个字节:
1 2 3 4 5 6 7 8 9 10 11
| public class Main { public static void main(String[] args) { int i1 = 1234567; short s1 = (short) i1; System.out.println(s1); int i2 = 12345678; short s2 = (short) i2; System.out.println(s2); } }
|
因此,强制转型的结果很可能是错的。
小结
整数运算的结果永远是精确的;
运算结果会自动提升;
可以强制转型,但超出范围的强制转型会得到错误的结果;
应该选择合适范围的整型(int或long),没有必要为了节省内存而使用byte和short进行整数运算。