cqh-运算符

5. 运算符

内容导视:

  • 算术运算符
  • 赋值运算符
  • 关系运算符
  • 逻辑运算符
  • 条件运算符
  • 按位运算符
  • 移位运算符
  • 运算符优先级

加油,还不算晚。

5.1 算术运算符

内容导视:

  • 四则运算
  • 求余数
  • 自增自减

5.1.1 四则运算

这个不用过多介绍,分别是加减乘除,操作数是数值类型或者能自动拆箱成数值类型,与初等数学一致,直接使用即可。

 int a = 5;
 int b = 2;
 
 int add = a + b;// 7
 int sub = a - b;// 3
 int mult = a * b;// 10
 int divide = a / b;// 2

注意 int 类型只能保存整数,会被削去小数部分。

此外 “+” 除了能够计算加法,操作数可以是 String 类型,用于拼接字符串,只要有一边操作数是 String 类型,则最终结果也是 String 类型,之前已经说过:

 System.out.println(100 + 2 + "字符串" + 3 * 8);

“102字符串24”;从左至右,遇到字符串就拼接,拼接后还是字符串;乘法优先于加法。

5.1.2 求余数

求余数也称取模。得到的余数一定小于除数。

如 10 % 3 = 1、11 % 3 = 2、12 % 3 = 0;

 int i = 11 % 4;

11 除以 4,商为 2,余数为 3;所以 i = 3。

% 的本质

a % b = a - a / b * b

知道了上面的式子,自己试着算一下吧:

 System.out.println(-10 % 3);
 System.out.println(10 % -3);
 System.out.println(-10 % -3);

计算结果如下:

 -10 % 3 = -10 - (-10) / 3 * 3
         = -10 - (-3) * 3
         = -1
         
 10 % -3 = 10 - 10 / (-3) * (-3)
         = 10 - (-3) * (-3)
         = 1
         
 -10 % -3 = -10 - (-10) / (-3) * (-3)
          = -10 - 3 * (-3)
          = -1

5.1.3 自增自减

++ 是让变量保存的值,自加一。

 int i = 10;
 i++;
 System.out.println(i);// i = 11
 int i = 10;
 ++i;
 System.out.println(i);// i = 11

++ 放在变量前后的区别

++ 放在变量前:

 int i = 10;
 int j = ++i;
 System.out.println(j);// j = 11

等价于

int i = 10;
i = (int)(i + 1);// 先自加一,后赋值
int j = i;
System.out.println(j);// j = 11

++ 放在变量后:

int i = 10;
int j = i++;
System.out.println(j);// j = 10
int i = 10;
int j = i;// 先返回值,后自加一
i = (int)(i + 1);
System.out.println(j);// j = 10

结论:

当 ++ 出现在变量前,先自加一,再返回值;所以 i 自加一等于 11,再赋值给 j,j 为 11;

当 ++ 出现在变量后,会先返回值,再自加一;把 i 赋值给 j 即 10,再 i 自加 1。

反正 i 一定是 11;使用 ++ 或 -- 时,不会改变运算结果的类型

例如:

byte b = 1;
b = b + 1;

会报错从 int 转成 byte 可能会有损失;

换成如下会自动强转成 byte 类型,但是要注意别超了 byte 的取值范围,否则强转后会有精度损失。

byte b = 127;
b++;//b = -128

-- 同理:

int i = 3;// i = 3
int j = i--;// j = 3、i = 2
int z = --j;// j = 2、z = 2

i = 2、j = 2、z = 2。

建议 ++、--,最好单独一行,很直观地就能判断是加一或减一,放在表达式中分不清谁前谁后容易让人困扰。

思考

int i = 10;
i = ++i;
System.out.println(i);

这道题,不是那么好讲,使用 javap -v 反编译(先弹出栈顶的数)看看就行,不用了解,没有人会这么用的。

什么是栈顶,就是容器最上面的一个元素。打个比方,一个空羽毛球筒,现在放入羽毛球 a(称为入栈),此时栈顶是羽毛球 a;现在又放入一个羽毛球 b,b 把 a 压住了,b 在 a 的上面,此时 b 就是栈顶;想要拿出 a,必须先拿出 b(称为弹栈,把最上面的元素取出来),这就是后进先出先进后出

