深入浅出Java编程
上QQ阅读APP看书,第一时间看更新

4.1 Java中的运算符列表

先让我们了解一下Java的所有运算符号,再展开来讲。Java的运算符分为四类:算术运算符、关系运算符、逻辑运算符、位运算符。

· 算术运算符号:+(加)、-(减)、*(乘)、/(除)、%(取模)、++(自加)、--(自减)。

· 关系运算符:==(等于)、!=(不等于)、>(大于)、>=(大于等于)、<(小于)、<=(小于等于)。

· 逻辑运算符:&&(短路与)、||(短路或)、!(非)、^(异或)、&(与)、|(或)。

· 位运算符:&(与)、|(或)、~(按位取反)、>>(右位移)、<<(左位移)、>>>(无符号位移)。

4.1.1 算术运算符

+(加)运算符可以对数值类型进行操作,相加的结果至少为int类型或是较大一方的数据类型。

以下是一些加运算的例子。

【文件4.1】Operation.java

1. byte a1 = 1;
2. short a2 = 1;
3. int a3 = 1;
4. double a4 = 1D;
5. // 相加的结果为int类型,所以将a1+a2的结果转成byte类型
6. byte b1 = (byte) (a1 + a2);
7. //相加的结果为short类型,所以将a1+a2的结果转成short类型
8. short b2 = (short)(a1+a2);
9. //相加的结果为int类型,可以直接赋值给int类型
10. int b3= a1+a2;
11. //相加的结果为double类型,所以赋值给double类型是可以的
12. double b4 = a1+a4;

-(减)、*(乘)、/(除)的运算与上面的类似,不再赘述。需要说明的是/(除)运算,如果参与的都是int或long类型,则只会返回整数部分。只有float和double参与运算时,才会返回小数。

1. int a1 = 10/4;//返回的结果为2
2. double a2=10.0D/4;//返回2.5

+(运算)不仅可以进行数值的运算,还可以进行字符串的串联操作,使用+对任意对象进行+操作时,将按优先级将这个对象转成String。相加的结果也同样为String类型。

【文件4.2】Operation1.java

1. int a1 = 10;
2. int a2 = 90;
3. String str = "Mrchi";
4. String str1 = a1+a2+str;
5. String str2 = str+a1+a2;

在上面的代码中,第4行相加的结果为100Mrchi。按照运算的优先级,先计算10+90的结果(100)再与Mrchi进行字符串串联,结果为100Mrchi。

第5行的结果为Mrchi1090。因为先进行Mrchi与10的串联,成为字符串,再串联a2,结果为Mrchi1090。

采用%取余(取模)运算符,两数计算的结果为余数。

【文件4.3】Operation2.java

1. int a = 10%2;//余数为0,整除
2. int b = 10%4;//余数为2,即10除以4余2
3. int c = 10%7;//余数为3

++(自加)分前++、后++。前++是指先加再用,后++是指先用当前的数再进行++操作。--(自减)同上。以下是示例:

【文件4.4】Operation3.java

1. int a = 1;
2. int b = a++;
3. //先将a的值赋给b,所以b的值为1,然后a做自加,所以a的值为2
4. int c = 1;
5. int d = ++c;
6. //先对c做自加操作,此时c的值为2,再将c的值赋给d,所以d的值为2

需要说明的是,++、--操作不会修改数据的类型。例如,以下两种代码所获取的结果不同:

1. byte a = 1;
2. a++;//++不修改数据类型
3. a=(byte)(a+1);//a+1的结果为int,所以必须强制类型转换才可以回到byte上

4.1.2 关系运算符

关系运算符用于比较两个数值的大小,比较结果为boolean值。>=、<=、>、<可以直接比较两个数值,==和!=不仅可以比较数值,还可以比较任意对象的内存地址。

示例程序如下:

【文件4.5】Operation4.java

1. int a = 1;
2. int b = 1;
3. Integer c = new Integer(1);
4. String str1 = "Jack";
5. String str2 = new String("Jack");
6. boolean b1 = a == b;
7. boolean b2 = a == c;
8. boolean b3 = str1 == str2;

第6行直接比较两个数值的结果为true。在第7行,虽然c是对象类型,但是在JDK 1.5以后会自动将c拆成int类型,所以也是直接比较两个值,结果为true。第8行为比较两个对象类型的内存是否一样,由于str2是一个新内存对象的声明,因此第8行的结果为false。

4.1.3 逻辑运算符

&(与)和|(或)既可以进行逻辑运算,也可以进行位运算。当&(与)两边运算表达式的值都为true时,结果为true;两边只要有一方为false,则结果为false。|(或)两边表达式的值只要有一个为true则结果为true,只有两边都为false时结果才为false。值得注意的是,&和|两边的表达式无论如何都会参与运算。

请见以下表达式:

