01_basic_05_operator

第五章 操作符与运算

在Java语言中,变量用于保存数据,而数据的运算是通过操作符(Operator)完成的。我们将在本章中介绍Java语言基本数据类型的运算和相应操作符的使用方法。

1 整数运算

1.1 四则运算

其实,我们在上一章已使用过加法和乘法来完成简单的计算。在下面的例子中,我们计算了数学公式y = x2+2x+1的值。加法、减法、乘法、除法、取余、乘方这六种运算都需要作用在两个数上。在Java语言中,这六种操作分别使用符号+、-、*、/、%、^表示,这些符号被称为操作符(Operator)。作用在操作符上的数被称为操作数(Operand)。因为这些操作符需要两个操作数,所以它们又被称为二元操作符(Binary Operator)

int x = 1;
int y = x * x + 2 * x + 1;

使用这些操作符,我们能完成一些更为复杂的计算。例如,z = y2-x%3。

int z = y*y - x%3;

在阅读y=x2+2x+1时,我们很自然的按照四则运算的法则,先计算x2,然后再计算2x,最后将三项加在一起。在Java程序中,操作符运算的次序由它们的优先级决定。因为乘法的优先级比加法的优先级高,所以,Java程序会先运算乘法,再运算加法。

因为Java语言支持许多操作符,有时开发人员可能会记不清操作符的优先级顺序。为了避免这样的问题,提高代码的可读性,Java语言支持使用圆括号来明确指明计算顺序。括号的优先级比乘号高,所以,如下的运算会先计算x+2的值,然后再计算乘法。

int t = x * (x + 2);

1.2 自增和自减运算

自增和自减是一元操作符(Unary Operator);它作用于一个操作数上,并在该操作数的值上增加1或者减少1。自增操作符++由两个相连的加号组成;自减操作符--由两个相连的减号组成。例如,在运行完以下的程序后,变量i和j的值分别为1和-1。这是因为变量i在值0的基础上增加了1,而变量j在值0的基础上减少了1。

int i = 0;
i++;
// 此时,变量i的值为1。

int j = 0;
j--;
// 此时,变量j的值为-1。

自增和自减操作符分别有前缀(Prefix)后缀(Postfix)两种用法。当自增或者自减操作符出现在变量前面时,为前缀自增或者前缀自减操作符。当自增或者自减操作符出现在变量后面时,为后缀自增或者后缀自减操作符。它们的区别是,当在计算整个表达式之前,Java程序会先进行前缀自增/前缀自减的运算。当整个表达式运算完毕后,Java程序才会进行后缀自增/后缀自减的运算。

int i = 0;
int j = 1;

int m = ++i;
// 此时,i = 1, m = 1,因为对变量i的自增发生在赋值之前
int n = --j;
// 此时,j = 0, n = 0,因为对变量j的自减发生在赋值之前

int p = i++;
// 此时,i = 2, p = 1,因为对变量i的自增发生在赋值之后
int q = j--;
// 此时,j = -1,q = 0,因为对变量j的自减发生在赋值之后

1.3 位运算

在Java语言中,整数还可被看成一串0和1的二进制数,可进行位操作。进行位运算的操作符被称为位操作符(Bit Operator)。按位运算的操作符有按位与(&)按位或(|)按位异或(^)按位取反(~)。按位与(&),按位或(|),按位异或(^)是二元操作符,按位取反(~)是一元操作符。

根据逻辑运算的规则,按位操作符是将变量的每一位进行逻辑运算,并获得计算结果。以下是逻辑与的运算逻辑。

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

例如,在下面的程序中,我们计算整数5和7的按位与的值,其运算结果为5。因为,整数5的二进制表示为0101,整数7的二进制表示为0111。所以,在按位与运算后,得到的结果为0101,其值为5。

/*
  0101
& 0111
------
= 0101
 */
int x = 5;
int y = 7;
int z = x & y; // z的值为5

类似的,我们还可以进行按位或运算。

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

在下面的程序中,整数5的二进制表示为0101,整数7的二进制表示为0111。所以,在按位或运算后,得到的结果为0111,其值为7。

/*
  0101
| 0111
------
= 0111
 */
int x = 5;
int y = 7;
int z = x | y;  // 此时,z的值为7

按位异或的逻辑稍有不同。当两个位的值相同时,异或的值为0。当两个位的值不同时,异或的值为1。

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

所以,我们很容易的能够得到下面程序的结果,z的值为2,或者以二进制表示为0010。

/*
  0101
^ 0111
------
= 0010
 */
int x = 5;
int y = 7;
int z = x ^ y; // z的值为2

按位取反(~)则是将0的值变为1,将1的值变为0。

~0 = 1
~1 = 0

所以,当x的值为5时,它的取反的结果为-6。

除了按位运算以外,Java语言还支持整形变量的位移运算,即将整形变量以二进制的形式向左或者向右移动。位移操作分为向左位移和向右位移。向左位移操作符由连续两个小于号表示“<<”。向右位移操作符由连续两个或者三个大于号表示">>"或者">>>"。“>>”与">>>"的区别是当使用">>>"时,在位移之后,最高位由0填充。而当使用">>"时,最高位由操作数的值决定。向左位移操作符和向右位移操作符均为二元操作符。

如下面的程序所示,当x的值为0b0011时,如果将x的二进制形式向左移动一位,则其值将变为0b0110,即整数6。如果将x的二进制形式向右移动一位,则其值将变为0b0001,即整数1。

int x = 0b0011;
int y = x << 1;  // y的值为0b0110,即整数6
int z = x >> 1;  // z的值为0b0001,即整数1

1.4 溢出问题(Overflow)