cqh-运算符

对代码进行反汇编,反编译后得到的结果:

0: bipush 10 // 将 10 压入操作数栈中 {10}
2: istore_1 // 从操作数栈中弹出一个数也就是 10,存储到第 2 个变量槽 {} {?,10}
3: iinc 1, 1 // 第 2 个变量槽的值自增 1 {} {?,11}
6: iload_1 // 将局部变量表的第 2 个变量槽中的值复制到操作数栈顶 {11} {?,11}
7: istore_1 // 将 11 取出,存储到第 2 个变量槽中 {} {?,11}

人话就是:将 10 赋给变量 i,i 自增一为 11,将 11 赋给 i。

下一道题:

int i = 10;
i = i++;
System.out.println(i);

反编译部分节选:

// int i = 10;
0: bipush 10 // 将 10 压入操作数栈 {10}    
2: istore_1 // 将 10 弹出存储到第 2 个变量槽 {} {?,10}

3: iload_1 // 将第 2 个变量槽的值压入操作数栈 {10} {?,10}
// i++;
4: iinc 1, 1 // 第 2 个变量槽的值自增 1 {10} {?,11}
// i = 10;
7: istore_1	// 将 10 弹出存储到第 2 个变量槽 {} {?,10}
8: return

可以看到 10 被存储到操作数栈,但没有先赋给 i,而是 i++ 自增为 11 后,再将 10 赋给 i。

例 3:

byte b = 1;
byte b1 = b++;// b1 = 1,b = 2
byte b2 = --b;// b = 1,b2 = 1
System.out.println(b);// 1
System.out.println(b1);// 1
System.out.println(b2);// 1

反编译部分节选:

// byte b = 1;
0: iconst_1	# 将 1 压入栈中 {1}
1: istore_1	# 弹栈,存储到第 2 个变量槽中 {} {?,1}

// b1 = b, b = (byte)(b + 1);
2: iload_1	# 将第 2 个变量槽的值压入操作数栈 {1} {?,1}
3: iload_1	# 将第 2 个变量槽的值压入操作数栈 {1,1} {?,1}
4: iconst_1	# 将 1 压入栈中 {1,1,1} {?,1}
5: iadd		# 取出栈中最上面两个值相加,并将结果 2 压入栈中 {2,1} {?,1}
6: i2b		# 将 int 类型的值转为 byte,然后又扩展至 int 类型压入栈中 {2,1} {?,1}
7: istore_1	# 弹栈,存储到第 2 个变量槽中 {1} {?,2}
8: istore_2 # 弹栈,存储到第 3 个变量槽中 {} {?,2,1}

// b = (byte)(b - 1), b2 = b;
9: iload_1	# 将第 2 个变量槽的值压入栈中 {2} {?,2,1}
10: iconst_1# 将 int 类型的常量 1 压入栈中 {1,2} {?,2,1}
11: isub	# 取出栈中最上面两个值相减:2 - 1,并将结果 1 压入栈中 {1} {?,2,1}
12: i2b		# 将 int 类型的值转为 byte,... {1} {?,2,1}
13: istore_1# 弹栈,存储到第 2 个变量槽 {} {?,1,1}
14: iload_1	# 将第 2 个变量槽的值压入栈中 {1} {?,1,1}
15: istore_3# 弹栈,存储到第 4 个变量槽 {} {?,1,1,1}
16: return	# 结束。

5.2 赋值运算符

内容导视:

  • 基本赋值运算符
  • 扩展赋值运算符

5.2.1 基本赋值运算符

之前用过很多次了,通过 “=” 把值赋给变量,完成值传递。

int a = 10;// 10 被赋给了 int 类型的 a
int j = 10;
int i = j;// j 保存的 10 被赋给了 int 类型的 i

5.2.2 扩展赋值运算符

扩展赋值运算符:+=、-=、*=、/=、%=、...

只要学会了 +=,其它触类旁通。

int x = 5;
x += 2;// x = 7

相当于将自保存的值加 2:

int x = 5;
x = (int)(x + 2);// x = 7

使用扩展运算符,不会改变运算结果类型,与 ++ 一样,++ 就等同于 += 1。

