运算符

运算符是一些特殊的符号,主要用于数学函数、一些类型的赋值语句和逻辑比较方面。Java 中提供了丰富的运算符,如赋值运算符、算术运算符、比较运算符等。本节将向读者介绍这些运算符。

赋值运算符

赋值运算符以符号 “=” 表示,它是一个二元运算符(对两个操作数做处理),其功能是将右方操作数所含的值赋给左方的操作数。例如:

int a = 100;

该表达式是将 100 赋值给变量 a。左方的操作数必须是一个变量,而右边的操作数则可以是任何表达式,包括变量(如 a、number)、常量(如 123、'book')、有效的表达式(如 45 * 12)。

由于赋值运算符 “=” 处理时会先取得右方表达式处理后的结果,因此一个表达式中若含有两个以上的 “=” 运算符,会从最右方的 “=” 开始处理。

【例3.7】使用赋值运算符同时为两个变量赋值(实例位置:资源包\TM\sl\3\7)

在项目中创建类 Eval,在主方法中定义变量,使用赋值运算符为变量赋值。

public class Eval { // 创建类
	public static void main(String[] args) { // 主方法
		int a, b, c; // 声明int型变量a、b、c
		a = 15; // 将15赋值给变量a
		c = b = a + 4; // 将a与4的和赋值给变量b,然后再赋值给变量c
		System.out.println("c值为:" + c); // 将变量c的值输出
		System.out.println("b值为:" + b); // 将变量b的值输出
	}
}

运行结果如下:

c值为:19
b值为:19

在 Java 中可以把赋值运算符连在一起使用。如:

x = y = z = 5;

在这个语句中,变量 x、y、z 都得到同样的值 5,但在实际开发中建议开发者分开对其进行赋值,这样可以让代码的层次更清晰。

算术运算符

Java 中的算术运算符主要有 +(加)、-(减)、*(乘)、/(除)、%(求余),它们都是二元运算符。Java 中算术运算符的功能及使用方式如表3.5 所示。

image 2023 12 12 15 01 00 345
Figure 1. 表3.5 Java算术运算符

其中,“+” 和 “-” 运算符还可以作为数值的正负符号,如 +5、-7。

在进行除法运算时,0 不能做除数。例如,对于语句 “int a = 5 / 0;”,系统会抛出 ArithmeticException 异常。

下面通过一个小程序来介绍算术运算符的使用方法。

【例3.8】使用算术运算符模拟计算器(实例位置:资源包\TM\sl\3\8)

创建 ArithmeticOperator 类,让用户输入两个数字,分别用 5 种运算符对这两个数字进行计算。

import java.util.Scanner;

public class ArithmeticOperator {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in); // 创建扫描器,获取控制台输入的值
		System.out.println("请输入两个数字,用空格隔开(num1 num2):"); // 输出提示
		double num1 = sc.nextDouble(); // 记录输入的第一个数字
		double num2 = sc.nextDouble(); // 记录输入的第二个数字
		System.out.println("num1+num2的和为:" + (num1 + num2)); // 计算和
		System.out.println("num1-num2的差为:" + (num1 - num2)); // 计算差
		System.out.println("num1*num2的积为:" + (num1 * num2)); // 计算积
		System.out.println("num1/num2的商为:" + (num1 / num2)); // 计算商
		System.out.println("num1%num2的余数为:" + (num1 % num2)); // 计算余数
		sc.close();// 关闭扫描器
	}
}

运行结果如图3.6 所示,图中数字 23 和 15 为用户在控制台中输入的值。

image 2023 12 12 15 03 47 771
Figure 2. 图3.6 运行结果

代码中出现的 Scanner 扫描器类可以让程序获得用户在控制台输入的值,关于 Scanner 类的详细用法请参考本书 “第11章常用类库” 的相关内容。

自增和自减运算符

