3.7运算符
3.7.2自增自减运算符
在程序中,变量的加1、减1操作是经常会碰到的。Java和其他语言一样,给我们提供了自增、自减运算符来方便的完成这些操作。“++”表示自增,“--”表示自减。我们看一个例子:
int a = 3; a++; System.out.println(a);// 结果是4 int b = 8; b--; System.out.println(b);// 结果是7
自增自减运算符会改变变量的值,因此它们的操作数不能是数值。例如5++就是一条非法的语句。另外这2个运算符还有另外一种形式,就是放在操作数的前面,我们看一个例子:
int a = 3; ++a; System.out.println(a);// 结果是4 int b = 8; --b; System.out.println(b);// 结果是7
另外,我们还可以把变量和自增自减运算符当做一个整体,参与到赋值语句或者是运算表达式中。例如:
int a1 = 3; int b = a1++; int a2 = 3; int c = ++a2; int a3 = 3; int d = a3++ * 5; int a4 = 3; int e = ++a4 * 5; System.out.println(b);// 结果是3 System.out.println(c);// 结果是4 System.out.println(d);// 结果是15 System.out.println(e);// 结果是20
我们可以看到,无论是赋值语句,还是运算表达式,当++在操作数后面的时候,都是先赋值或先参与运算,然后再自己增加1。当++在操作数前面的时候,都是先自己增加1,再赋值或参与运算。对于自减也是一样。可以总结一个规律:运算符在前面时先起作用,运算符在后面时后起作用。
这里有一个小笑话,我们都知道C++语言,这门语言把++运算符用到语言命名上了,本意是C语言的扩展。但是根据运算法则,运算符在后面后起作用,因此反对C++的程序员说我们使用的它的时候还没起作用呢,应该命名为++C才对。
3.7.3关系运算符
前面的运算符对应数学中的加减乘除取余等运算,关系运算符对应的是比较2个数的关系,关系有等于、不等于、大于、大于等于、小于和小于等于。列表如下:
运算符 |
表达式 |
结果(假设a=15,b=10) |
== |
a==b |
false |
!= |
a!=b |
true |
> |
a>b |
true |
>= |
a>=b |
true |
< |
a<b |
false |
<= |
a<=b |
false |
使用和结果都很简单,没什么可讲的。
3.7.4逻辑运算符
逻辑运算包括3个:逻辑与、逻辑或、逻辑非,对应的运算符和说明如下:
运算符 |
表达式 |
说明 |
&& |
expression1 && expression2 |
逻辑与。当且仅当两个操作数都为真,条件才为真 |
|| |
expression1 || expression2 |
逻辑或。如果任何一个为真,条件为真。 |
! |
!expression1 |
逻辑非。用来反转操作数的逻辑状态。 |
例如:
boolean a = true; boolean b = false; boolean c = a && b;// 结果是false boolean d = a || b;// 结果是true boolean e = !a;// 结果是false
需要注意的是,逻辑与和逻辑或都是采用“短路”的方式进行运算的。就是某一个表示的结果已经能够确定整个运算表达式的结果的时候,剩下的表达式就不用再进行计算了。
例如:
int a = 5; boolean b = (a < 4) && (a++ < 10);// a<4结果是false,整个表达式结果就是false,因此a++不会运算,a的值依然是5 int c = 5; boolean d = (c < 6) || (c++ < 10);// c<6结果是true,整个表达式结果就是true,因此a++不会运算,c的值依然是5
3.7.5条件运算符
条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量,表达式为:
condition ? expression1 : expression2
当条件condition为真时,计算expression1并返回,否则计算expression2并返回。
例如:
int a = 5; int b = 10; int c = 20; int d = a < b ? c + a : c + b;// a<b结果是true,因此d=c+a=25
3.7.6位运算符
在Java中,处理整型数值时,可以直接对数值的二进制的各个位进行操作,我们先列一个表,然后再进行例子演示:
操作符 |
说明 |
& |
按位与操作符。如果相对应位都是1,则结果为1,否则为0 |
| |
按位或操作符。如果相对应位都是0,则结果为0,否则为1 |
^ |
按位异或操作符。如果相对应位值相同,则结果为0,否则为1 |
~ |
按位取反操作符。翻转操作数的每一位,即0变成1,1变成0 |
<< |
按位左移运算符。左操作数按位左移右操作数指定的位数 |
>> |
按位右移运算符。左操作数按位右移右操作数指定的位数 |
>>> |
按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 |
我们看一个例子:
int a = 55; // 二进制为 0011 0111 int b = 18; // 二进制为 0001 0010 int c = a & b;// 结果二进制为 0001 0010,18 int d = a | b;// 结果二进制为 0011 0111,55 int e = a ^ b;// 结果二进制为0010 0101,37 int f = ~a;// 结果二进制为 1100 1000,-56 int g = a << 2;// 结果二进制为 1101 1100,220 int h = a >> 2;// 结果二进制为 0000 1101,13 int i = a >>> 2;// 结果二进制为 0000 1101,13
对于按位与&运算,有个小技巧,就是可以快速判断一个整数m的二进制从右往左数第n位是否为1,判断方法为看m&2n-1的值,值为0,则第n位为0,值为2n-1,则第n为为1。
对于<<运算,要注意几点:
- 对byte、short、char型进行左移运算,移位之前,它们会自动转换为int
- 右侧的参数,需要进行模32运算,其实就是保证右侧的参数小于32(当左侧是long,则模64,保证右侧的参数小于64),因为左移超过32没有意义。
- 因为右侧参数不可能超过32(64),所以其实符号位是不变的。
- 左移n位,其实相当于乘以2n(由十进制转二进制公式可以得出)
例如:
20的二进制补码:0001 0100,左移两位后:0101 0000,结果是80
-20的二进制补码:1110 1100,左移两位后:1011 0000,结果是-80
对于>>运算,需要注意几点:
- 右移是带符号移动的,即如果是正数,高位补0,如果负数,高位补1
- 右移n位,相当于除以2n取整
例如:
20的二进制补码:0000 0000 0000 0000 0000 0000 0001 0100
右移两位后:0000 0000 0000 0000 0000 0000 0000 0101,结果是5
-20的二进制补码:1111 1111 1111 1111 1111 1111 1110 1100
右移两位后:1111 1111 1111 1111 1111 1111 1111 1011,结果是-5
对于>>>运算,需要注意几点:
- 右移是不带符号的,即不管正负,高位都补0
例如:
20的二进制补码:0000 0000 0000 0000 0000 0000 0001 0100
右移两位后:0000 0000 0000 0000 0000 0000 0000 0101,结果是5
-20的二进制补码:1111 1111 1111 1111 1111 1111 1110 1100
右移两位后:0011 1111 1111 1111 1111 1111 1111 1011,结果是1073741819
3.7.7赋值运算符
Java还支持把一些二元运算符和赋值符号联合起来使用,我们把它们称为赋值运算符,归结如下:
操作符 |
说明 |
举例 |
+= |
左操作数加右操作数,结果赋值给左操作数 |
C += A即C = C + A |
-= |
左操作数减右操作数,结果赋值给左操作数 |
C -= A即C = C - A |
*= |
左操作数乘右操作数,结果赋值给左操作数 |
C *= A即 C = C * A |
/= |
左操作数除以右操作数,结果赋值给左操作数 |
C /= A即C = C / A |
%= |
左操作数对右操作数取模,结果赋值给左操作数 |
C %= 2即C = C % 2 |
<<= |
左操作数左移右操作数,结果赋值运算符 |
C <<= 2即C = C << 2 |
>>= |
左操作数右移右操作数,结果赋值运算符 |
C >>= 2即C = C >> 2 |
>>>= |
左操作数右移右操作数,结果赋值运算符 |
C >>>= 2即C = C >>> 2 |
&= |
左操作数和右操作数按位与,结果赋值给左操作数 |
C &= 2即C = C & 2 |
^= |
左操作数和右操作数按位异或,结果赋值给左操作数 |
C ^= 2即C = C ^ 2 |
|= |
左操作数和右操作数按位或,结果赋值给左操作数 |
C |= 2即C = C | 2 |
3.7.8运算优先级
Java可以在一个表达式中进行多个运算,这就涉及到运算符优先级问题了。下表按优先级从高到底给出运算符的排序(排在一行的优先级相同):
操作符 |
结合性 |
[] 、()、 .(点操作符) |
从左向右 |
++、--、 +(一元运算)、-(一元运算)、!、~ |
从右向左 |
*、/、% |
从左向右 |
+、- |
从左向右 |
<<、>>、.>>> |
从左向右 |
<、<=、>、>= |
从左向右 |
==、!= |
从左向右 |
& |
从左向右 |
^ |
从左向右 |
| |
从左向右 |
&& |
从左向右 |
|| |
从左向右 |
?: |
从右向左 |
=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=、>>>= |
从右向左 |
是不是看着头都大了?笔者也头大,笔者强烈不推荐在一个表达式中使用多个运算符,可读性太差了。
3.7.9数值类型转换
在程序的运行过程中,经常会碰到一种数值转换为另一种数值类型。有时候是程序自动转换的,有时候是我们用代码显性转换的。下图列出了数值类型转换的过程:
需要注意的是,对于int转float、long转float,long转double,是可能会丢失精度的。例如:
int n = 123456789; float f = n;// n包含的位数比float多,结果f为1.23456792E8
3.7.9.1自动类型转换
自动类型转换经常发生在2个不同类型操作数进行二元操作时。例如:
int n = 123; float f = 456.3f; float ff = n + f;// 自动将n转换为float,然后相加,结果是579.3
对于这种自动转换,遵循如下规则:
- 如果两个操作数中有一个double,则另一个会转换为double。
- 否则如果有一个操作数是float,另一个会转换为float。
- 否则如果有一个操作数是long,另一个会转换为long。
- 否则两个操作数都被转换为int。
3.7.9.2强制类型转换
上面我们知道了自动类型转换,有时候我们想把double转换为int,可以吗?Java中是允许这种数值转换的,方法就是用强制类型转换,但是会丢失精度。强制类型转换的格式为:
(type)value
type是最终想要的类型,value是被强制转换的原数值,例如:
float f = 456.3f; int nf = (int) f;// 截断小数部分,结果是456
需要注意的是,如果将一个数值从一种类型转换为另一种类型,但是又超出目标类型的范围,结果就会无法预料。例如把300转换为byte类型:
byte b = (byte) 300;// 结果是44