看看下面的 i 的值为多少?

byte i = 8;
i *= 2;

i = (byte)(i * 2) = 16

例 2:

byte b = 64;
b *= 2;

b = (byte)(b * 2) = (byte)128 = -128;

例 3:

byte b = 23;
b %= 2;

b = (byte)(b % 2) = 1;

例 4:

byte b = 13;
b /= 2;

b = (byte)(13 / 2) = 6;

5.3 关系运算符

就是判断对错,得到一个结果。结果为真,返回 true,假:false。

使用的符号都是英文半角符号,字符之间没有空格。

!= != ! = 都错。

内容导视:

  • 数字比较运算符
  • 判等运算符

5.3.1 数字比较运算符

<、<=、>、>=,要求运算符两边是数值类型,或者可以自动拆箱为数值类型,得到的布尔字面量使用 boolean 类型的变量接收。

boolean result1 = 6 > 3;
boolean result2 = 9 < 6;
    
System.out.println(result1);// true
System.out.println(result2);// false

特殊情况

  • 如果有一边操作数是 NaN,则结果就是 false。
  • Infinity 是正无穷大,大于所有有穷值,-Infinity 是负无穷大,小于所有有穷值。

5.3.2 判等运算符

分为 == 和 !=,对应等于和不等于。[1]

数值类型的判等

当两边的操作数都是数值类型,或者其中之一是数值类型,且另一个可以自动拆箱转为数值类型,按数值大小比较。

System.out.println(5 == 5);// true,5 等于 5,返回 true
System.out.println(5 == new Integer(5));// true,Integer 自动拆箱为 5
System.out.println(5 != 4);// true      
System.out.println('a' == 95);// false,'a' 对应十进制整数为 97             

当判等运算符的两边之一是 NaN,则 == 结果为 false,!= 结果为 true,换言之 NaN 不等于任何数,包括它自己。如果 x != x 为 true,则 x 的值为 NaN。

final double NaN = 0.0 / 0.0;
System.out.println(NaN == NaN);// false
System.out.println(NaN != NaN);// true
System.out.println(NaN == 3);// false
System.out.println(NaN != 3);// true

Infinity 除了与自己比较时,== 为 true,与其它值比较,== 都为 false。

布尔类型的判等

当两边的操作数都是布尔类型,或者其中之一是布尔类型,且另一个可以自动拆箱转为布尔类型,如果两边都是 true 或者 false,则 == 结果为 true,否则为 false。

如果两边都是 true 或者 false,则 != 结果为 false,否则为 true。

System.out.println(true == true);// true
System.out.println(false == false);// true
System.out.println(true == false);// false

System.out.println(true != true);// false
System.out.println(false != false);// false
System.out.println(true != false);// true

引用类型的判等

两边的操作数都是引用类型,两边操作数的类型相同或是父子类型。

如果两边的操作数的值都为 null,或都引用相同的对象,则 == 结果为 true,否则为 false。

如果两边的操作数的值都为 null,或都引用相同的对象,则 != 结果为 false,否则为 true。

String str1 = null;
Object str2 = null;
System.out.println(str1 == str2);// true
System.out.println("hello" == "hello");// true
System.out.println("hello1" == "hello");// false

关于 String 类型的比较可查看 “2.3.5 解析 -> 字符串类型字面量解析”,其它引用类型的比较等到面向对象吧。


[1] 注意别把 “==” 与 “=” 混用了。我知道在日常生活中,使用 “=” 判断是否相等,但是在程序里 “=” 是用来赋值的,“==” 才是用来判断是否相等。

我举个例子,if 旁边的括号中的值只允许为 boolean 类型,当值为 true 时,才会执行 if {} 中的语句。

int i = 4;
if (i = 5) {
    System.out.println("Hello");
}

假如你的原意是想 i 等于 5 时,就输出 “Hello”,但是由于你使用的是单等号,代表赋值,此时的 i = 5,所以原意成了

int i = 4;
if (5) {
    System.out.println("Hello");
}

在 Java 中,正好 boolean 类型不能参与转换,所以编译时直接报错,你可以及时发现,但不是次次都是这么好运气。

如下:

