且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《Java和Android开发学习指南(第2版)》——第2章,第2.8节操作符

更新时间:2022-08-21 21:04:32

本节书摘来自异步社区《Java和Android开发学习指南(第2版)》一书中的第2章,第2.8节操作符,作者 【加】Budi Kurniawan,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.8 操作符
计算机程序是实现某一功能的操作汇集在一起的一个集合。有很多种类型的操作,包括加法、减法、乘法、除法和位移。在本小节中,我们将学习各种Java操作。

一个操作符会对一个、两个或三个操作数执行操作。操作数是操作的目标,而操作符则是表示动作的一个符号。例如,如下是加法操作:

x + 4

在这个例子中,x和4是操作数,+是操作符。

一个操作符可能返回一个结果,也可能不返回结果。

操作符和操作数的任何合法的组合,叫作表达式(expression)。例如,x+4是一个表达式。一个布尔表达式会得到真或假;一个整数表达式会得到一个整数;浮点数表达式的结果是一个浮点数。
只需要一个操作数的操作符叫作一元操作符(unary operator),Java中有几个一元操作符。二元操作符(binary operator)接受两个操作数,这是Java操作符中最常见的类型。还有一个三元操作符(ternary operator)? :,它需要3个操作数。


《Java和Android开发学习指南(第2版)》——第2章,第2.8节操作符

在Java中,操作符分为6类:

一元操作符。
算术操作符。
关系和条件操作符。
位移和逻辑操作符。
赋值操作符。
其他操作符。
下面分别介绍每一种操作符。

2.8.1 一元操作符
一元操作符在一个操作数上起作用。有6个一元操作符,本节中将一一介绍。

一元减操作符
一元减操作符返回其操作数的负值。操作数必须是一个数字值,或者是一个数值基本类型的变量。例如,在下面的代码中,y的值是-4.5;

float x = 4.5f; 
float y = -x;

一元加操作符
一元加操作符返回其操作数的值。操作数必须是一个数值类型,或者是一个数值类型的变量。例如,在如下的代码中,y的值是4.5。

float x = 4.5f; 
float y = +x;

这个操作符没有那么重要,因为不使用它也不会有什么差别。

自增操作符++
自增操作符将其操作数增加1。操作数必须是一个数值基本类型的变量。操作符可以出现在操作数之前或之后。如果操作符出现在操作符之前,它叫作前缀自增操作符。如果它写在操作数之后,叫作后缀自增操作符。

作为例子,下面展示了一个前缀自增操作符:

int x = 4; 
++x;
在++x之后,x的值变为5。前面的代码等同于:

int x = 4; 
x++;
在x++之后,x的值为5。

然而,如果在同一个表达式中,自增操作符的结果需要赋值给另一个变量,那么,前缀操作符和后缀操作符之间就存在差异了。考虑如下的例子:

int x = 4; 
int y = ++x; 
// y = 5, x = 5

在赋值之前,使用了前缀自增操作符。x自增到5,并且随后将其值复制给y。

查看一下如下的后缀自增操作符的用法。

int x = 4; 
int y = x++; 
// y = 4, x = 5

使用后缀自增操作符,在将操作数(x)的值赋值给另一个变量(y)之后,才将操作数的值加1。

注意,自增操作符对于int类型来说是最常用的。它对于其他的数字基本类型(例如float和long)来说,也是适用的。

自减操作符--
自减操作符将操作数的值减去1。操作数必须是数值基本类型的一个变量。和自增操作符类似,它也有前缀自减操作符和后缀自减操作符。例如,如下的代码将x的值自减,并将其赋值给y。
``
int x = 4;
int y = --x;
// x = 3; y = 3

在如下的示例中,使用了后缀自减操作符:

int x = 4;
int y = x--;
// x = 3; y = 4

逻辑取反操作符!
逻辑取反操作符只适用于一个布尔基本类型或者java.lang.Boolean的一个实例。如果操作数是false,这个操作符的值为true;如果操作数为true,这个操作符的值为false。例如:

boolean x = false;
boolean y = !x;
// at this point, y is true and x is false

位取反操作符~
位取反操作符的操作数必须是一个整数基本类型或者整数基本类型的一个变量。其结果是对操作数的按位取反。例如:

int j = 2;
int k = ~j; // k = -3; j = 2

要理解这个操作符是如何工作的,需要将操作数转换为一个二进制数,并且将所有的位都取反。整数2的二进制形式是:

0000 0000 0000 0000 0000 0000 0000 0010
对其按位取反之后,得到

1111 1111 1111 1111 1111 1111 1111 1101

而这是整数-3的二进制表示。

**2.8.2 算术操作符**
有5种类型的算术操作符,分别是加法、减法、乘法和除法,以及模除。下面将分别介绍这5种操作符。

加法操作符+
加法操作符将两个操作数相加。操作数的类型必须可以转换为一个数值基本类型。例如:

byte x = 3;
int y = x + 5; // y = 8
要确保接受加法结果的变量有足够的容量。例如,在如下的代码中,k的值是-294967296而不是40亿。

int j = 2000000000; // 2 billion
int k = j + j; // not enough capacity. A bug!!!
如下的代码则像预期的那样工作:

long j = 2000000000; // 2 billion
long k = j + j; // the value of k is 4 billion

减法操作符-
减法操作符在两个操作数之间执行减法。操作数的类型必须可以转换为一个数值类型。例如:

int x = 2;
int y = x – 1; // y = 1

乘法操作符*
乘法操作符在两个操作数之间执行乘法。操作数的类型必须能够转换为一种数值基本类型。例如:

int x = 4;
int y = x * 4; // y = 16

除法操作符/
除法操作符在两个操作数之间执行除法。左操作数除以右操作数。除数和被除数都必须是能够转换一种数值基本类型的类型。例如:

int x = 4;
int y = x / 2; // y = 2

注意,在运行时,如果除数为0的话,将会导致一个错误。使用/操作符的除法的结果,总是一个整数。如果除数不能够将被除数除尽,余数将会被忽略。例如:

int x = 4;
int y = x / 3; // y = 1

第5章将会介绍java.lang.Math类,它能够执行更为复杂的除法操作。

模除操作符%
模除操作符执行两个操作数之间的除法,但是返回余数。左操作数是被除数,右操作数是除数。被除数和除数都必须是能够转换为数值基本类型的一种类型。例如,如下操作的结果是2。

8 % 3
2.8.3 相等操作符
有两种相等操作符,==(相等)和!=(不相等),它们都可以作用于两个整数、浮点数、字符或布尔类型的操作数。相等操作符的结果也是一个布尔类型。

例如,如下的比较,结果为真。

int a = 5; 
int b = 5; 
boolean c = a == b;
又如

boolean x = true;
boolean y = true;
boolean z = x != y;

比较之后,z的值为false,因为x等于y。

**2.8.4 关系操作符**
一共有5种关系操作符:<、>、<=、>=以及instanceof。前4个操作符都将在本节中介绍。第7章将会介绍instanceof。

<、>、<=和>=操作符作用于两个操作数之上,它们的类型必须能够转换为一种数值基本类型。关系操作符返回一个布尔值。<操作符计算左边操作数的值是否小于右边的操作数的值。例如,如下的操作返回假:

9 < 6

>操作符计算左操作数的值是否大于右操作数的值。例如,如下的操作返回真:

9 > 6

<=操作符测试左操作数的值是否大于或等于右操作数的值。例如,如下的操作结果为假:

9 <= 6

>=操作符测试左操作数的值是否大于或等于右操作数的值。例如,如下操作返回真:

9 >= 9

**2.8.5 条件操作符**
一共有3个条件操作符:AND操作符&&、OR操作符||以及?:操作符。下面详细地介绍每一种操作符。

&&操作符
&&操作符接受两个表达式作为操作数,并且这两个表达式都必须返回一个值,而这个值必须能够转换为一个布尔类型。如果两个操作数的结果都为真,那么它返回真。否则,它返回假。如果左操作数为假,右操作数就不必计算了。例如,如下的例子返回假:

(5 < 3) && (6 < 9)

||操作符
||操作符接受两个表达式作为操作数,并且这两个表达式都必须返回一个值,而这个值必须能够转换为一个布尔类型。如果两个操作数有一个结果为真,那么||返回真。如果左操作数为真,右操作数就不必计算了。例如,如下的例子返回真:

(5 < 3) || (6 < 9)
? :操作符
? :操作符有3个操作数。其语法如下:

expression1 ? expression2 : expression3 
这里,expression1必须返回一个能够转换为布尔类型的值。如果expression1的结果为真,返回expression2,否则的话,返回expression3。

例如,如下的表达式返回4。

(8 < 4) ? 2 : 4

**2.8.6 位移操作符**
位移操作符接受两个操作数,操作符类型必须能够转换为一个整数基本类型。左操作数是要位移的值,右操作数表示位移的距离。如下是3种类型的位移操作符:

向左位移操作符<<。
向右位移操作符>>。
无符号位向右位移操作符>>>。
向左位移操作符<<
向左位移操作符会把一个数字向左位移,右边的位用0来补充。n << s的值是将n向左移动s位。相当于将操作数乘以2的s次幂。

例如,将一个值为1的int向左位移3个位置(1<<3),结果是8。为了搞清楚这一点,需要将操作数转换为一个二进制数。

0000 0000 0000 0000 0000 0000 0000 0001

向左位移3位后,得到:

0000 0000 0000 0000 0000 0000 0000 1000

其结果等于8(等同于1*23)。

另一条规则是,如果左操作数是一个int类型,只有位移距离的前5位会使用。换句话说,位移距离必须在0~31之间。如果传递的数字大于31,只有前5位会使用。也就是说,如果x是一个int类型,x<<32等同于x<<0;x<<33等同于x<<1。

