资源描述
2.3运算符和表达式
运算符指明了对各种类型的操作数所进行的运算。Java提供了丰富的运算符,Java语言的基本运算符分为算术运算符、逻辑运算符、关系运算符和位运算符4大类。同时也可以使用圆括号将一个表达式的几个部分组合到一起,并根据标准的代数规则建立优先关系。此外,Java还提供了余数运算符(又称取模运算符)生成两个整数相除所得的余数。
表达式是由标识符、常量、变量和运算符组成,是程序的基本组成部分。
2.3.1优先级、结合性以及单/双目运算
1.优先级、结合性
运算符的优先级决定了表达式中不同运算执行的先后顺序。如关系运算符的优先级要高于逻辑运算符的,x>y&&!z相当于(x>y)&&(!z)。
运算符的结合性决定了并列的相同运算的先后执行顺序。如对于左结合的“+”,a+b+c等价于(a+b)+c;对于右结合的“!”,!!x等价于!(!x)。
表2.6 Java运算符的优先级和结合性
优先级
描述
运算符
结合性
1
最高优先级
.、[]、()
左/右
2
单目运算
=、~、!、++、--强制类型转换符
右
3
算数乘除运算
*、/、!、%
左
4
算数加减运算
+、-
左
5
移位运算
>>、<<、>>>
左
6
大小关系运算
<、<=、>、>=
左
7
相等关系运算
==、!=
左
8
按位与,非简洁与
&
左
9
按位异或运算
^
左
10
按位或,非简洁或
|
左
11
简洁与
&&
左
12
简洁或
||
左
13
三目条件运算
?:
右
14
简单、复杂赋值
=运算符=
右
大多数人在第一次学习计算机语言时,经常把运算符优先级和运算顺序混淆。这两个概念实际上是不相同的。优先级规则帮助我们确定表达式中哪个运算符应该先跟哪些操作数执行这个运算符的运算。
例如,下面的代码中“*”运算符的操作数是x和(y+z):
w=x*(y+z);
计算的顺序规则不是决定何时进行特殊定义的运算,而是决定何时对操作数进行计算。
下面有三个帮助我们记住关于表达式如何被计算的规则:
(1)对于二元运算符,左边的操作数先计算,右边的操作数后计算;
(2)操作数总是在执行运算符定义的运算之前计算出来的;
(3)如果在方法中调用了多个参数,这些参数是用逗号隔开的,那么参数是严格按照从左到右的规则进行计算的。
2.单目/双目运算
运算符指明了对操作数所进行的运算。
运算符按其要求的操作数个数可分为:
单目运算符 如 ++、--
双目运算符 如 +、-、*、/、%
三目运算符 如 ? :
运算符按其功能分为七类:
算术运算符 如 +、-、*、/、%、++、--
关系运算符 如 >、<、>=、<=、==、!=
逻辑运算符 如 !、&&、||、^
位运算符 如 >>、<<、>>>、&、|、^、~
条件运算符 如 ? :
赋值运算符 如 =、+=、-=
其他 分量运算符、下标运算符[]、实例运算符instanceof。
2.3.2算术运算符
算术表达式由操作数和算术运算符组成。在算术表达式中,操作数只能是整型或浮点型数据,算术运算符作用于操作数,完成算术运算。Java的算术运算符分为一元运算符和二元运算符两种。
1.一元算术运算符
一元算术运算符涉及的操作数只有一个,一个算术表达式是由一个操作数和一个一元算术运算符构成的。一元算术运算符共有四种,如表2.7所示。
表2.7 一元算术运算符
运算符
名称
表达式
功能
+
一元加
+opt1
取正值
-
一元减
-opt1
取负值
++
增量
++opt1
加1
--
减量
--opt1
减1
一元加和一元减运算符仅仅表示某个操作数的符号,其操作结果为该操作数的正值或负值。增量运算符将操作数加1,如对浮点数进行增量操作,则结果为加1.0;减量运算符将操作数减1,如对浮点数进行减量操作,则结果为减1.0。
增量运算符为操作数加1,如下所示:
a++;
它等价于:
a+=1;
增量运算符和减量运算符放在变量的左边和放在变量的右边的作用是不同的,变量放在运算符的后面(如++a),就是先进行加运算后进行赋值运算。
下面的程序代码中y的结果是4:
int x=3;
int y=++x; //y=4 x=4
如果运算符出现在变量的右边,则在赋值之后执行运算。注意,在两种情况下x的值都是7:
int x=3;
int y=x++; //y=3 x=4
类似于增量运算,减量操作符(--)是从变量上减1,并且运算符放在变量的左边和右边所得的结果也是不同的。下面是几个单目运算符的例子:
int a1=1,a2=1,a3=1,a4=1;
int b1,b2,b3,b4;
b1=++a1; //a1先加1,再赋于b1,b1=2,x1=2
b2=a2++; //a2先赋于b2,再自增1,b2=1,a2=2
b3=--a3; //a3先减1`,再赋于b3,b3=0,x3=0
b4=a4--; //a4先赋于b4,再自减1,b4=1,a4=0
程序2.2 SOption.java
public class SOption{
public static void main(String[] args){
int a=3;
int b=1;
int c;
c=a++*3;
System.out.println("a="+a);
System.out.println("c="+c);
c=++a*3;
System.out.println("a="+a);
System.out.println("c="+c);
c=a--*3;
System.out.println("a="+a);
System.out.println("c="+c);
c=--a*3;
System.out.println("a="+a);
System.out.println("c="+c);
}
}
输入结果:
a=4
c=9
a=5
c=15
a=4
c=15
a=3
c=9
2.二元算术运算符应有两个操作数,两个操作数加一个二元算术运算符可构成一个算术表达式。二元运算符共有5种,如表2.8所示。
表2.8 二元算术运算符
运算符
表达式
名称及功能
+
opt1+opt2
加
-
opt1-opt2
减
*
opt1*opt2
乘
/
opt1/opt2
除
%
opt1%opt2
模数除(求余)
二元算术运算符适用于所有数值型数据类型,包括整型和浮点型。值得注意的是:若操作数全为整型,那么,只要其中有一个为long型,则表达式结果也为long型;其他情况下,即使两个操作数全为byte型或short型,表达式结果也为int型;若操作数为浮点型,则只要其中有一个为double型,表达式结果就为double型,只有两个操作数全是float型或其中一个是float型而另外一个是整型时,表达式结果才是float型。另外还应该注意,当“/”运算和“%”运算中除数为0时,会产生异常。
在以上提到的这些运算中我们可能对取模运算不太熟悉。取模运算就是求出被模数除所剩的余数。例如,4%2的余数为0,因为4能够被2整除。而5%2的结果是1,因为5被2除的结果是2,余1。下面是几个双目运算的例子:
6+4=10;6+4.0=10.0;
6-4=2;6-4.0=2.0;
6*4=24;6*4.0=24.0;
3/2=1;3.0/2=1.5;
6%2=0;8%6=2;
与C/C++不同,对取模运算符号“%”来说,其操作数可以为浮点数,例如11.1%10=1.1。
程序2.3 DOption.java
import java.util.*;
public class DOption {
// Create a shorthand to save typing:
static void prt(String s){
System.out.println(s);
}
static void pInt(String s, int i){
prt(s + " = " + i);
}
// shorthand to print a string and a float:
static void pFrt(String s, float f){
prt(s + " = " + f);
}
public static void main(String[] args){
// 创建一个随机数生成器;
// seeds with current time by default:
Random rand = new Random();
int i, j, k;
// '将最大值限制到99;
j = rand.nextInt() % 100;
k = rand.nextInt() % 100;
pInt("j",j); pInt("k",k);
i = j + k; pInt("j + k", i);
i = j - k; pInt("j - k", i);
i = k / j; pInt("k / j", i);
i = k * j; pInt("k * j", i);
i = k % j; pInt("k % j", i);
j %= k; pInt("j %= k", j);
// Floating-point number tests:
float u,v,w; // applies to doubles, too
v = rand.nextFloat();
w = rand.nextFloat();
pFrt("v", v); pFrt("w", w);
u = v + w; pFrt("v + w", u);
u = v - w; pFrt("v - w", u);
u = v * w; pFrt("v * w", u);
u = v / w; pFrt("v / w", u);
//同样适用于 char, byte, short, int, long and double类型:
u += v; pFrt("u += v", u);
u -= v; pFrt("u -= v", u);
u *= v; pFrt("u *= v", u);
u /= v; pFrt("u /= v", u);
}
}
输出结果:
j = -8
k = 96
j + k = 88
j - k = -104
k / j = -12
k * j = -768
k % j = 0
j %= k = -8
v = 0.13120472
w = 0.07683301
v + w = 0.20803773
v - w = 0.054371715
v * w = 0.0100808535
v / w = 1.7076609
u += v = 1.8388656
u -= v = 1.7076609
u *= v = 0.22405317
u /= v = 1.7076609
注:由于是利用随机数生成器来产生的数据,所以每次的执行结果不尽相同。
2.算术运算中需要注意的问题有以下几条:
(1)只有整型类型(int,long,short)数据才能进行取模运算,float型和double型数据不能进行取模运算;
(2)取模运算%是求两个数相除的余数。如100%9的结果是1;
(3)两个整型类型的数据做除法时,结果是取整数商。如果要保留小数部分,则应该对除法运算的操作数做强制类型转换。如5/2的结果是2而不是2.5;
(4)在算术表达式中,如果出现不同类型的数据混合运算时,表达式类型为存储长度最大、精度最高的数据类型;
(5)表达式中有括号的,应先求括号中表达式的操作数的值。因此,括号通常用于按程序员的意愿,强行按某一顺序进行求值。我们认为括号具有“最高的优先级”。在嵌套括号的情况下,由内层向外层求值;
(6)如果一个表达式中包含了几个乘、除和取模运算,那么操作运算按照从左到右的顺序求值。乘、除和取模都在同样的优先级上;
(7)算术运算符的操作数必须是数字类型的,不能在布尔型上使用算数运算符,但可以在char类型上使用,是因为Java中的char类型本质上是int型的一个子集;
(8)如果一个表达式中同时包含了几个加法和减法运算,那么操作运算符按照从左到右的顺序求值;
2.3.3关系运算符
关系运算符用来比较两个数据,确定一个操作数与另一个操作数之间的关系,即进行关系运算,关系运算符如表2.9所示。返回布尔类型的值为true或false。若关系是真实的,关系表达式会生成true(真);若关系不真实,则生成false(假)。
关系运算符包括小于(<)、大于(>)、小于或等于(<=)、大于或等于(>=)、等于(==)以及不等于(!=)。等于和不等于适用于所有的数据类型,但是其他的关系运算符不适用于boolean类型。
表2.9 关系运算符
运算符
名称
示例
说明
>
大于
a>b
a大于b
>=
大于等于
a>=b
a大于等于b
<
小于
a<b
a小于b
<=
小于等于
a<=b
a小于等于b
==
等于
a==b
a等于b
!=
不等于
a!=b
a不等于b
注意:
(1)关系运算符<、<=、>、>=的优先级相同,而且按照从左到右的顺序结合。两个相等性操作“==”和“!=”运算符具有相同的优先级,但要比其他关系运算符的优先级低;
(2)不能在两个浮点数之间作“等于”比较,因为浮点数使用二进制表达后不是精确值;
(3)切记不可将相等性运算符“==”同赋值运算符“=”混淆。按照传统的逻辑,相等性运算符应该被读为“等于”。相反,赋值运算符应该读成“获得”、“获得……的值”或者“被赋予值……”。
例如:
6>5; //结果为true
6.5>5; //结果为true
‘x’==’y’; //结果为false
true>false; //错误,boolean型数据只能比较==或!=,不能比较大小
例2.4 ReOption.java
public class ReOption{
public static void main(String[] args){
int a,b;
boolean c,d,e;
a=1;
b=2;
c=(a>=b);
d=(a!=b);
e=(a==b);
System.out.println("c="+c);
System.out.println("d="+d);
System.out.println("e="+e);
System.out.println("c!=d"+(c!=d));
System.out.println("c==d"+(c==d));
}
}
输出结果:
c=false
d=true
e=false
c!=dtrue
c==dfalse
2.3.4逻辑运算符
布尔逻辑运算符用来连接关系表达式,对关系表达式的值进行布尔逻辑运算,由关系表达式加布尔逻辑运算符构成了布尔逻辑表达式。布尔逻辑运算符共有三种,即逻辑与(&&)、逻辑或(||)和逻辑非(~),其操作结果也都是布尔型的,如表2.10所示。
表2.10 布尔逻辑运算符
A
B
a&&b
a||b
!a
False
False
false
false
true
False
True
false
true
true
True
False
false
true
false
True
True
true
true
false
注意:
(1)“&”与“&&”虽然名称相同,但功能有细微差别,“&”与“|”在运算时,符号两边的表达式都进行计算之后才得出值,而“&&”与“||”在运算时,如果运算符左边的值决定整个表达式的值,运算符右边的表达式不再计算。如对于a&&b,如果a为“假”,则不再计算b的值,而得到a&&b的值为“假”。而对于a&b,分别计算a和b之后,再计算a&b的值是“真”还是“假”;
(2)如果想要在两个条件都为真的前提下才能选择一个特定的执行路径,就使用逻辑运算符“&&”。在包含“&&”运算符的表达式中,首先求得是“&&”运算符左边的简单条件,如果左侧的值为“真”,才会继续对右侧进行求值。只有“&&”的左、右侧的结果都为真时,才执行下一条语句,否则将跳过;
(3)“||”运算符是在两个条件中只要有一个为“真”的情况下,整个表达式的值就为“真”;
(4)“&&”运算符的优先级要比“||”运算符高。两个运算符的结合顺序都是从左到右进行的;
(5)“!”运算符用于逆转条件。不同于“&&”运算符和“||”运算符的是,“!”运算符只要求一个操作数;而前面两个运算符都要同时又两个操作数。也就是说,“&&”和“||”均为双目运算符,“!”是单目运算符。
程序2.5 LogicalOption1.java
import java.util.*;
public class LogicalOption1{
public static void main(String[] args) {
Random rand = new Random();
int i = rand.nextInt() % 100;
int j = rand.nextInt() % 100;
prt("i = " + i);
prt("j = " + j);
prt("i > j is " + (i > j));
prt("i < j is " + (i < j));
prt("i >= j is " + (i >= j));
prt("i <= j is " + (i <= j));
prt("i == j is " + (i == j));
prt("i != j is " + (i != j));
// Treating an int as a boolean is
// not legal Java
//prt("i && j is " + (i && j));
//prt("i || j is " + (i || j));
//prt("!i is " + !i);
prt("(i < 20) && (j < 20) is "+ ((i < 20) && (j < 20)) );
prt("(i < 20) || (j < 20) is "+ ((i < 20) || (j < 20)) );
}
static void prt(String str) {
System.out.println(str);
}
}
输出结果:
i = -4
j = 96
i > j is false
i < j is true
i >= j is false
i <= j is true
i == j is false
i != j is true
(i < 20) && (j < 20) is false
(i < 20) || (j < 20) is true
程序2.6 LogicalOption2.java
public class LogicalOption2{
public static void main(String[] args){
int x=2,y=3;
boolean t1,t2,t3;
t1=x<y||++x<y;
System.out.println("t1="+t1+",x="+x);
t2=x>y&&x++<y;
System.out.println("t2="+t2+",x="+x);
t3=x>y|x++<y;
System.out.println("t3="+t3+",x="+x);
}
}
输出结果:
t1=true,x=2
t2=false,x=2
t3=true,x=3
2.3.5位运算符
位运算符是以二进制位(bit)为单位进行运算的,操作数和结果都是整数型数据。位运算符如表2.11所示。
位运算符中,除“~”以外,其余均为二元运算符。操作数只能为整型和字符型数据。Java语言中是用补码来表示二进制数据,最高位为符号位,正数符号位为0,负数为1。
表2.11 位运算符
运算符
名称
示例
功能说明
~
位反
~x
将x按比特位取反
&
位与
x&
x和y按比特位取与
|
位或
x|y
x和y按比特位取或
^
位异或
x^y
x和y按比特位取异或
>>
右移
x>>a
x的比特位右移a位
<<
左移
x<<a
x的比特位左移a 位
>>>
(无符号)右移
x>>>a
x的比特位右移a位,左边空出位填零
1.按位取反运算符“~”
“~”是一元运算符,对数据的每个二进制位取反,即把1变成0,把0变为1。
例:byte I=13;转换成二进制为0000 1101,则~I=1111 0010,该补码的真值为-14,即~I=-14。
2.按位与运算符号“&”
“&”运算的两个数据,如果对应位都为1,则该位结果为1,否则为0。
例:byte I=13;转换成二进制为0000 1101;byte J=12;转换成二进制为0000 1100,则I&J=0000 1100,该补码的真值为12,即I&J=12。
按位与可以用来对特定位清零(通过与特定位为0,其他位为1的标志码进行&运算),还可以取一个数中的指定位(通过与特定位为1,其他位为0的标志码进行&运算)。
3.按位或运算符号“|”
参与“|”运算的两个数据,如果对应位有一个为1,则该位结果为1,只有对应位都为0时结果为0。
例:byte I=13;转换成二进制为0000 1101,byte J=12;转换成二进制为0000 1100,则I|J=0000 1101,把补码转换成真值为13。
按位或可以用来对特定位置1(通过与特定位为1,其他位为0的标志码进行|运算)。
4.按位异或运算符号“^”
参与“^”运算的两个数据,如果对应位相同,则该位结果为0,否则结果为1。
例:byte I=13;转换成二进制为0000 1101,byte J=12;转换成二进制为0000 1100,
则I^J=0000 0001,把补码转换成真值为1。
按位异或可以用来对特定位取反(通过与特定位为1,其他位为0的标志码进行^运算)。
5.左移运算符“<<”
“<<”用来将一个数据的各二进制位全部左移若干位,右端补0。
例:byte I=13;转换成二进制为0000 1101,I=I<<2;将I左移2位,右端补0,则I=0011 0100,相当于I=52。在不产生溢出的情况下,左移一位相当于乘2,而且左移来实现乘法比乘法运算速度要快。
6.右移运算符“>>”
“>>”用来将一个数据的各二进制位全部右移若干位,低位被舍弃,最高位移入原来的高位。
例如,byte I=13;转换成二进制为0000 1101,I=I>>2;将I右移2位,左端补原来的高位0,则I=0000 0011,相当于I=3。右移一位相当于除2取商,这种右移实现除法操作要比除法运算速度要快。
7.无符号右移运算符“>>>”
“>>>”用来将一个数据的各二进制位无符号右移若干位,移出的低位被舍弃,最高位补0。例:byte I=13;转换成二进制为0000 1101,I=I>>>2;将I右移2位,左端补0,则I=0000 0011,相当于I=3。
8.不同长度的数据进行位运算
不同长度的数据进行位运算时,首先把长度较短的数据左端补符号位(即最高位,正数补0,负数补1),直到两个数据长度相同,然后再进行按位运算。
程序2.7 BitShiftOption.java
public class BitShiftOption{
public static void main(String[] args){
int i = -1;
i >>>= 10;
System.out.println(i);
long l = -1;
l >>>= 10;
System.out.println(l);
short s = -1;
s >>>= 10;
System.out.println(s);
byte b = -1;
b >>>= 10;
System.out.println(b);
b = -1;
System.out.println(b>>>10);
}
}
输出结果:
4194303
18014398509481983
-1
-1
194303
2.3.6赋值运算符
赋值表达式的组成是:在赋值运算符的左边是一个变量,右边是一个表达式。表达式的值的类型应与左边的变量类型一致或可以转换为左边的变量类型。
赋值运算符分为赋值运算符(=)和扩展赋值运算符两种。
最简单的赋值运算符就是标准的赋值运算符。这个运算符也叫做取(gets)运算符,因为是左边的对象取右边的值。
“=” 赋值运算符
另外还有一些扩展赋值运算符。扩展赋值运算符的特点是:可以使程序表达简练,并且还可以提高程序的编译速度。
+= 加并赋值运算符
-= 减并赋值运算符
*= 乘并赋值运算符
/= 除并赋值运算符
%= 取模并赋值运算符
&= 按位与并赋值运算符
|= 按位或并赋值运算符
^= 按位异或并赋值运算符
<<= 循环左移并赋值运算符
>>= 循环右移并赋值运算符
>>>= 无符号右移并赋值运算符
变量的当前值是决定赋值后值的一个因素,除赋值运算符以外,算术赋值运算符就好像是将运算符左边的变量放在右边来执行相应的运算,故下面两行是完全等价的语句:
a=a+5;
等价于:
a+=5;
程序2.8 AssignmentOption.java
public class AssignmentOption{
public static void main(String[] args){
int a=2;
System.out.println("1:a="+a);
a+=3;
System.out.println("2:a="+a);
a-=3;
System.out.println("3:a="+a);
a*=3;
System.out.println("4:a="+a);
a/=3;
System.out.println("5:a="+a);
a%=3;
System.out.println("6:a="+a);
a&=3;
System.out.println("7:a="+a);
a|=3;
System.out.println("8:a="+a);
a^=3;
System.out.println("9:a="+a);
a<<=3;
System.out.println("10:a="+a);
a>>=3;
System.out.println("11:a="+a);
a>>>=3;
System.out.println("12:a="+a);
}
}
输出结果:
1:a=2
2:a=5
3:a=2
4:a=6
5:a=2
6:a=2
7:a=2
8:a=3
9:a=0
10:a=0
11:a=0
12:a=0
程序2.9 AssignmentOption1.java
class Number{
int i;
}
public class AssignmentOption1{
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
n1.i = 9;
n2.i = 47;
System.out.println("1: n1.i: " + n1.i +", n2.i: " + n2.i);
n1 = n2;
System.out.println("2: n1.i: " + n1.i +", n2.i: " + n2.i);
n1.i = 27;
System.out.println("3: n1.i: " + n1.i +", n2.i: " + n2.i);
}
}
输出结果:
1: n1.i: 9, n2.i: 47
2: n1.i: 47, n2.i: 47
3: n1.i: 27, n2.i: 27
程序2.10 AssignmentOption2.java
class Number{
int i;
}
public class AssignmentOption2{
public static void main(String[] args){
Number n1 = new Number();
Number n2 = new Number();
n1.i = 9;
n2.i = 47;
System.out.println("1: n1.i: " + n1.i +", n2.i: " + n2.i);
n1.i = n2.i;
System.out.println("2: n1.i: " + n1.i +", n2.i: " + n2.i);
n1.i = 27;
System.out.println("3: n1.i: " + n1.i +", n2.i: " + n2.i);
}
}
输出结果:
1: n1.i: 9, n2.i: 47
2: n1.i: 47, n2.i: 47
3: n1.i: 27, n2.i: 47
程序2.11 AssignmentOption3.java
public class AssignmentOption3{
public static void main(String[] args){
int a,b;
a=5;
b=4;
System.out.println("a="+a+",b="+b);
a=b;
System.out.println("a="+a+",b="+b);
a=3;
System.out.println("a="+a+",b="+b);
}
}
输出结果:
a=5,b=4
a=4,b=4
a=3,b=4
2.3.7条件运算符
条件运算符(?:)是一个特殊的操作符,它支持条件表达式。即一个简单的双重选择if……else语言的简缩。条件运算符为三元运算符,它的一般形式为:
expression?statement1:statement2; 即:表达式?语句1:语句2;
其中表达式expression的值应该为一个布尔值,如果该值为true,则执行语句statement1,否则执行语句statement2,而且语句statement1和statement2需要返回到相同的数据类型,该类型不能是void。例如:
int x=1,y=2,z=3;
int i=x<0?y:z;
该例子中,如果x<0,则i=2,否则i=3。
如果要通过测试某个表达式的值来选择两个表达式中的一个时,用条件运算符来实现是一种简便的方法,这样它就可以实现条件语句的功能。
例2.12 ConditionOption.java
//条件运算实例。
public class ConditionOption{
public static void main(String[] args){
int x=1,y=2,z=3;
int i=x<0?y:z;
System.out.println("i="+i);
if(x<0)
i=y;
else
i=z;
System.out.println("i="+i);
}
}
输出结果:
i=3
i=3
2.3.8其他运算符
Java语言还包括其他一些运算符,如分量运算符“.”,实例运算符“instanceof”,内存分配运算符“new”,强制类型转换运算符“(类型)”,方法调用运算符“()”,取数组元素运算符“[]”。
展开阅读全文