boolean b1 = false;
if (b1 = true) {
    System.out.println("Hello");// Hello
}
System.out.println(b1);// true

原意是当 b1 等于 true 时,输出 “Hello”,但是你的意图被扭曲了,这只是你不小心丢了一个等号而已。

在 JDK 源码中,经常看到赋值、判断混合在一起,令人头晕,完全可以单独提取出来,可读性会好些。

你的原意可能如下:

boolean b1 = false;
if (b1 == true) {
    System.out.println("Hello");
}
System.out.println(b1);// false

说了这么多,只为说一句:判断两个值是否相等,请用 “==”。但是我私底下或写注释时,还是习惯用单等号代表两个值相等。

5.4 逻辑运算符

内容导视:

  • 异或

前提:逻辑运算符的两边要求都是布尔类型,且最终的结果也是布尔类型,下面是我高中时记的口诀:

and 与(&):一假为假

or 或(|):一真为真

not 非(!):真即假,假即真

xor 异或(^):不同为真,相同为假

5.4.1 逻辑与

a & b:当 a 和 b 同时为 true 时,结果才为真,否则为 false。

boolean b1 = 5 > 3;
boolean b2 = 8 < 9;
System.out.println(b1 & b2);// true

翻译成人话:5 大于 3 且 8 小于 9,命题为真。

需要注意的是,不同的运算符优先级不同,优先级高的先运算。之前通过乘法与加法的混合运算中就能看出来乘法优先。

思考如下输出结果:

boolean b1 = 1 < 0;
boolean b2 = 79 > 99;
boolean b3 = false;

System.out.println(b1 & b2 == b3);

b1、b2、b3 都为 false;b1 & b2 为 false,false == b3,所以结果为 true?

但你没有想到 “==” 的优先级高于 “&”,所以是 b2 == b3 先运算结果为 true,b1 & true,结果为 false。

上面的一句相当于:

System.out.println(b1 & (b2 == b3));// false

那该怎么办?扭曲了原意!可以加小括号提升优先级:

System.out.println((b1 & b2) == b3);// true

短路与

&&(short circuiting)相比于逻辑与,效率更高:如果整个表达式结果已经确定,就停止并返回结果,剩下式子不再执行与判断。

什么意思?

一假为假,当左边的值为 false 时,还需要去执行去判断右边吗?完全可以推出结果为 false。

int x = 3;
System.out.println((x < 2) & (++x > 3));
System.out.println(x);// 4
int x = 3;
System.out.println((x < 2) && (++x > 3));
System.out.println(x);// 3

x < 2 为 false,那么整个式子已经可以确定为 false,不需要再判断。通过上面的例子可以看出,短路与并没有去执行右半部分的 ++x,所以 x 的结果不变,还是为 3。利用短路可以省去不必要的计算,从而提高效率。

5.4.2 逻辑或

a | b:当 a 和 b 同时为 false 时,结果才为 false,否则为 true。

System.out.println(8 < 1 | 9 > 3);// true

命题:8 小于 1 或者 9 大于 3,命题为真。

与 & 一样的是,| 也有对应的 ||。

短路或

如果整个表达式的结果已经确定,后面不再执行判断。

int x = 3;
System.out.println(8 < 9 | ++x == 8);
System.out.println(x);// 4
int x = 3;
System.out.println(8 < 9 || ++x == 8);
System.out.println(x);// 3

一般我们使用的是短路与、短路或,我几乎没有看见单个的,除非你想把所有的式子都执行一遍。

5.4.3 非

!a:对 a 取反;a 为 true,则结果为 false;a 为 false,结果为 true。

System.out.println(!(3 < 5));// false

3 < 5 为 true,再取反为 false。

5.4.4 逻辑异或

a ^ b:当 a 不等于 b,结果为 true,否则为 false。

System.out.println(8 < 9 ^ 9 > 3);// false
System.out.println(8 < 9 != 9 > 3);// false

8 < 9 为 true,9 > 3 为 true,它们相等,所以为 false。

很少见,一般使用 !=。

5.5 条件运算符

算是条件语句的简化版吧,可以根据表达式的真假返回不同的值。

条件运算符又称三目运算符、三元运算符,因为有三个操作数。[1]