自增、自减运算符是单目运算符,可以放在操作元之前,也可以放在操作元之后。操作元必须是一个整型或浮点型变量。自增、自减运算符的作用是使变量的值增1或减1。放在操作元前面的自增、自减运算符,会先将变量的值加1(减 1),然后使该变量参与表达式的运算。放在操作元后面的自增、自减运算符,会先使变量参与表达式的运算,然后将该变量加1(减 1)。例如:

++a(--a)  //表示在使用变量a之前,先将a的值加(减)1
a++(a--)  //表示在使用变量a之后,将a的值加(减)1

粗略地分析,++a 与 a++ 的作用都相当于 a = a + 1。假设 a = 4,则:

b = ++a;  //先将a的值加1,然后赋给b,此时a值为5,b值为5

再看另一个语法,同样假设 a = 4,则:

b = a++;  //先将a的值赋给b,再将a的值变为5,此时a值为5,b值为4

比较运算符

比较运算符属于二元运算符,用于程序中的变量之间、变量和自变量之间以及其他类型的信息之间的比较。比较运算符的运算结果是 boolean 型。当运算符对应的关系成立时,运算结果为 true,否则为 false。所有比较运算符通常作为判断的依据用在条件语句中。比较运算符共有 6 个,如表3.6 所示。

image 2023 12 12 15 24 53 309
Figure 3. 表3.6 比较运算符

【例3.9】使用不同的比较运算符判断两个整数的关系(实例位置:资源包\TM\sl\3\9)

在项目中创建类 Compare,在主方法中创建整型变量,使用比较运算符对变量进行比较运算,并输出运算后的结果。

public class Compare { 						//创建类
	public static void main(String[] args) {
		int number1 = 4; 					//声明int型变量number1
		int number2 = 5; 					//声明int型变量number2
		//依次将变量number1与变量number2的比较结果输出
		System.out.println("number1>number的返回值为:" + (number1 > number2));
		System.out.println("number1< number2返回值为:"+ (number1 < number2));
		System.out.println("number1==number2返回值为:"+ (number1 == number2));
		System.out.println("number1!=number2返回值为:"+ (number1 != number2));
		System.out.println("number1>= number2返回值为:"+ (number1 >= number2));
		System.out.println("number1<=number2返回值为:"+ (number1 <= number2));
	}
}

运行结果如下:

number1>number2的返回值为:false
number1< number2返回值为:true
number1==number2返回值为:false
number1!=number2返回值为:true
number1>= number2返回值为:false
number1<=number2返回值为:true

逻辑运算符

返回类型为布尔型的表达式(如比较运算符)可以被组合在一起构成一个更复杂的表达式。这是通过逻辑运算符来实现的。逻辑运算符包括 &(&&)(逻辑与)、||(逻辑或)、!(逻辑非)。逻辑运算符的操作元必须是 boolean 型数据。在逻辑运算符中,除了 “!” 是一元运算符,其他都是二元运算符。表3.7 给出了逻辑运算符的用法和含义。

image 2023 12 12 15 27 18 158
Figure 4. 表3.7 逻辑运算符

结果为 boolean 型的变量或表达式可以通过逻辑运算符组合为逻辑表达式。

用逻辑运算符进行逻辑运算时,结果如表3.8 所示。

image 2023 12 12 15 27 55 663
Figure 5. 表3.8 使用逻辑运算符进行逻辑运算

逻辑运算符 “&&” 与 “&” 都表示 “逻辑与”,那么它们之间的区别在哪里呢?从表3.8 中可以看出,当两个表达式都为 true 时,“逻辑与” 的结果才会是 true。使用逻辑运算符 “&” 会判断两个表达式;而逻辑运算符 “&&” 则是针对 boolean 类型的类进行判断的,当第一个表达式为 false 时则不去判断第二个表达式,直接输出结果,从而节省计算机判断的次数。通常将这种在逻辑表达式中从左端的表达式可推断出整个表达式的值的情况称为 “短路”,而将那些始终需要执行逻辑运算符两边的表达式才能推断出整个表达式的值的情况称为 “非短路”。“&&” 属于 “短路” 运算符,而 “&” 属于 “非短路” 运算符。

