赞
踩
序号 | 符号 | 名称 | 结合性(与操作数) | 对目 | 说明 |
1 | . | 点 | 从左到右 | 双目 | |
() | 圆括号 | 从左到右 | |||
[ ] | 方括号 | 从左到右 | |||
2 | + | 正号 | 从右到左 | 单目 | |
- | 负号 | 从右到左 | 单目 | ||
++ | 自增 | 从右到左 | 单目 | 前缀增,后缀增 | |
-- | 自减 | 从右到左 | 单目 | 前缀减,后缀减 | |
~ | 按位非/取补运算 | 从右到左 | 单目 | ||
! | 逻辑非 | 从右到左 | 单目 | “!”不可以与“=”联用 | |
3 | * | 乘 | 从左到右 | 双目 | |
/ | 除 | 从左到右 | 双目 | 整数除法: 取商的整数部分,小数部分去掉,不四舍五入 | |
% | 取余 | 从左到右 | 双目 | ||
4 | + | 加 | 从左到右 | 双目 | |
- | 减 | 从左到右 | 双目 | ||
5 | << | 左移位运算符 | 从左到右 | 双目 | |
>> | 右移位运算符 | 从左到右 | 双目 | ||
>>> | 无符号右移位运算符 | 从左到右 | 双目 | ||
6 | < | 小于 | 从左到右 | 双目 | |
<= | 小于或等于 | 从左到右 | 双目 | ||
> | 大于 | 从左到右 | 双目 | ||
>= | 大于或等于 | 从左到右 | 双目 | ||
instanceof | 确定某对象是否属于指定的类 | 从左到右 | 双目 | ||
7 | == | 等于 | 从左到右 | 双目 | |
!= | 不等于 | 从左到右 | 双目 | ||
8 | & | 按位与 | 从左到右 | 双目 | |
9 | | | 按位或 | 从左到右 | 双目 | |
10 | ^ | 按位异或 | 从左到右 | 双目 | |
11 | && | 短路与 | 从左到右 | 双目 | |
12 | || | 短路或 | 从左到右 | 双目 | |
13 | ?: | 条件运算符 | 从右到左 | 三目 | |
14 | = | 赋值运算符 | 从右到左 | 双目 | |
+= | 混合赋值运算符 | ||||
-= | |||||
*= | |||||
/= | |||||
%= | |||||
&= | |||||
|= | |||||
^= | |||||
<<= | |||||
>>= | |||||
>>>= |
部分运算符说明:
算数运算符:+
:加法,-
:减法,*
:乘法,/
:除法,%
:取余运算
关系运算符:
<
:只能比较基本类型数据之间的关系,不能比较对象之间的关系;>
: (同关系运算符<
);<=
: (同关系运算符<
);>=
: (同关系运算符<
);==
:若使用该运算符比较两个对象的引用(变量),则实质上是比较两个变量是否引用了相同的对象。所谓相同的对象是指,是否是在堆栈(Heap
)中开辟的同一块儿内存单元中存放的对象。equals()
方法,该方法的返回值类型是布尔值。需要注意的是:若用类库中的类创建对象,则对象的引用调用equals()
方法比较的是对象的内容;若用自定义的类来创建对象,则对象的引用调用equals()
方法比较的是两个引用是否引用了同一个对象,因为第二种情况equals()
方法默认的是比较引用。!=
:(同关系运算符==
)逻辑运算符 (操作符只能是布尔类型的)&&
,||
,!
public class Demo {
public static void main(String[] args) {
// System.out.println((!'1'||'1')+5);//编译错误
// System.out.println(!5);//编译错误
// System.out.println(('1'||'1')+5);//编译错误
// System.out.println(1||2);//编译错误
// System.out.println(5-3||4-2);//编译错误 8
System.out.println(5<3||4>3);//true 9 }
}
位运算符:&
,|
,^
,!
:不可以与=
联用,因为!
是一元操作符;不可以对布尔类型的数据进行按位非运算
移位运算符(只能处理整数运算符):Char
、byte
、short
类型,在进行移位之前,都将被转换成int
类型,移位后的结果也是int
类型;移位符号右边的操作数只截取其二进制的后5
位(目的是防止因为移位操作而超出int
类型的表示范围:2
的5
次方是32,int类型的最大范围是32位);对long
类型进行移位,结果仍然是long类型,移位符号右边的操作符只截取其二进制的后6位。
>>
:若符号位为正,则在最高位插入0
;若符号位为负
,则在最高位插入1
>>>
:无论正负,都在最高位插入0
基本位操作符号:
~
按位非(NOT)&
按位与(AND)|
按位或(OR)^
按位异或(XOR)>>
右移>>>
无符号右移<<
左移前面几个都非常简单,主要是移位操作比较容易出错.
首先要搞清楚参与运算的数的位数,如int
的是32
位,long
的是64
位。
如int i = 1
; i的二进制原码表示为: 00000000000000000000000000000001
long l = 1;
l的二进制原码表示为: 0000000000000000000000000000000000000000000000000000000000000001
各个相关概念定义如下:
机器数
:一个数在计算机中的二进制表示形式
, 叫做这个数的机器数。机器数是带符号
的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1
比如,十进制中的数 +3
,计算机字长为8位
,转换成二进制就是00000011
。如果是 -3
,就是 10000011
。那么,这里的 00000011
和 10000011
就是机器数。
真值
:因为第一位是符号位,所以机器数的形式值
就不等于真正的数值。例如上面的有符号数 10000011
,其最高位1
代表负
,其真正数值是 -3
而不是形式值131
(10000011
转换成十进制等于131
)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数真值。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
原码
:原码就是符号位
加上真值的绝对值, 即用第一位表示符号, 其余位表示值.
比如:如果是8位
二进制:[+1]原 = 0000 0001
,[-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位
二进制数的取值范围就是:[1111 1111 , 0111 1111]
,即:[-127 , 127]
原码是人脑最容易理解和计算的表示方式.
反码
:反码的表示方法是: 正数的反码跟原码一样
,负数
的反码是在其原码的基础上, 符号位不变
,其余各位取反
比如:[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算
补码
:补码的表示方法是: 正数的补码就是其本身
;负数
的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
比如:[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值
如果有运算得话,负数
都是用补码
参与运算的,得到的结果也是补码
,需要减1取反
获得原码
在开始深入学习前, 先死记硬背上面的原码, 反码和补码的表示方式以及计算方法.
现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
所以不需要过多解释. 但是对于负数:
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?
首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别符号位
显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位参与运算
的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0
, 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码。计算十进制的表达式:
1-1=0
1 - 1 = 1 + (-1)
= [00000001]原 + [10000001]原
= [10000010]原 = -2
如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.
为了解决原码做减法的问题, 出现了反码。计算十进制的表达式:
1-1=0
1 - 1 = 1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]反 + [1111 1110]反
= [1111 1111]反 = [1000 0000]原
= -0
发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在0
这个特殊的数值上. 虽然人们理解上+0
和-0
是一样的, 但是0
带符号是没有任何意义的. 而且会有[0000 0000]
原和[1000 0000]
原两个编码表示0
.
于是补码
的出现, 解决了0
的符号以及两个编码的问题:
1-1 = 1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]补 + [1111 1111]补
= [0000 0000]补=[0000 0000]原
这样0
用[0000 0000]
表示, 而以前出现问题的-0
则不存在了.而且可以用[1000 0000]
表示-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原
= [1111 1111]补 + [1000 0001]补
= [1000 0000]补
-1-127
的结果应该是-128
, 在用补码运算的结果中, [1000 0000]补
就是-128
. 但是注意因为实际上是使用以前的-0
的补码来表示-128
, 所以-128没有原码和反码
表示.(对-128
的补码表示[1000 0000]补
算出来的原码是[0000 0000]原
, 这是不正确的)
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127]
, 而使用补码表示的范围为[-128, 127]
.
因为机器使用补码, 所以对于编程中常用到的32
位int
类型, 可以表示范围是: [
−
2
31
-2^{31}
−231,
2
31
−
1
2^{31}-1
231−1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.
负数参与的运算
,得到的是 补码
,需要将补码先减1,然后逐位取反,得到原码
,即为运算结果
。
0
例外,如果得到的是0
,则不需减1
和取反。
另外,两个正数运算后得到的就是原码,不需减1
和取反。
举例:
1^-1,
-1 :
10000000000000000000000000000001--原码
11111111111111111111111111111110--反码
11111111111111111111111111111111--补码
1
00000000000000000000000000000001`--原码
则1^-1等于
11111111111111111111111111111111^
00000000000000000000000000000001=
11111111111111111111111111111110--补码
11111111111111111111111111111101--反码
10000000000000000000000000000010--原码==-2
即1^-1=-2
举例:
1^-2
-2
10000000000000000000000000000010--原码
01111111111111111111111111111101--反码
01111111111111111111111111111110--补码
1
00000000000000000000000000000001--原码
则1^-2等于
01111111111111111111111111111110^
00000000000000000000000000000001=
01111111111111111111111111111111--补码
01111111111111111111111111111110--反码
10000000000000000000000000000001--原码==-1
例如:把汉字转为16进制需要如下:
@Test
public void testEnHex() throws UnsupportedEncodingException {
String s = "严";
byte[] bytes = s.getBytes("utf-8");
StringBuffer sb = new StringBuffer();
for (int i = 0;i<bytes.length;i++){
String s1 = Integer.toHexString(bytes[i] & 0x0ff).toUpperCase();
if(s1.length()<2){
sb.append(0);
}
sb.append(s1);
}
System.out.println(sb.toString());
}
如上的为什么需要 &0x0ff呢
先看下添加和去掉结果:
有&0x0ff
是E4B8A5,没有时是FFFFFFE4FFFFFFB8FFFFFFA5
没有&0x0ff
转换后多了一串FF
这是因为Integer.toHexString()
的接收参数是int
,不是byte
,于是运算是会先把byte
强制转换为int
由于java
中强制转换是保持值不变,而在计算机中数都是用 补码
表示的,java
中int
是32位4个byte
, 正数补码是正数本身,这样不会有问题,强转为32
位时前面24位会填充0,
而负数的补码是将其对应正数二进制表示所有位取反(原码符号位除外,0变1,1变0)后加1
,于是32
位的0x00 00 00 80(0000 … 0000 1000 0000)补码是0xFF FF FF 80(1111 ... 1111 1000 0000)
,前面是填充的1
所以Integer.toHexString()
后就会变成前面多了一串F
所以要得到正确的结果,需要用 Integer.toHexString(card[i] & 0xff)
,这样会只取最后8位(1byte=8位二进制
),前面都置0,这样转换出来就是正确的了
为什么非要添加&0x0ff
,不加也有结果,只是长点而已嘛
如果不加&0x0ff
,那么保存的字符长度难以统一 ,字符串长度太长。
并且byte
的取值范围在[-128,127]
之间,16进制
表示用两个符号就够了0x00—0xff
。负数如果用这么多字符,造成正负数符号长度不统一怎么保存呢?从16进制
还原成2进制
时怎么按长度区分各个数字呢?
所以我们把byte
与运算&0xff
。以-1为例:1111 1111 1111 1111 1111 1111 1111 1111 & 0xff
得到0000 0000 0000 0000 0000 0000 1111 1111
即0xff
。这个数结果依然是-1
。用这种方式处理后,byte不论正负,得到的都是二位的16进制数。以两位存储,两位还原,很方便
还原代码如下,使用E4B8A5
(用&0xff)能得到正确值,但是使用FFFFFFE4FFFFFFB8FFFFFFA5
(没有用&0xff)就不能得到正确的值
public void testHanzi() {
String hexStr = "E4B8A5";
String str = "0123456789ABCDEF" ;
char [] hexs = hexStr.toCharArray();
byte [] bytes = new byte [hexStr.length() / 2 ];
int n;
for ( int i = 0 ; i < bytes.length; i++) {
n = str.indexOf(hexs[ 2 * i]) * 16 ;
n += str.indexOf(hexs[ 2 * i + 1 ]);
bytes[i] = ( byte ) (n & 0xff );
}
System.out.println(new String(bytes));
}
按照16进制
两位两位判断,在进行byte
强转时高于127
时,溢出转为负值
常用的位运算符–0在位运算中是比较特殊的
^
异或:相同为0,相异为1; 任何数与0异或都等于原值。
&
与: 全1为1, 有0为0;任何数与0异或都等于0。
|
或: 有1为1, 全0为0。任何数与0或都等于原值。
<<
左移: 补0。
>>
右移:符号位是0补0,是1补1。
>>>
无符号右移:补0。
~
非:逐位取反
<<
:逻辑左移,右边补0,符号位和其他位一样.
正数: x<<1
一般相当于2x
,但是可能溢出.
溢出范围:
2
30
2^{30}
230 ~ (
2
31
2^{31}
231-1) 二进制表示 010000…000到01111…1111,移位后最高为变为1了,变成负数了.
负数:
1111111111111111111111111111111
this is 2^31
1000000000000000000000000000000
this is 2^30
x<<1
一般也相当于2x
,也有可能溢出.所以, x*32
可以写成x<<5
溢出范围:
−
2
31
-2^{31}
−231~-(
2
30
2^{30}
230+1)二进制表示10000...000
到101111...1111
,移位后最高为变成0了,变成正数了.
>>
:算术右移,和上面的不对应,为正数时左边补0
,为负数时左边补1
.
x>>1
,相当于x/2
,余数被舍弃,因为这个是缩小,所以不会溢出.
不过有一点要注意: -1
右移多少位都是-1
.
另外舍弃的余数是正的, 3>>1=1
舍弃的余数是1.
-3>>1=-2
舍弃的余数也是1
,而不是-1
.
对于正数 x>>
1和x/2
相等
对于负数 x>>1
和x/2
不一定相等.
>>>
:逻辑右移,这个才是和<<
对应的
这个把符号位一起移动,左边补0
对于正数,>>>
和>>
是一样的
对于负数,右移之后就变成正数了.
可以使用Integer.toBinaryString(int i)
来看01
比特,更加直观.
考虑下面的代码:
for (val = 0; val < 100000; val +=5) { alterX = val * 8; myResult = val * 2; }
用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:
for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val << 1; }
修改后的代码不再做乘以8的操作,而是改用等价的左移3位
操作,每左移1位相于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。
无符号右移位操作符>>>
在将bit
串右移位时,从bit串的最左边填充0
,这和带符号右移位操作符>>
不同。>>
在将bit串右移位时,从bit串的最左边填充原来最左边的位。也就是说,bit
串原来最左边的位是符号位,如果为1
,则在带符号右移时最左边始终填充1
;如果为0
,则在带符号右移时最左边始终填充0。
移位操作符的例子见下表。
操作 | 结果 | 说明 |
00110010<< 2 | 11001000 | 右边始终填充0 |
00110010 >> 2 | 00001100 | 结果一样 |
00110010 >>> 2 | 00001100 | |
10110010 >> 2 | 11101100 | 结果不同 |
10110010 >>> 2 | 00101100 |
按位与
操作符&
对两个bit串按位进行逻辑与,按位或
操作符|
对两个bit串按位进行逻辑或,按位异或
操作符^
对两个bit串按位进行异或操作。运算规则如下表所示。
按位与 | 按位或 | 按位异或 |
---|---|---|
0 & 0 = 0 | 0 | 0 = 0 | 0 ^ 0 = 0 |
0 & 1 = 0 | 0 | 1 = 1 | 0 ^ 1 = 1 |
1 & 0 = 0 | 1 | 0 = 1 | 1 ^ 0 = 1 |
1 & 1 = 1 | 1 | 1 = 1 | 1 ^ 1 = 0 |
基本类型
,或者叫做内置类型
,是JAVA
中不同于类的特殊类型。它们是我们编程中使用最频繁的类型
基本类型共有八种,它们分别都有相对应的包装类。关于它们的详细信息请如下:
基本类型可以分为三类,字符类型char
,布尔类型boolean
以及数值类型byte、short、int、long、float、double
。
数值类型又可以分为整数类型byte、short、int、long
和浮点数类型float、double
。JAVA
中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。实际上,JAVA
中还存在另外一种基本类型void
,它也有对应的包装类java.lang.Void
,不过我们无法直接对它们进行操作。对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:
public class PrimitiveTypeTest { public static void main(String[] args) { // byte System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE); System.out.println("包装类:java.lang.Byte"); System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE); System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE); System.out.println(); // short System.out.println("基本类型:short 二进制位数:" + Short.SIZE); System.out.println("包装类:java.lang.Short"); System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE); System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE); System.out.println(); // int System.out.println("基本类型:int 二进制位数:" + Integer.SIZE); System.out.println("包装类:java.lang.Integer"); System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE); System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE); System.out.println(); // long System.out.println("基本类型:long 二进制位数:" + Long.SIZE); System.out.println("包装类:java.lang.Long"); System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE); System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE); System.out.println(); // float System.out.println("基本类型:float 二进制位数:" + Float.SIZE); System.out.println("包装类:java.lang.Float"); System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE); System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE); System.out.println(); // double System.out.println("基本类型:double 二进制位数:" + Double.SIZE); System.out.println("包装类:java.lang.Double"); System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE); System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE); System.out.println(); // char System.out.println("基本类型:char 二进制位数:" + Character.SIZE); System.out.println("包装类:java.lang.Character"); // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台 System.out.println("最小值:Character.MIN_VALUE=" + (int) Character.MIN_VALUE); // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台 System.out.println("最大值:Character.MAX_VALUE=" + (int) Character.MAX_VALUE); } } 运行结果: 基本类型:byte 二进制位数:8 包装类:java.lang.Byte 最小值:Byte.MIN_VALUE=-128 最大值:Byte.MAX_VALUE=127 基本类型:short 二进制位数:16 包装类:java.lang.Short 最小值:Short.MIN_VALUE=-32768 最大值:Short.MAX_VALUE=32767 基本类型:int 二进制位数:32 包装类:java.lang.Integer 最小值:Integer.MIN_VALUE=-2147483648 最大值:Integer.MAX_VALUE=2147483647 基本类型:long 二进制位数:64 包装类:java.lang.Long 最小值:Long.MIN_VALUE=-9223372036854775808 最大值:Long.MAX_VALUE=9223372036854775807 基本类型:float 二进制位数:32 包装类:java.lang.Float 最小值:Float.MIN_VALUE=1.4E-45 最大值:Float.MAX_VALUE=3.4028235E38 基本类型:double 二进制位数:64 包装类:java.lang.Double 最小值:Double.MIN_VALUE=4.9E-324 最大值:Double.MAX_VALUE=1.7976931348623157E308 基本类型:char 二进制位数:16 包装类:java.lang.Character 最小值:Character.MIN_VALUE=0 最大值:Character.MAX_VALUE=65535
Float
和Double
的最小值和最大值都是以科学记数法的形式输出的,结尾的E+数字
表示E
之前的数字要乘以10
的多少倍。比如3.14E3
就是3.14×1000=3140
,3.14E-3
就是3.14/1000=0.00314
大家将运行结果与上表信息仔细比较就会发现float
、double
两种类型的最小值与Float.MIN_VALUE
、 Double.MIN_VALUE
的值并不相同,这是为什么呢?实际上Float.MIN_VALUE
和Double.MIN_VALUE
分别指的是 float
和double
类型所能表示的最小正数。也就是说存在这样一种情况,0
到±Float.MIN_VALUE
之间的值float
类型无法表示,0
到±Double.MIN_VALUE
之间的值double
类型无法表示。这并没有什么好奇怪的,因为这些范围内的数值超出了它们的精度范围。
需要注意
:基本类型存储在栈中
,因此它们的存取速度要快于存储在堆中的对应包装类的实例对象。从Java5.0(1.5)
开始,JAVA
虚拟机(Java Virtual Machine
)可以完成基本类型和它们对应包装类之间的自动转换。因此我们在赋值、参数传递以及数学运算的时候像使用基本类型一样使用它们的包装类,但这并不意味着你可以通过基本类型调用它们的包装类才具有的方法。另外,所有基本类型(包括void
)的包装类都使用了final
修饰,因此我们无法继承它们扩展新的类,也无法重写它们的任何方法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。