语法:布尔值 ? 值1 : 值2;

当布尔值为 true,返回值 1;为 false,返回值 2。

boolean isBoy = true;
char zhangSan = isBoy ? '男' : '女';

System.out.println(zhangSan);// 男
double scope = 99.5;
String evaluate = scope < 60 ? "不及格" : "及格";
System.out.println(evaluate);// 及格

很明显 scope 小于 60 为 false,所以返回值2,“及格”。

三目运算符与之后讲的 if-else 类似,但是它是有返回结果的,而 if-else 没有。

[1] 与此对应的二元运算符(如 !=、==、<、+...)、一元运算符(++、--)。

5.6 按位运算符

内容导视:

  • 按位与
  • 按位或
  • 按位异或
  • 按位取反

前提:要求操作数是整数类型。

按位运算符主要用来对操作数二进制的位进行运算。按位运算表示按每个二进制位(bit)进行计算,其操作数和运算结果都是整数类型。

除按位取反(一元运算符)外,其它按位运算符可以与 “=” 组合成扩展赋值运算符。如 &=,x &= 2; 相当于 x = (?)(x & 2);

? 是 x 的类型。

5.6.1 按位与

参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位同时为 1,那么结果才为 1,否则为 0。因此,任何数与 0 进行按位与运算,其结果都为 0。

int x = 2 & 3;
首先写下它们的补码
2 的补码是	00000000 00000000 00000000 00000010
3 的补码是 	00000000 00000000 00000000 00000011
比较结果:   00000000 00000000 00000000 00000010

所以 x = 2。

5.6.2 按位或

参与运算的数字,低位对齐,高位不足的补零。如果对应的二进制位只要有一个为 1,那么结果就为 1,否则为 0。因此,任何数与 0 进行按位与运算,其结果都为那个数自己。

int x = 3 | 4;
3 的补码:... 00000011
4 的补码:... 00000100
比较结果:... 00000111

x = 7。

5.6.3 按位异或

参与运算的数字,低位对齐,高位不足的补零,如果对应的二进制位不同时,结果为 1,否则为 0。

int x = 9 ^ 12;
9  的补码:... 00001001
12 的补码:... 00001100
比较结果: ... 00000101

x = 5。

特性

x ^ y ^ y = x;

x ^ y ^ x = y;

int x = 9;
int y = 15;
int a = x ^ y ^ y;
int b = x ^ y ^ x;
9  的补码: 00001001
15 的补码: 00001111
9 ^ 15:   00000110

(9^15)^15:00001001
(9^15)^9: 00001111

可以利用此特性,完成两整数交换:

int a = 3;
int b = 9;
a = a ^ b;// a = 3 ^ 9
b = a ^ b;// b = 3 ^ 9 ^ 9 = 3
a = a ^ b;// a = 3 ^ 9 ^ 3 = 9

5.6.4 按位取反

只对一个操作数进行运算,将操作数的二进制中的 1 改为 0,0 改为 1。

byte b = ~0;
0 的补码:00000000 00000000 00000000 00000000
取反:	   11111111 11111111 11111111 11111111
是 -1 的补码,所以 b = -1;

例 2:

byte b = ~1;
1 的补码:00000000 00000000 00000000 00000001
取反:	   11111111 11111111 11111111 11111110
是 -2 的补码,b = -2;

例 3:

b = ~ -128;
-128 的原码:10000000 00000000 00000000 10000000
反码:		  11111111 11111111 11111111 01111111
补码:		  11111111 11111111 11111111 10000000
取反:    	  00000000 00000000 00000000 01111111
是 127 的补码,b = 127;

推测:~x = -x - 1;

引子:

5 + (-6) 对应的补码为 5 的补码 + -6 的补码
= 00000101 + 11111010
= 11111111

所以补码 11111111 对应的值是 5 + (-6) = -1

证明:* 代表未知,# 代表对 * 取反后的二进制位。

设正数 x,补码、原码、反码为:0*******
~x = 1#######
现在只要求补码 1####### 对应的值即可

-x - 1 = -x + (-1)
-x 的原码是 1******* -> 反码 1####### -> 补码 1####### + 00000001
-1 的原码是 10000001 -> 反码 11111110 -> 补码 11111111

