移位运算符
移位运算符就是在二进制的基础上对数字进行平移。按照平移的方向
和填充数字的规则分为三种:<<(左移)、>>(带符号右移)和>>>(无符号右移)。
在移位运算时,byte、short 和 char 类型移位后的结果会变成 int 类
型,对于 byte、short、char 和 int 进行移位时,规定实际移动的次数是移动次
数和 32 的余数,也就是移位 33 次和移位 1 次得到的结果相同。移动 long 型的
数值时,规定实际移动的次数是移动次数和 64 的余数,也就是移动 66 次和移动
2 次得到的结果相同。
三种移位运算符的移动规则和使用如下所示:
l <<
运算规则:
按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位
补零。
语法格式:
需要移位的数字 << 移位的次数
例如: 3 << 2,则是将数字 3 左移 2 位
计算过程:
3 << 2
首先把 3 转换为二进制数字 0000 0000 0000 0000 0000 0000 0000
0011,然后把该数字高位(左侧)的两个零移出,其他的数字都朝左平移 2 位,最
后在低位(右侧)的两个空位补零。则得到的最终结果是 0000 0000 0000 0000
0000 0000 0000 1100,则转换为十进制是 12。
数学意义:
在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以
2 的 1 次方,左移 n 位就相当于乘以 2 的 n 次方。
l >>
运算规则:
按二进制形式把所有的数字向右移动对应巍峨位数,低位移出(舍弃),
高位的空位补符号位,即正数补零,负数补 1。
语法格式:
需要移位的数字 >> 移位的次数
例如 11 >> 2,则是将数字 11 右移 2 位
计算过程:
11 的二进制形式为:0000 0000 0000 0000 0000 0000 0000 1011,
然后把低位的最后两个数字移出,因为该数字是正数,所以在高位补零。则得到
的最终结果是 0000 0000 0000 0000 0000 0000 0000 0010。转换为十进制是 3。
数学意义:
右移一位相当于除 2,右移 n 位相当于除以 2 的 n 次方。
l >>>
运算规则:
按二进制形式把所有的数字向右移动对应巍峨位数,低位移出(舍弃),
高位的空位补零。对于正数来说和带符号右移相同,对于负数来说不同。
其他结构和>>相似。
小结:
二进制运算符,包括位运算符和移位运算符,使程序员可以在二进制
基础上操作数字,可以更有效的进行运算,并且可以以二进制的形式存储和转换
数据,是实现网络协议解析以及加密等算法的基础。
但是,在实际使用中,为了使代码可读性强,还是大量使用一般的算
术运算符来进行数字运算。
其它运算符
对于无法归类,或者单独占一类的运算符,将在下面进行介绍。
l ++、--
这两个运算符是程序中的递增和递减运算符。其意义参照以下示例代码:
int n = 0;
n++; // n = n + 1
System.out.println(n);
n++的意义就是对原来变量 n 的值加 1 以后再赋值给自身,因为原来变量 n 的值
是 0,加 1 以后则变成 1。
同理,递减运算符的意义也是这样,示例代码:
int m = 0;
m--;
System.out.println(m);
m—的意义就是对原来变量 m 的值减 1 以后再赋值给自身,则 m 的值变成-1。
需要注意的是++和—只能操作变量,而不能操作其他的内容,以下使用都是错误
的:
int a = 0;
int b = 0;
(a + b)++; //错误
final int M = 1;
M++; //错误
5++; //错误
在实际书写时,++和—既可以写在变量的前面,也可以写在变量的后面,例如:
int k = 0;
k++;
++k;
同理,--也可以这样,那么这些在实际使用中有什么区别呢?其实对于变量的值
来说,没有区别,也就是++无论写后面还是写前面,变量的值肯定增加 1,--无
论写在后面还是前面,变量的值都减 1。其最大的区别在于整个式子的值,如 n
++,规则如下:
1)++或—写在变量前面,则该式子的值等于变量变化以前的值。
2)++或—写在变量后面,则该式子的值等于变量变化以后的值。
示例代码如下:
int n = 1;
int m= 1;
n++; //n 的值变为 2
++m; //m 的值变为 2
int k = n++; //n 的初始值是 2,则 n++的值是 2,结果 n 的值为 3,
k 的值为 2
int j = ++m; //m 的初始值是 2,则++m 的值是 3,结果 m 的值是 3,
j 的值为 3
同理,--也是这样。
下面是一个稍微综合点的示例:
int a = 0;
int b = 0;
a = b++; //a 为 0,b 为 1
a = ++b; //a 为 2,b 为 2
b = a++; //a 为 3,b 为 2
a = ++b; //a 为 3,b 为 3
说明:注释部分为对应行代码运行以后,a 和 b 的值。
在程序开发中,可以使用该区别简化代码的书写,但是不推荐这样做,因为这样
将增加阅读代码的难度。
l +、-
前面介绍过加减运算符,其实+、-还有另外一个意义,也就是代表正负,通常情
况下正号可以省略,而负号可以和数值、变量以及运算式进行结合,示例代码如
下:
int a = 0;
int b = 1;
int c = -5;
c = -a;
c = -(a + b);
l ? :
这个运算符称为条件运算符,其作用是根据判断的结果获得对应的值,语法格式
如下:
条件式 ? 值 1 : 值 2
语法要求条件式部分必须是 boolean类型,可以是 boolean值,也可以是 boolean
变量,或者是关系运算符或逻辑运算符形成的式子,值 1 和值 2 必须能够转换成
相同的类型。
功能说明:如果条件式的结果是 true,则整个式子的值取值 1 的值,否则取值 2
的值。示例代码如下:
int x = 10;
int y = 20;
int max = x > y ? x : y; //因为 x 大于 y,则取变量 x 的值,然后
赋值给 max
int a = -10;
int abs = a > 0 ? a : -a; //实现求绝对值得功能
l ()
括号,也是运算符的一种,作用是可以让括号内部的计算首先进行,这个和数学
上一致,只是程序代码中可以使用这个组合任意的合法运算式。示例代码为:
int a = 1 + 2 * 3;
int a = (1 + 2) * 3; //和以上代码的运行结果不一致
其实每个运算符都有自己的优先级,使用括号可以提升对应式子的优先级。关于
运算符优先级的概念,后续将进行介绍。
—运算符优先级
在实际的开发中,可能在一个运算符中出现多个运算符,那么计算时,
就按照优先级级别的高低进行计算,级别高的运算符先运算,级别低的运算符后
计算,具体运算符的优先级见下表:
运算符优先级表
优先级 运算符 结合性
1 () [] . 从左到右
2 ! +(正) -(负) ~ ++ -- 从右向左
3 * / % 从左向右
4 +(加) -(减) 从左向右
5 << >> >>> 从左向右
6 < <= > >= instanceof 从左向右
7 == != 从左向右
8 &(按位与) 从左向右
9 ^ 从左向右
10 | 从左向右
11 && 从左向右
12 || 从左向右
13 ?: 从右向左
14 = += -= *= /= %= &= |= ^= ~= <<= >>= >>>= 从右向左
说明:
1、 该表中优先级按照从高到低的顺序书写,也就是优先级为 1 的优先级最高,
优先级 14 的优先级最低。
2、 结合性是指运算符结合的顺序,通常都是从左到右。从右向左的运算符最典
型的就是负号,例如 3+-4,则意义为 3 加-4,符号首先和运算符右侧的内容结
合。
3、 instanceof 作用是判断对象是否为某个类或接口类型,后续有详细介绍。
4、 注意区分正负号和加减号,以及按位与和逻辑与的区别
其实在实际的开发中,不需要去记忆运算符的优先级别,也不要刻意的使用运算
符的优先级别,对于不清楚优先级的地方使用小括号去进行替代,示例代码:
int m = 12;
int n = m << 1 + 2;
int n = m << (1 + 2); //这样更直观
这样书写代码,更方便编写代码,也便于代码的阅读和维护