1. boolean boo1 = true & false;// false
2. boolean boo2 = true & true;// true;
3. boolean boo3 = false | true;// true

两边都为运算表达式时,表达式两边都会参与运算:

     boolean boo1 = (1==2) & (1==1);// false

&&(短路与)、||(短路或)的两边只能是boolean表达式。使用&&时,如果&&左边的表达式已经为false,则无论右边为true还是false,结果都是false,此时右边的表达式将不再参与运算,所以叫作短路与运算。同样的,对于||(短路或),如果左边已经是true,那么无论右边是true还是false都将为true,此时右边也不再参与运算,所以叫短路或。

在进行比较时,虽然使用&&和||可以省去不必要的运算,但是也会带来一些问题,如下代码将不会抛出异常。

【文件4.6】Operation5.java

1. String str = null;
2. boolean boo = false && str.length()==3;
3. System.err.println(boo);

在上面的第2行中,str为null值,如果直接调用str.length()获取长度,则会抛出一个NullPointerException异常,但是&&左边已经是false,右边不会参与运算,所以不会抛出异常。如果将&&修改为&,将会抛出NullPointerException异常,因为&两边都会参与运算,此时str的值为null:

1. String str1 = null;
2. boolean boo1 = false & str.length()==3;
3. System.err.println(boo1);

使用^(异域运算符)时,两个表达值式的值不一样时结果才是true,即:

1. boolean boo1 = false ^ true;// true
2. boolean boo2 = false ^ false;// false

!(非运算符号)为取反操作,如!true的结果为false,!false的结果为true。

4.1.4 位运算符

位运算符包含的符号有&(与)、|(或)、~(按位取反)、>>(右位移)、<<(左位移)、>>>(无符号位移),是对二进制数据进行运算,即运算的对象为0和1。

&(与)运算符的两边都为1时结果为1,例如:

【文件4.7】Operation6.java

1. // 声明一个二进制的数15,使用0b声明一个二进制的数
2. int a = 0b00000000_00000000_00000000_00001111;
3. // 声明一个二进制的1
4. int b = 0b00000000_00000000_00000000_00000001;
5. // a&b则c的结果为1
6. int c = a & b;

在上例中,c的结果为1,运算过程如图4-1所示。

图4-1

进行|(或)运算时,只要表达式的两边有一个为1,结果就是1。

【文件4.8】Operation7.java

1. // 声明一个二进制的数15,使用0b声明一个二进制的数
2. int a = 0b00000000_00000000_00000000_00001111;
3. // 声明一个二进制的1
4. int b = 0b00000000_00000000_00000000_00000001;
5. // a|b,则c的结果为15
6. int c = a | b;

在上例中,c的结果为15,运算过程如图4-2所示。

图4-2

~是按位取反运算符号,若是1则转换成0,若是0则转换成1。

【文件4.9】Operation8.java

1. // 声明一个二进制的数15,使用0b声明一个二进制的数
2. int a = 0b00000000_00000000_00000000_00001111;
3. int b = ~a;
4. System.err.println(b);//-16
5. //11111111111111111111111111110000
6. System.err.println(Integer.toBinaryString(b));

在上例中,第4行的输出为-16。a的首位是0,按位取反以后为1,对于一个二进制来说,首位为1时为负数,所以b的值为负数。

在第6行中,通过Integer包装类型的静态方法,将b转成二进制的结果为11111111111111111111111111110000,正是a值按位取反以后的结果。

>>(右位移)和<<(左位移)运算是将二进制数据向右或左进行位移,移出去的数据将在前面或者后面补0。

1. int a = 0b00000000_00000000_00000000_00001111;
2. int b = a >> 2;//结果为3

上面的向右位移运算过程如图4-3所示。

图4-3

向右位移两位以后,移出两个1,前面补两个0,所以最后的二进制结果为00000000_00000000_00000000_00000011,这个二进制数据的结果为2。

左位移运算同理,只是后面补0,不再赘述。

>>>为无符号位移运算符,对于>>右位移运算,如果为负数,则前面补1,即依然是负数。如>>右位移运算的示例:

【文件4.10】Operation9.java

1. int a = 0b10000000_00000000_00000000_00000000;
2. int b = a >> 2;
3. String bin = "00000000000000000000000000000000" + Integer.toBinaryString(b);
4. bin = bin.substring(bin.length() - 32);
5. System.err.println(b + "," + bin);

在上面的代码中,a变量的二进制形式是以1开始的,所以为负数,使用>>右移两位,即后面去除两个0,则是有符号位移,所以前面补1,结果为-536870912,11100000000000000000000000000000,即依然为负数。如果使用>>>无符号位移,即将第2行的代码修改成“int b =a>>>2;”,结果为536870912,00100000000000000000000000000000即前面补0,结果为正数。对于无符号位移,无论是正数还是负数,前面都补0,有符号位移则会根据情况前面补0或补1。