Java基础概览和常用知识(五)
自增自减运算符
在写代码的过程中,常见的一种情况是需要某个整数类型变量增加 1 或减少 1。Java 提供了自增运算符 (++
) 和自减运算符 (--
) 来简化这种操作。
++
和 -- 运算符可以放在变量之前,也可以放在变量之后:
- 前缀形式(例如
++a
或--a
):先自增/自减变量的值,然后再使用该变量,例如,b = ++a
先将a
增加 1,然后把增加后的值赋给b
。 - 后缀形式(例如
a++
或a--
):先使用变量的当前值,然后再自增/自减变量的值。例如,b = a++
先将a
的当前值赋给b
,然后再将a
增加 1。
为了方便记忆,可以使用下面的口诀:符号在前就先加/减,符号在后就后加/减。
下面来看一个考察自增自减运算符的高频笔试题:执行下面的代码后,a
、b
、 c
、d
和e
的值是?
int a = 9;
int b = a++;
int c = ++a;
int d = c--;
int e = --d;
答案:a = 11
、b = 9
、 c = 10
、 d = 10
、 e = 10
。
移位运算符
移位运算符(Shift Operators)是编程中用于对二进制位进行操作的运算符。它们主要用于对整数类型的变量进行高效的位级操作。移位就是将其向左或向右移动若干位的运算。移位运算符包括左移(<<
)、右移(>>
)和无符号右移(>>>
)。
移位运算符在各种框架以及 JDK 自身的源码中使用还是挺广泛的,HashMap
(JDK1.8) 中的 hash
方法的源码就用到了移位运算符:
左移运算符(<<
)
左移运算符将操作数的二进制表示向左移动指定的位数,右边空出的位用0填充。左移一位相当于乘以2,因为每向左移动一位,二进制数的值就翻倍。
int a = 5; // 二进制表示为 0000 0101
int b = a << 2; // 左移2位,结果为 0001 0100,即十进制的20
右移运算符(>>
)
右移运算符将操作数的二进制表示向右移动指定的位数,左边空出的位根据符号位(最左边的位)进行填充。对于正数,左边空出的位用0填充;对于负数,左边空出的位用1填充(这是算术右移)。
int a = 20; // 二进制表示为 0001 0100
int b = a >> 2; // 右移2位,结果为 0000 0101,即十进制的5 int c = -20; // 二进制表示为 1111 1111 1111 1111 1111 1111 1110 1100(补码表示)
int d = c >> 2; // 右移2位,结果为 1111 1111 1111 1111 1111 1111 1111 1011,即十进制的-5
无符号右移运算符(>>>
)
无符号右移运算符将操作数的二进制表示向右移动指定的位数,左边空出的位总是用0填充,无论操作数的符号位如何。这种操作通常用于处理无符号整数或需要忽略符号位的场景。
int a = -20; // 二进制表示为 1111 1111 1111 1111 1111 1111 1110 1100(补码表示)
int b = a >>> 2; // 无符号右移2位,结果为 0011 1111 1111 1111 1111 1111 1111 0110,即十进制的1073741818
虽然移位运算本质上可以分为左移和右移,但在实际应用中,右移操作需要考虑符号位的处理方式。
由于 double
,float
在二进制中的表现比较特殊,因此不能来进行移位操作。
移位操作符实际上支持的类型只有int
和long
,编译器在对short
、byte
、char
类型进行移位前,都会将其转换为int
类型再操作。
使用移位运算符的主要原因:
- 高效性:移位操作通常比乘法和除法操作更快,因为它们是直接在硬件层面实现的。
- 紧凑性:移位操作可以用于紧凑地表示和处理二进制数据,这在嵌入式系统、网络通信和低级编程中非常有用。
- 位掩码:移位操作常用于生成位掩码(bitmask),用于对特定位进行读取、设置或清除操作。
- 算法优化:某些算法(如快速傅里叶变换、位图处理等)可以通过移位操作实现高效的实现。
移位运算符最常用于快速乘以或除以 2 的幂次方。除此之外,它还在以下方面发挥着重要作用:、
- 位字段管理:例如存储和操作多个布尔值。
- 哈希算法和加密解密:通过移位和与、或等操作来混淆数据。
- 数据压缩:例如霍夫曼编码通过移位运算符可以快速处理和操作二进制数据,以生成紧凑的压缩格式。
- 数据校验:例如 CRC(循环冗余校验)通过移位和多项式除法生成和校验数据完整性。。
- 内存对齐:通过移位操作,可以轻松计算和调整数据的对齐地址。
如果移位的位数超过数值所占有的位数会怎样?
当 int 类型左移/右移位数大于等于 32 位操作时,会先求余(%)后再进行左移/右移操作。也就是说左移/右移 32 位相当于不进行移位操作(32%32=0),左移/右移 42 位相当于左移/右移 10 位(42%32=10)。当 long 类型进行左移/右移操作时,由于 long 对应的二进制是 64 位,因此求余操作的基数也变成了 64。
也就是说:x<<42
等同于x<<10
,x>>42
等同于x>>10
,x >>>42
等同于x >>> 10
。
左移运算符代码示例:
int i = -1;
System.out.println("初始数据:" + i);
System.out.println("初始数据对应的二进制字符串:" + Integer.toBinaryString(i));
i <<= 10;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
输出:
初始数据:-1
初始数据对应的二进制字符串:11111111111111111111111111111111
左移 10 位后的数据 -1024
左移 10 位后的数据对应的二进制字符 11111111111111111111110000000000
由于左移位数大于等于 32 位操作时,会先求余(%)后再进行左移操作,所以下面的代码左移 42 位相当于左移 10 位(42%32=10),输出结果和前面的代码一样。
int i = -1;
System.out.println("初始数据:" + i);
System.out.println("初始数据对应的二进制字符串:" + Integer.toBinaryString(i));
i <<= 42;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
右移运算符使用类似。
continue、break 和 return 的区别是什么?
在循环结构中,当循环条件不满足或者循环次数达到要求时,循环会正常结束。但是,有时候可能需要在循环的过程中,当发生了某种条件之后 ,提前终止循环,这就需要用到下面几个关键词:
continue
:指跳出当前的这一次循环,继续下一次循环。break
:指跳出整个循环体,继续执行循环下面的语句。
return
用于跳出所在方法,结束该方法的运行。return 一般有两种用法:
return;
:直接使用 return 结束方法执行,用于没有返回值函数的方法return value;
:return 一个特定值,用于有返回值函数的方法
思考一下:下列语句的运行结果是什么?
public static void main(String[] args) {boolean flag = false;for (int i = 0; i <= 3; i++) {if (i == 0) {System.out.println("0");} else if (i == 1) {System.out.println("1");continue;} else if (i == 2) {System.out.println("2");flag = true;} else if (i == 3) {System.out.println("3");break;} else if (i == 4) {System.out.println("4");}System.out.println("xixi");}if (flag) {System.out.println("haha");return;}System.out.println("heihei");
}
运行结果:
0
xixi
1
2
xixi
3
haha