在Java语言中,整型变量是有取值范围的限制的。例如,一个int类型的变量,它的取值范围在[-231, 231-1]之间。因为在默认情况下,int类型变量是一个四字节的有符号整数(32-bit Signed Integer)。如果在计算过程中,int类型的值超出了这个范围,程序将会得到错误的结果。因此,我们称这个错误为计算溢出(Overflow)。如果变量的值超过其取值范围的上界,我们称为上界溢出。如果变量的值低于其取值范围的下界,我们称为下界溢出

例如,下面的程序会出现上界溢出,因为y的值超过的int类型变量的上界,Java程序无法使用int类型的变量来保存这么大的整数。所以,在程序运行中,y的实际值是-2147483648,这显然是一个错误的结果。

int x = 2147483647;
int y = x + 1;

类似的,下面的程序会出现下界溢出,因为-2147483648是int类型变量所能表示的最小整数。在程序运行中,y的实际值是2147483647,这也显然是一个错误的结果。

int x = -2147483648;
int y = x - 1;

溢出问题还可能会出现于使用其他整形变量的场景下。为了避免溢出问题,程序应当避免使用或者出现接近于边界的值。如果此种情况无法避免,则可选择使用精度更高、表达范围更广的类型变量。例如,在上述的程序中,如果把变量x和y的类型调整为long之后,程序能够正确运行。

long x = 2147483647;
long y = x + 1; // y的值为2147483648

2 逻辑运算

Java语言支持逻辑"真"和逻辑"假"两个值。它们由truefalse表示。逻辑操作符&&和||分别是逻辑与操作符逻辑或操作符。这两个操作符均为二元操作符。它们的运算逻辑如下。

true && true = true
true && false = false
false && true = false
false && false = false
true || true = true
true || false = true
false || true = true
false || false = false

逻辑取反操作符!会将逻辑"真"设置为逻辑"假",将逻辑"假"设置为逻辑"真"。逻辑取反操作符为一元操作符。

另外,Java语言支持比较操作符。这些操作符比较操作数的值,并生成逻辑"真"或者逻辑"假"。比较操作符有大于操作符>小于操作符<大于等于操作符>=小于等于操作符<=等于操作符==不等操作符!=

boolean b1 = 1 < 2; // b1为true,因为1小于2
boolean b2 = 1 <= 2; // b2为true,因为1小于等于2
boolean b3 = 1 > 0; // b3为true,因为1大于0
boolean b4 = 1 >= 0; // b4为true,因为1大于等于0
boolean b5 = 1 == 0; // b5为false,因为1不等于0
boolean b6 = 1 != 0; // b6为true,因为1不等于0

3 浮点数运算

浮点数也支持四则运算。例如,单精度浮点数float和双精度浮点数double都支持加法、减法、乘法和除法运算。

final double PI = 3.1415926;
double radius = 10; // 圆的半径
double circumference = 2 * PI * radius; // 圆的周长

4 赋值操作符

除了我们已介绍过的赋值操作符=,Java语言还支持一系列的,与运算相结合的赋值操作符。例如,赋值操作符+=将加法和赋值结合在一起。其意义是将操作符左边的操作数加上操作符右边的操作数,然后再将结果赋值给操作符左边的操作数。

int x = 0;
x += 1; // 它等效于 x = x + 1

类似的赋值操作符还有+= -= *= /= %= &= ^= |= <<= >>= >>>=

5 三元操作符

Java语言中唯一的一个三元操作符是?:。它由一个问号和一个冒号组成;它接收三个操作数。

三元操作符与三个操作数组成一个表达式,它的结构如下。它首先计算expr1的值。当expr1的值为true时,三元操作符会使用expr2来代替整个三元表达式的值。当expr1的值为false时,三元操作符会使用expr3来代替整个三元表达式的值。

expr1 ? expr2 : expr 3 

6 操作符优先级

当在程序中连续使用多个操作符时,Java程序会按照它们的优先级顺序处理和计算表达式的值。例如,在如下的计算中,Java程序会先运算乘法,再运算加法。所以,变量y的值为5。

int x = 1;
int y = 2 + 3 * x;

当表达式较为复杂时,小水滴建议使用括号来明确指出运算的顺序。当同等优先级的两个或者多个操作符出现在一起时,赋值操作符使用左结合,从左向右运算求值。其他的二元操作符使用右结合,从右向左运算求值。

Java语言支持的操作符的优先级如下表所示。

优先级操作符结合顺序解释
最高expr++ expr-- 后缀自增,后缀自减
 ++expr --expr +expr -expr ~ ! 前缀自增、前缀自减、一元操作符
 * / %右结合乘法、除法、取余
 + -右结合加法、减法
 << >> >>>右结合向左位移、向右位移
 < > <= >= instanceof右结合关系比较操作符
 == !=右结合相等比较操作符、不等比较操作符
 &右结合按位与操作符
 ^右结合按位异或操作符
 |右结合按位或操作符
 &&右结合逻辑与操作符
 ||右结合逻辑或操作符
 ?: 三元操作符
最低= += -= *= /= %= &= ^= |= <<= >>= >>>=左结合赋值操作符

7. 结语

本章介绍了Java语言支持的操作符。操作符蕴含了计算过程中最基本的运算逻辑。Java语言的操作符包括一元操作符、二元操作符和三元操作符,它们分别作用于一个操作数、两个操作数和三个操作数。每个操作符运算完成后都会生成一个新的值。值的类型由操作符的定义决定。我们在本章中着重介绍了基本数据类型的操作符的使用方法。随着内容的深入,我们会在合适的时候,介绍可应用于对象的操作符instanceof和其相关的内容。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.