第 6 章 运算符

第 6 章 运算符

Java语言中的运算符(也称操作符)在风格和功能上都与C 和C++极为相似。本章为大家介绍Java语言中一些主要的运算符,包括算术运算符、关系运算符、逻辑运算符、位运算符和其他运算符。

6.1 算术运算符

Java中的算术运算符主要用来组织数值类型数据的算术运算,按照参加运算的操作数的不同可以分为一元运算符和二元运算符。

6.1.1 一元运算符

算术一元运算符一共有3个,分别是-、++和--。具体说明参见表6-1。

表 6-1 一元算术运算符

{%}

表6-1中,-a是对a取反运算,a++或a--是在表达式运算完后,再给a加一或减一。而++a或--a是先给a加一或减一,然后再进行表达式运算。

示例代码如下:

int a = 12;
System.out.println(-a);             ①
int b = a++;                        ②
System.out.println(b);
b = ++a;                            ③
System.out.println(b);

输出结果如下:

-12
12
14

上述代码第①行是-a,是把a变量取反,结果输出是-12。第②行代码是先把a赋值给b变量再加一,即先赋值后++,因此输出结果是12。第③行代码是把a加一,然后把a赋值给b变量,即先++后赋值,因此输出结果是14。

6.1.2 二元运算符

二元运算符包括:+、-、*、/和%,这些运算符对数值类型数据都有效,具体说明参见表6-2。

表 6-2 二元算术运算符

{%}

示例代码如下:

//声明一个字符类型变量
char charNum = 'A';
// 声明一个整数类型变量
int intResult = charNum + 1;            ①
System.out.println(intResult);

intResult = intResult - 1;
System.out.println(intResult);

intResult = intResult * 2;
System.out.println(intResult);

intResult = intResult / 2;
System.out.println(intResult);

intResult = intResult + 8;
intResult = intResult % 7;
System.out.println(intResult);

System.out.println("-------");

// 声明一个浮点型变量
double doubleResult = 10.0;
System.out.println(doubleResult);

doubleResult = doubleResult - 1;
System.out.println(doubleResult);

doubleResult = doubleResult * 2;
System.out.println(doubleResult);

doubleResult = doubleResult / 2;
System.out.println(doubleResult);

doubleResult = doubleResult + 8;
doubleResult = doubleResult % 7;
System.out.println(doubleResult);

输出结果如下:

66
65
130
65
3
-------
10.0
9.0
18.0
9.0
3.0

上述例子中分别对数值类型数据进行了二元运算,其中代码第①行将字符类型变量charNum与整数类型进行加法运算,参与运算的该字符('A')的Unicode编码为65。其他代码比较简单不再赘述。

6.1.3 算术赋值运算符

算术赋值运算符只是一种简写,一般用于变量自身的变化,具体说明参见表6-3。

表 6-3 算术赋值运算符

{%}

示例代码如下:

int a = 1;
int b = 2;
a += b;     // 相当于 a = a + b
System.out.println(a);

a += b + 3; // 相当于 a = a + b + 3
System.out.println(a);
a -= b;     // 相当于 a = a - b
System.out.println(a);

a *= b;     // 相当于 a=a*b
System.out.println(a);

a /= b;        // 相当于 a=a/b
System.out.println(a);

a %= b;        // 相当于 a=a%b
System.out.println(a);

输出结果如下:

3
8
6
12
6
0

上述例子分别对整型进行了+=、-=、*=、/=和%=运算,具体语句不再赘述。

6.2 关系运算符

关系运算是比较两个表达式大小关系的运算,它的结果是布尔类型数据,即true或false。关系运算符有6种:==、!=、>、<、>=和<=,具体说明参见表6-4。

表 6-4 关系运算符

{%}

提示 ==和!=可以应用于基本数据类型和引用数据类型。当用于引用数据类型比较时,比较的是两个引用是否指向同一个对象,但在当时实际开发过程中多数情况下,只是比较对象的内容是否相当,不需要比较是否为同一个对象。