-x + (-1) 对应的补码为:
= 1####### + 00000001 + 11111111
= 1####### + (00000001 + 11111111)
= 1####### + 00000000
= 1#######

所以补码 1####### 对应的值是 -x - 1
设负数 x,原码为 1*******,反码 1#######,补码 1####### + 00000001
~x = 11111111 - (1####### + 00000001) = 0******* - 00000001
现在只要求补码 0******* - 00000001 对应的值即可

-x 是正数,原码、反码、补码为 0*******
-x - 1 = -x + (-1) 对应的补码为:
= 0******* + 11111111
= 0******* + 11111111 + 00000001 - 00000001
= 0******* + 00000000 - 00000001
= 0******* - 00000001

0******* - 00000001 对应的值是 -x - 1

当 x 为 0 和 -128 时,取反也是 -x - 1。

5.7 移位运算符

移位运算符主要用来对操作数二进制的位进行移动,其操作数和运算结果都是整数类型。

可以与 “=” 组合成扩展赋值运算符。

内容导视:

  • 左移位运算符
  • 有符号右移位运算符
  • 无符号右移位运算符

5.7.1 左移位运算符

左移位运算符为 <<,其运算规则是:按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。

byte b = 4 << 2;
4 的补码:00000000 00000000 00000000 00000100
左移两位:00000000 00000000 00000000 00010000
是 16 的补码,b = 16;

例 2:

byte b = 3 << 4;
3 的补码:00000000 00000000 00000000 00000011
左移四位:00000000 00000000 00000000 00110000
是 48 的补码,b = 48;

规律:x << n = x * 2 ^ n。

5.7.2 有符号右移位运算符

对于正数, >> 会把所有的位右移,并在最前面补 0; 对于负数, >> 会把所有的位右移,并在最前面补 1。

byte b = 3 >> 1;
3 的补码 00000000 00000000 000000000 00000011
右移一位:00000000 00000000 000000000 00000001
是 1 的补码,所以 b = 1;

例 2:

byte b = -4 >> 2;
-4 的补码:11111111 11111111 11111111 11111100
右移 2 位:11111111 11111111 11111111 11111111
是 -1 的补码,所以 b = -1;

规律:x >> n = x / 2 ^ n。

我有时使用 x >> 1 代替 x / 2 的操作。

如 7 >> 1 = 7 / 2 = 3;

int start = 3;
int end = 9;
int mid1 = (start + end) / 2;
int mid2 = start + (end - start) / 2;
int mid3 = start + ((end - start) >> 1);
System.out.println(mid1);// 6
System.out.println(mid2);// 6
System.out.println(mid3);// 6

注意移位运算符没有算术运算符优先级高,所以需要加小括号提升优先级,否则 int mid3 = start + (end - start) >> 1 = 3 + 6 >> 1 = 9 >> 1 = 4;

5.7.3 无符号右移位运算符

将所有位数右移,最前面补 0。

byte b = (byte)(-4 >>> 2);
-4 的补码:11111111 11111111 11111111 11111100
右移 2 位:00111111 11111111 11111111 11111111
是 1073741823 的补码,超出 byte 的取值范围
截去前面 3 个字节得到:11111111
是 -1 的补码,所以 b = -1;

例 2:

int i = -8 >>> 5;
-8 的补码:11111111 11111111 11111111 11111000
右移 5 位:00000111 11111111 11111111 11111111
是 134217727 的补码,i = 134217727;

例 3:

773 = 0x0305;使用两个字节 0x03、0x05 保存。

byte b1 = 0x03;
byte b2 = 0x05;
System.out.println(b1 << 8 | b2);
3 的补码: 00000000 00000000 00000000 00000011
左移 8 位:00000000 00000000 00000011 00000000
5 的补码: 00000000 00000000 00000000 00000101
按位或:   00000000 00000000 00000011 00000101
是 773 的补码,输出 773

5.8 运算符优先级

优先级高的先运算。

优先级从高到低

. ()
++ -- ~ !						单目运算符
* / %							算术运算符
+ -	
<< >> >>>						位移运算符
< > <= >= instanceof			比较运算符
== !=							逻辑运算符
&
^
|
&&
||
? :								三目运算符
= *= /= %=						赋值运算符
+= -= <<= >>=
>>>= &= ^= |=

例如:int a = 1 + 2 * 1; 先算乘法,接着加法,最后赋值。

int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
boolean f = true;
boolean g = false;
boolean h = g & f == e < d << c + b / ++a;
g & f == ?
先算 f == ?
f == e < ?
先算 e < ?
e < d << ?
先算 d << ?
d << c + ?
先算 c + ?
c + b / ?
先算 b / ?
b / ++a

先算 ++a,先++,a = 2,后返回值 2
b / 2 = 1;
c + 1 = 4;
d << 4 = 64;
e < 64 为 true
f == true 为 true
g & true 为 false
所以 h = false

5.x 总结回顾

使用 ++、--、+= 等扩展赋值运算符时不会改变运算结果类型。(使用了强制转换运算符,可能会造成精度损失)

判断两个基本类型的值是否相等请用 “==”。

逻辑运算符中一般使用短路与、短路或。

一定要注意不同运算符之间的优先级,最好亲自测一下,看是否与预期一致,或者直接加小括号提升优先级。

5.y 课后习题

5.1 double i = 5 / 2; 中的 i 的值为?

5.2 输出什么?

1)

