2.5 整数

简单地说,整数就是没有小数部分的数值。不过,在程序开发过程中,整数的使用方式还是有点复杂的,先从整数的直接量说起。

生活中使用的数字都是十进制数,在Java代码中,默认情况下也使用十进制数,如0,1,2,…不过,在Java代码中还可以使用其他进制,如:

□ 整数以0开头时定义为八进制数,如020表示十进制数16。

□ 整数以0x开头时定义为十六进制数,如0xF表示十进制数15。

□ 整数以0b开头时定义为二进制数,如0b0101表示十进制数5。

下面的代码可以验证这些数的值。

此外,还需要注意,如果没有指定类型,整数默认为int类型。

2.5.1 算术运算

加、减、乘、除、取余数,这些概念并不陌生。软件开发中,它们同样是基本的数据运算方式。Java代码中分别使用如下运算符。

□ +,加法运算符,如x+y。

□ -,减法运算符,如x-y。

□ *,乘法运算符,如x*y。

□ /,除法运算符,如x/y。

□ %,取余数运算符,也称为取模运算符,如x % y。

整数运算时,应注意以下一些问题。

□ 整数运算的结果依然是整数,如果运算数的类型不一样,则统一转换为取值范围较大的类型,然后进行计算。

□ 整数的除数运算结果中只保留整数部分。

□ 整数的除法和取余数运算时,如果右运算数为0,即x/y或x%y中的y为0时,会产生错误。

下面的代码演示了整数的算术运算。

2.5.2 增量与减量运算

简单地说,增量运算负责执行变量加1的操作。增量运算又分为前增量和后增量,都使用++运算符。下面分别讨论二者。

首先介绍前增量,先看一下代码的执行结果。

第一个输出语句显示的是前增量运算表达式的值,第二个输出语句显示的则是前增量运算后x变量的值。

前增量时,会先执行变量加1的操作,然后返回增量表达式的值,所以,++x表达式显示的值是2。

后增量时,增量表达式会先返回x的值,然后进行加1的操作,如下面的代码所示。

执行代码后,可以看到,x++表达式首先返回x的值,然后执行加1的操作,最终x的值同样为2。

对于前增量和后增量操作,如果只需要使用变量运算后的值,则它们的结果是一样的,但在使用运算表达式的值时,就需要注意它们的区别了。

此外,减量和增量运算类似,只是减量执行减1的操作。应用时,也需要注意前减量运算和后减量运算的区别。

2.5.3 位运算

位运算主要包括逻辑位运算和位移运算。其中,逻辑位运算会对操作数的二进制位进行逻辑运算,包括:

□ 位“与”运算,使用&运算符,当两个二进制位数据都为1时返回1,否则返回0。

□ 位“或”运算,使用|运算符,当两个二进制位数据有一个为1时返回1,否则返回0。

□ 位“取反”运算,使用~运算符,当二进制位数据为1时返回0,为0值时返回1。

□ 位“异或”运算,使用^运算符,当两个二进制位数据相同时返回0,不同时返回1。

下面的代码演示了逻辑位运算的应用。

本例中定义了两个byte类型的变量,并赋给它们8位二进制数。与(&)、或(|)、异或(^)运算的结果都能容易计算出来,就是把二进制数转换为十进制数的方法,如x|y的结果,其计算方法就是:

那么,对于x取反(~)操作的结果0b11111101怎么会显示-3呢?byte类型的数据是有符号的,即最高位是符号位,1表示负数,0表示0或正数。此外,当数据为负数时,其二进制实际上是其绝对值的补码形式。

整数3的8位二进制形式是00000011,其补码的计算是按位取反后加1,即11111100+1,也就是11111101,表示-3。

实际开发工作中,逻辑位运算经常进行一些标识数据的比较工作,如下面的代码所示。

代码中,首先定义三个基本的标识数据,即flag1、flag2和flag3。它们的特点是在不同的二进制位的数据是1,其他位都是0。然后定义了flagA变量,设置其为flag1和flag2的组合(使用或运算)。

接下来,分别将flagA与flag1、flag3对比(使用与运算)。当flagA包括指定的标识数据时,就会返回这个标识数据,否则返回0。这样就可以通过位运算非常高效地进行数据的对比工作。

另一种二进制位的运算是位移运算,在Java中包括三个位移运算符,即<<运算符、>>运算符和>>>运算符。下面分别讨论三者。

<<运算符称为位左移运算,执行此运算时,二进制位会向左移动,低位补0,如下面的代码所示。

    System.out.println(0b0010 << 2);        // 8, 0b1000

二进制数0010表示2,左移两位就是1000,即数字8。实际上,当整数x进行左移n位运算时,就是在执行x * 2n的运算。

请注意,如果位左移操作超过类型所支持的位数,超出部分就会丢失,也就是发生了数据溢出,如下面的代码所示。

    System.out.println((byte)(0b11000001<< 1));     // -126

代码中的-126是怎么得到的呢?首先,计算11000000左移1位的操作,其结果是110000010,如果是int类型,它就是386。但是,如果将数据强制转换为byte类型,而byte只能保存8位整数,那么最高位的一个1会丢失,结果变为10000010。对于byte类型的数据,其最高位为符号位,所以这是一个负数,但它是数值绝对值的补码,需要反推计算。首先,减1得到10000001。然后,取反得到01111110。接下来,通过如下算式计算其十进制数据。

    26+25+24+23+22+21 = 64+32+16+8+4+2 = 126

这样,byte类型的二进制10000010表示的就是-126。

>>运算符称为有符号位右移运算符,执行此操作时,二进制位会向右移动,此时,符号位不动,右移空出的高位要补上与符号位相同的数据。下面的代码用于右移一个正整数。

    System.out.println((byte)0b01000000 >> 2);     // 16

当01000000右移两位时,其结果为00010000,即正整数16。实际上,执行x>>n运算时,就是在计算x/2n。下面的代码对负整数执行>>运算。

    System.out.println((byte)0b11000000 >> 2);     // -16

11000000表示一个负数,通过反推计算(减1取反)可以得出,它表示-64。进行右移两位运算时,其结果就是11110000,再进行减1取反得到00010000,也就是16。这样也就验证了代码中的计算结果。

>>>运算符称为无符号右移运算,执行此运算时,不考虑符号位,数据整体右移,并在高位补0,如下面的代码所示。

    System.out.println(0b11000000 >>> 2);        // 48

当11000000右移两位时,其结果为00110000,也就是48。

本节已经使用了+、-、*、/、%、&、|、^、<<、>>、>>>等运算符,这些运算符可以与赋值运算符=组合使用,如x += 2的含义就是x = x + 2,其他运算符的组合也有类似的功能。