示例代码如下:

int value1 = 1;
int value2 = 2;

if (value1 == value2) {
    System.out.println("value1 == value2");
}

if (value1 != value2) {
    System.out.println("value1 != value2");
}

if (value1 > value2) {
    System.out.println("value1 > value2");
}

if (value1 < value2) {
    System.out.println("value1 < value2");
}

if (value1 <= value2) {
    System.out.println("value1 <= value2");
}

运行程序输出结果如下:

value1 != value2
value1 < value2
value1 <= value2

6.3 逻辑运算符

逻辑运算符是对布尔型变量进行运算,其结果也是布尔型,具体说明参见表6-5。

表 6-5 逻辑运算符

{%}

提示 短路与(&&)和短路或(||)能够采用最优化的计算方式,从而提高效率。在实际编程时,应该优先考虑使用短路与和短路或。

示例代码如下:

int i = 0;
int a = 10;
int b = 9;

if ((a > b) || (i == 1)) {                        ①
    System.out.println("或运算为 真");
} else {
    System.out.println("或运算为 假");
}

if ((a < b) && (i == 1)) {                        ②
    System.out.println("与运算为 真");
} else {
    System.out.println("与运算为 假");
}

if ((a > b) || (a++ == --b)) {                    ③
    System.out.println("a = " + a);
    System.out.println("b = " + b);
}

上述代码运行输出结果如下:

或运算为 真
与运算为 假
a = 10, b = 9

其中,第①行代码进行短路计算,由于(a > b)是true,后面的表达式(i == 1)不再计算,输出的结果为真。类似地,第②行代码也进行短路计算,由于(a < b)是false,后面的表达式(i == 1)不再计算,输出的结果为假。

代码第③行中在条件表达中掺杂了++和—运算,由于(a > b)是true,后面的表达式(a++ == --b)不再计算,所以最后是a = 10, b = 9。如果把短路或(||)改为逻辑或(|),那么输出的结果就是a = 11, b = 8了。

6.4 位运算符

位运算是以二进位(bit)为单位进行运算的,操作数和结果都是整型数据。位运算符有如下几个运算符:&、|、^、~、>>、<<和>>>,以及相应的赋值运算符,具体说明参见表6-6。

表 6-6 位运算符

{%}

注意 无符号右移>>>运算符仅被允许用在int和long整数类型,如果用于short或byte数据,则数据在位移之前,转换为int类型后再进行位移计算。

位运算示例代码:

byte a = 0B00110010;    //十进制50                             ①
byte b = 0B01011110;    //十进制94                             ②

System.out.println("a | b = " + (a | b));     // 0B01111110    ③
System.out.println("a & b = " + (a & b));     // 0B00010010    ④
System.out.println("a ^ b = " + (a ^ b));     // 0B01101100    ⑤
System.out.println("~b = " + (~b));         // 0B10100001      ⑥

System.out.println("a >> 2 = " + (a >> 2));     // 0B00001100      ⑦
System.out.println("a >> 1 = " + (a >> 1));     // 0B00011001      ⑧
System.out.println("a >>> 2 = " + (a >>> 2));     // 0B00001100    ⑨
System.out.println("a << 2 = " + (a << 2));     // 0B11001000      ⑩
System.out.println("a << 1 = " + (a << 1));     // 0B01100100      ⑪

int c = -12;                                      ⑫
System.out.println("c >>> 2 = " + (c >>> 2));     ⑬
System.out.println("c >> 2 = " + (c >> 2));       ⑭

输出结果如下:

a | b = 126
a & b = 18
a ^ b = 108
~b = -95
a >> 2 = 12
a >> 1 = 25
a >>> 2 = 12
a << 2 = 200
a << 1 = 100
c >>> 2 = 1073741821
c >> 2 = -3

上述代码第①行和第②行分别定义了byte变量a和b,为了便于查看代码采用二进制整数表示。