String s = "张三";
int i = 3;
System.out.println(s + i * 2 + "b");

2)

int x = 5;
int y = 5;
System.out.println(++x < 6);
System.out.println(y++ < 6);
System.out.println(y);

3)

int x = 10;
int a = x + (x++);
int b = x + (++x);
System.out.println(a);
System.out.println(b);
System.out.println(x);

4)

int i = 34;
int j = i--;
int z = --i;
System.out.println(i);
System.out.println(j);
System.out.println(z);

5)

int i = 2;
int j = 2 + i++;
System.out.println(i);
System.out.println(j);

6)

boolean b = false;
System.out.println(b = true);
System.out.println(b == false);

7)

int x = 9;
int y = 12;

int z = x < y ? x + y : x - y;
System.out.println(z);

8)

int a = 10;
int b = 99;

int result = a > b ? a++ : b--;
System.out.println(result);
System.out.println(a);
System.out.println(b);

5.3 利用三目表达式求出三个数之间的最大数。

5.4 利用所学知识求出 33 天是多少个星期零几天?

5.5 3 / 9 * (242.2 - 100) 的结果是什么?

5.6 下面代码输出什么?

1)

boolean x = true;
boolean y = false;

byte num = 2;

if ((num++ == 2) && (y = true)) {num++;}
if ((x = false) || (++num == 5)) {num++;}

System.out.println(num);
System.out.println(x);
System.out.println(y);

2)

int i = 342;
int b = ++i + i;

System.out.println(b);

3)

int i = 342;
int b = i++ + ++i;

System.out.println(i);
System.out.println(b);

5.z 习题答案

5.1 double i = 5 / 2; 中的 i 的值为?

5 / 2 = 2,再自动升级成 double 类型,所以 i = 2.0。


5.2 输出什么?

1)

String s = "张三";
int i = 3;
System.out.println(s + i * 2 + "b");

"张三" + 3 * 2 + "b"

先算乘法,所以输出 张三6b

2)

int x = 5;
int y = 5;
System.out.println(++x < 6);
System.out.println(y++ < 6);
System.out.println(y);

先++,x = 6,再判断,输出 false;先判断 5 < 6,输出 true,再++,y = 6,输出 6

3)

int x = 10;
int a = x + (x++);
int b = x + (++x);
System.out.println(a);
System.out.println(b);
System.out.println(x);

a = 10 + 10 = 20,x++ 后为 11;b = 11 + 12 = 23,x 为 12。输出 20、23、12。

4)

int i = 34;
int j = i--;
int z = --i;
System.out.println(i);
System.out.println(j);
System.out.println(z);

j = 34,后--,i = 33;先--,i = 32,z = 32。输出 32、34、32。

5)

int i = 2;
int j = 2 + i++;
System.out.println(i);
System.out.println(j);

j = 2 + 2 = 4,随后 i 自加一为 3,输出 3、4