【例3.10】使用不同的比较运算符判断两个整数的关系(实例位置:资源包\TM\sl\3\10)

在项目中创建类 Calculation,在主方法中创建 3 个整数,分别记录男生人数、女生人数和总人数,使用逻辑运算符来判断 “男生人数大于女生人数并且总人数大于 30 人” 和 “男生人数大于女生人数或者总人数大于 30 人” 这两种情况是否存在。

public class Calculation {
	public static void main(String[] args) {
		int boys = 15;// 男生人数
		int girls = 17;// 女生人数
		int totle = boys + girls;// 总人数
		// 男生人数大于女生人数,并且总人数大于30人
		boolean result1 = ((boys > girls) && (totle > 30));
		// 男生人数大于女生人数,或者总人数大于30人
		boolean result2 = ((boys > girls) || (totle > 30));
		// 输出结果
		System.out.println("男生人数大于女生人数并且总人数大于30人:" + result1);
		System.out.println("男生人数大于女生人数或者总人数大于30人:" + result2);
	}
}

运行结果如下:

男生人数大于女生人数并且总人数大于30人:false
男生人数大于女生人数或者总人数大于30人:true

位运算符

位运算符除 “按位与” 和 “按位或” 运算符外,其他只能用于处理整数的操作数,包括 byte、short、char、int 和 long 等数据类型。位运算是完全针对位方面的操作。整型数据在内存中以二进制的形式进行表示,如 int 型变量 7 的二进制表示是 00000000 00000000 00000000 00000111。

左边最高位是符号位,最高位是 0 表示正数,若为 1 则表示负数。负数采用补码表示,如 -8 的二进制表示为 111111111 111111111 1111111 11111000。这样就可以对整型数据进行按位运算。

  1. "按位与" 运算

    “按位与” 运算的运算符为 “&”,为双目运算符。“按位与” 运算的运算法则是:如果两个整型数据 a、b 对应位都是 1,则结果位才是 1,否则为 0。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图3.7 所示。

    image 2023 12 12 15 32 20 653
    Figure 6. 图3.7 5 & -4的运算过程
  2. "按位或" 运算

    "按位或" 运算的运算符为 “|”,为双目运算符。“按位或” 运算的运算法则是:如果两个操作数对应位都是 0,则结果位才是 0,否则为 1。如果两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图3.8 所示。

    image 2023 12 12 15 43 33 366
    Figure 7. 图3.8 3 | 6的运算过程
  3. “按位取反” 运算

    “按位取反” 运算也称 “按位非” 运算,运算符为 “~”,为单目运算符。“按位取反” 就是将操作数二进制中的 1 修改为 0,0 修改为 1,如图3.9 所示。

    image 2023 12 12 15 45 52 523
    Figure 8. 图3.9 ~7的运算过程
  4. “按位异或” 运算

    “按位异或” 运算的运算符是 “^”,为双目运算符。“按位异或” 运算的运算法则是:当两个操作数的二进制表示相同(同时为 0 或同时为 1)时,结果为 0,否则为 1。若两个操作数的精度不同,则结果的精度与精度高的操作数相同,如图3.10 所示。

    image 2023 12 12 15 46 42 008
    Figure 9. 图3.10 10 ^ 3的运算过程
  5. 移位操作

除了上述运算符,还可以对数据按二进制位进行移位操作。Java 中的移位运算符有以下 3 种:

  • <<:左移。

  • >>:右移。

  • >>>:无符号右移。

左移就是将运算符左边的操作数的二进制数据,按照运算符右边操作数指定的位数向左移动,右边移空的部分补 0。右移则复杂一些。当使用 “>>” 符号时:如果最高位是 0,右移空的位就填入 0;如果最高位是 1,右移空的位就填入 1,如图 3.11 所示。