代码第③行中表达式(a | b)进行位或运算,结果是二进制的0B01111110。a和b按位进行或计算,只要有一个为1,这一位就为1,否则为0。

代码第④行(a & b)是进行位与运算,结果是二进制的0B00010010。a和b按位进行与计算,只有两位全部为1,这一位才为1,否则为0。

代码第⑤行(a ^ b)是进行位异或运算,结果是二进制的0B01101100。a和b按位进行异或计算,只有两位相反时这一位才为1,否则为0。

代码第⑦行(a >> 2)是进行有符号右位移2位运算,结果是二进制的0B00001100。a的低位被移除掉,由于是正数符号位是0,高位空位用0补。类似代码第⑧行(a >> 1)是进行右位移1位运算,结果是二进制的0B00011001。

代码第⑨行(a >>> 2)是进行无符号右位移2位运算,与代码第⑦行不同的是,无论是否有数符号位,高位空位用0补,所以在正数情况下>>和>>>运算结果是一样的。

代码第⑩行(a << 2)是进行左位移2位运算,结果是二进制的0B11001000。a的高位被移除掉,低位用0补位。类似代码第⑪行(a << 1)是进行左位移1位运算,结果是二进制的0B01100100。

代码第⑫声明int类型负数。右位移(>>>和>>)在负数情况下差别比较大。代码第⑬行的(c >>> 2)表达式输出结果是1073741821,这是一个如此大的正数,从一个负数变成一个正数,这说明无符号右位移对于负数计算会导致精度的丢失。而有符号右位移对于负数的计算是正确的,见代码第⑭行。

提示 有符号右移n位,相当于操作数除以2n,例如代码第⑦行(a >> 2)表达式相当于(a / 22),a = 50所以结果等于12,类似的还有代码第⑧行和第⑭行。另外,左位移n位,相当于操作数乘以2n,例如代码第⑩行(a << 2)表达式相当于(a * 22),a = 50所以结果等于200,类似的还有代码第⑪行。

6.5 其他运算符

除了前面介绍的主要运算符,Java还有一些其他运算符。

  • 三元运算符(? :)。例如x?y:z;,其中x、y和z都为表达式。

  • 小括号。起到改变表达式运算顺序的作用,它的优先级最高。

  • 中括号。数组下标。

  • 引用号(.)。对象调用实例变量或实例方法的操作符,也是类调用静态变量或静态方法的操作符。

  • 赋值号(=)。赋值是用等号运算符(=)进行的。

  • instanceof。判断某个对象是否为属于某个类。

  • new。对象内存分配运算符。

  • 箭头(->)。Java 8新增加的,用来声明Lambda表达式。

  • 双冒号(::)。Java 8新增加的,用于Lambda表达式中方法的引用。

示例代码如下:

import java.util.Date;

public class HelloWorld {

    public static void main(String[] args) {

        int score = 80;
        String result = score > 60 ? "及格" : "不及格"; // 三元运算符(? : )
        System.out.println(result);

        Date date = new Date();                 // new运算符可以创建Date对象
        System.out.println(date.toString());    //通过.运算符调用方法

    }
}

此外,还有一些鲜为人知的运算符,随着学习的深入用到后再为大家介绍,这里就不再赘述了。

6.6 运算符优先级

在一个表达式计算过程中,运算符的优先级非常重要。表6-7中从上到小,运算符的优先级从高到低,同一行具有相同的优先级。二元运算符计算顺序从左向右,但是先级15的赋值运算符的计算顺序从右向左的。

表 6-7 Java运算符优先级

{%}

总结 运算符优先级大体顺序,从高到低是:算术运算符→位运算符→关系运算符→逻辑运算符→赋值运算符。

本章小结

通过对本章内容的学习,读者可以了解到Java语言的基本运算符,这些运算符包括算术运算符、关系运算符、逻辑运算符、位运算符和其他运算符。