6)

boolean b = false;
System.out.println(b = true);
System.out.println(b == false);

b = true,输出 b 的值:true。true 等于 false 为假,输出 false。

7)

int x = 9;
int y = 12;

int z = x < y ? x + y : x - y;
System.out.println(z);

9 < 12 为 true,返回 x + y = 21,赋给 z,输出 21。

8)

int a = 10;
int b = 99;

int result = a > b ? a++ : b--;
System.out.println(result);
System.out.println(a);
System.out.println(b);

10 > 99 为 false,返回 b--:先将 b 的值 99 赋给 result,后 b--,b = 98。输出 99、10、98。


5.3 利用三目表达式求出三个数之间的最大数。

先定义三个变量保存数值,先让其中两数比较,返回大值赋给 max1;再让 max1 与剩下的数比较返回大值。

int n1 = 241;
int n2 = 324;
int n3 = 242;

int max1 = n1 > n2 ? n1 : n2;
int max2 = max1 > n3 ? max1 : n3;
System.out.println(max2);

5.4 利用所学知识求出 33 天是多少个星期零几天?

让 33 除以 7,商就是星期数,余数就是天数。

int day = 33;
int week = day / 7;
int RemainDay = day % 7;
System.out.println(day + "天是" + week
	+ "个星期零" + RemainDay + "天");

5.5 3 / 9 * (242.2 - 100) 的结果是什么?

因为 3 / 9 = 0,所以结果为 0;要想不偏离原意,请至少把其中一个数转为 double 类型。


5.6 下面代码输出什么?

1)

boolean x = true;
boolean y = false;

byte num = 2;

if ((num++ == 2) && (y = true)) {num++;}
if ((x = false) || (++num == 5)) {num++;}

System.out.println(num);
System.out.println(x);
System.out.println(y);

只有 if () 中为 true,才会执行 {} 中的语句。

直接快进到 if 语句
num++ == 2:
先判断 num 是否等于 2,为 true
再自加一,num = 3;此时不能断定结果,继续看右边

y = true:给 y 赋值为 true
两边条件都为 true,&& 后的结果为 true,执行 num++
num = 4

接着看第 2 个 if 语句
x = false:给 x 赋值 false
既然 x 为假,不能断定结果,继续看第 2 个条件

++num == 5:
先自加一,num = 5
再判断 num 是否等于 5,为 true

两个条件一假一真,|| 后的结果为 true,执行 num++
num = 6

输出 6、false、true

2)

int i = 342;
int b = ++i + i;

System.out.println(b);

先 ++i,i = 343,再赋值:b = 343 + 343 = 686。

3)

int i = 342;
int b = i++ + ++i;

System.out.println(i);
System.out.println(b);
i++ + ++i
先进行加法运算再自加一
即 342 + ++(i),此时 i++ 后,i = 343

现在再看右边的 ++i,先自加一,即 i = 344,再运算
int b = 342 + 344 = 686
i = 344
输出 344、656

例 1:

int i = 342;
i = i++ + i++;

System.out.println(i);
先进行加法运算 342 + i++,后自加 1,i = 343

先进行加法运算 342 + 343
后 i 自加 1,i = 344

计算 342 + 343 = 685 再赋给 i,输出 685
0: sipush 342 {342}
3: istore_3 {} {?,?,?,342}
4: iload_3 {342} {?,?,?,342}
5: iinc 3, 1 {342} {?,?,?,343}
8: iload_3  {342,343} {?,?,?,343}
9: iinc 3, 1 {342,343} {?,?,?,344}
12: iadd {685} {?,?,?,344}
13: istore_3 {} {?,?,?,685}

第 2 个例子:

int i = 342;
i = i++ + ++i;

System.out.println(i);
先进行加法运算 342 + ++i,后自加 1,i = 343

++i 先自加一,i = 344,再进行加法运算 342 + 344 = 686
i = 686

第 3 个例子:

int i = 342;
i = ++i + ++i;

System.out.println(i);
先自加一,i = 343,得到:343 + ++i
先自加一,i = 344,得到:343 + 344 = 687

i = 687

像极了茴香豆的“茴”有几种写法,大家看个乐呵就行。

cqh
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章