如果左操作数是long类型,位移距离中只有前6位会使用。换句话说,实际使用的位移距离的范围在0~63之间。

向右位移操作符>>
向右位移操作符>>将左边的操作数向右位移右边操作数所指定的位数。n>>s是将n向右移动s位。结果是n/2s。

例如,16 >> 1等于8。为了证实这一点,写出16的二进制表示如下。

0000 0000 0000 0000 0000 0000 0001 0000

然后,将其向右移动1位,得到:

0000 0000 0000 0000 0000 0000 0000 1000

结果等于8。

无符号向右位移操作符>>>
n>>>s的值取决于n是正数还是负数。对于一个正数n,其结果值和n>>s相同。

如果n是负值,其结果取决于n的类型。

如果n是一个int类型,结果的值是(n>>s)+(2<<~s)。如果n是一个long类型,结果的值是(n>>s)+(2L<<~s)。

2.8.7 赋值操作符
一共有12个赋值操作符,如下所示:

= += -= *= /= %= <<= >>= >>>= &= ^= |=
左操作数必须是一个变量。例如:

int x = 5;
除了赋值操作符=以外,其他的操作符都以相同的方式工作,而且,你应该看到,其中的每一个都由两个操作符组成。例如,+=实际上是+和=。赋值操作符<<=也有两个操作符,分别是<<和=。

由两部分组成的赋值操作符是这样工作的,对两个操作数都应用第一个操作符,然后,将结果赋值给左操作数。例如,x+=5等同于x = x + 5。

x -= 5等同于x = x - 5

x <<= 5等同于x = x << 5

x &= 5得到的结果和x = x & 5相同

**2.8.8 整数按位操作符& | ^**
位操作符& | ^在两个操作数上执行位操作,操作数的类型必须能够转换为整数。&表示一个AND操作,|表示一个OR操作,^表示一个异或操作。例如:

0xFFFF & 0x0000 = 0x0000
0xF0F0 & 0xFFFF = 0xF0F0
0xFFFF | 0x000F = 0xFFFF
0xFFF0 ^ 0x00FF = 0xFF0F

**2.8.9 逻辑操作符 & | ^**
逻辑操作符& | ^在两个操作数上执行逻辑操作,这两个操作数能够转换为布尔类型。&表示一个AND操作。|表示一个OR操作,^表示一个异或操作。例如:

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

2.8.10 操作符优先级
在大多数程序中,一个表达式中常常会出现多个操作符,例如:

int a = 1; 
int b = 2; 
int c = 3; 
int d = a + b * c;
在代码执行之后,结果是多少?如果你说是9,那么你错了。实际结果是7。

乘法操作符*的优先级比加法操作符+的优先级高。因此,乘法将会在加法之前执行。如果你想要先执行加法,可以使用圆括号:

int d = (a + b) * c;

后者将会把9赋值给d。

表2.5按照优先级顺序列出了所有的操作符。同一行中的操作符,具有相同的优先级。

表2.5 操作符优先级

<div style="text-align: center">
 <img src="https://yqfile.alicdn.com/cd6a57893da6f4d2cfa38f0e103022547265987a.png" >
</div>

注意,圆括号具有最高的优先级。圆括号也可以使得表达式更为清晰。例如,考虑如下的代码:

int x = 5;
int y = 5;
boolean z = x * 5 == y + 20;

比较之后的z值为真。然而,这个表达式还远不够清晰,你可以使用圆括号重新编写最后一行。

boolean z = (x * 5) == (y + 20);

这并不会改变结果,因为*和+拥有比==更高的优先级,但是,这会使得表达式更为清晰。

**2.8.11 提升**
一些一元操作符(例如+、−和~)和二元操作符(例如,+、−、*和/),会导致自动提升(automatic promotion),例如,演变为一种更宽的类型,如从byte类型到int类型。考虑如下的代码:

byte x = 5;
byte y = -x; // error

第2行会莫名其妙地导致一个错误,即便一个byte类型可以容纳-5。其原因是,一元操作符-导致-x的结果提升为int。要修正这个问题,要么将y修改为int,要么像下面这样执行一次显式的收窄转换:

byte x = 5;
byte y = (byte) –x;

对于一元操作符来说,如果操作数的类型是byte、short或char,结果提升为int类型。

对于二元操作符来说,提升规则如下:

如果任何的操作数类型为byte或short,那么,两个操作数都将转换为int,并且结果会是一个int。
如果任何的操作数的类型为double,那么,另一个操作数转换为double,并且结果将会是一个double。
如果任何的操作数的类型为float,那么,另一个操作数转换为float,并且结果将会是一个float。
如果任何的操作数的类型是long,那么,另一个操作数转换为long,并且结果将会是一个long。
例如,如下的代码将会导致一个编译错误:

short x = 200;
short y = 400;
short z = x + y;

可以通过将z修改为int,或者对x+y执行一次显式的收窄转换,从而修正这个问题。

short z = (short) (x + y);