image 2023 12 12 15 49 42 714
Figure 10. 图3.11 右移

Java 还提供了无符号右移 “>>>”,无论最高位是 0 还是 1,左侧被移空的高位都填入 0。

移位可以实现整数除以或乘以 2n 的效果。例如,y << 2与 y*4 的结果相同,y >> 1的结果与 y / 2 的结果相同。总之:一个数左移 n 位,就是将这个数乘以 2n;一个数右移 n 位,就是将这个数除以 2n

复合赋值运算符

和其他主流编程语言一样,Java 中提供了复合赋值运算符。所谓复合赋值运算符,就是将赋值运算符与其他运算符合并成一个运算符来使用,从而同时实现两种运算符的效果。Java 中的复合运算符如表3.9 所示。

image 2023 12 12 15 52 16 663
Figure 11. 表3.9 复合赋值运算符

以 “+=” 为例,虽然 “a += 1” 与 “a = a + 1” 二者最后的计算结果是相同的,但是在不同的场景下,两种运算符都有各自的优势和劣势:

(1) 低精度类型自增。

在 Java 编译环境中,整数的默认类型时 int 型,因此下面的赋值语句会报错:

byte a = 1;  //创建byte型变量a
a = a + 1;   //让a的值+1,错误提示:无法将int型转换成byte型

在没有进行强制转换的条件下,a+1 的结果是一个 int 值,无法直接赋给一个 byte 变量。但是如果使用 “+=” 实现递增计算,就不会出现这个问题。

byte a = 1;  //创建byte型变量a
a += 1;      //让a的值+1

(2) 不规则的多值相加。

“+=” 虽然简洁、强大,但是有些时候是不好用的,比如下面这个语句:

a = (2 + 3 - 4) * 92 / 6;

这条语句如果改成使用复合赋值运算符,代码就会显得比较烦琐,代码如下:

a += 2;
a += 3;
a -= 4;
a *= 92;
a /= 6;

三元运算符

三元运算符的使用格式如下:

条件式 ? 值1 : 值2

三元运算符的运算法则是:若条件式的值为 true,则整个表达式取值 1,否则取值 2。例如:

boolean b = 20 < 45 ? true : false;

上述程序表达式 “20 < 45” 的运算结果返回真,那么 boolean 型变量 b 取值为 true。相反,如果表达式的运算结果返回为假,则 boolean 型变量 b 取值为 false。

三元运算符等价于 if…else 语句,例如上述代码等价于:

boolean a;  // 声明boolean型变量
if(20 < 45)  // 将20 < 45 作为判断条件
    a = true;  // 条件成立,将true赋值给a
else
    a = false; // 条件不成立,将false赋值给a

运算符的优先级

Java 中的表达式就是使用运算符连接起来的符合 Java 规则的式子。运算符的优先级决定了表达式中运算执行的先后顺序。通常,优先级由高到低的顺序依次是:

  • 增量和减量运算。

  • 算术运算。

  • 比较运算。

  • 逻辑运算。

  • 赋值运算。

如果两个运算有相同的优先级,那么左边的表达式要比右边的表达式先被处理。表3.10显示了在 Java 中众多运算符特定的优先级。

image 2023 12 12 16 24 25 522
Figure 12. 表3.10 运算符的优先级

在编写程序时尽量使用圆括号来指定运算次序,以免产生错误的运算顺序。

编程训练(答案位置:资源包\TM\sl\3\编程训练)

【训练5】计算机车加速度 平均加速度,即速度的变化量除以这个变化所用的时间。现有一辆轿车用了 8.7 秒从每小时 0 千米加速到每小时 100 千米,计算并输出这辆轿车的平均加速度。

【训练6】求解二元一次方程组 使用克莱姆法则求解二元一次方程组。

image 2023 12 12 16 25 58 317

提示:克莱姆法则求解二元一次方程组的公式如下:

image 2023 12 12 16 26 24 292