Java17-入门基础知识-三-

252 阅读56分钟

Java17 入门基础知识(三)

原文:Beginning Java 17 Fundamentals

协议:CC BY-NC-SA 4.0

五、运算符

在本章中,您将学习:

  • 什么是运算符

  • Java 中可用的不同类型的运算符

  • 运算符优先级,用于确定在同一个表达式中使用多个运算符时,运算符的计算顺序

我们在这一章中使用了很多代码片段。评估这些代码片段并查看结果的最快方法是使用 JShell 工具。关于如何在命令提示符下启动 JShell 工具,请参考第二章。

本章中的所有类都是名为jdojo.operator的模块的成员,其声明如清单 5-1 所示。

// module-info.java
module jdojo.operator {
    // No module statements
}

Listing 5-1Declaration of a Module Named jdojo.operator

什么是运营商?

一个操作符是一个符号,它对一个、两个或三个操作数执行特定种类的操作,并产生一个结果。运算符及其操作数的类型决定了所执行的运算的种类以及所产生的结果的类型。Java 中的运算符可以根据两个标准进行分类:

  • 它们所操作的操作数的数量

  • 它们对操作数执行的运算类型

根据操作数的数量,有三种类型的运算符:

  • 一元运算符

  • 二元运算符

  • 三元算子

如果一个操作符有一个操作数,这叫做一元操作符;如果它有两个操作数,就叫做二元运算符;如果它有三个操作数,就叫做三元运算符。

一元运算符可以使用后缀或前缀符号。在后缀表示法中,运算符出现在其操作数之后:

<operand> <postfix-unary-operator>

以下是使用后缀一元运算符的示例:

// num is an operand and ++ is a Java unary operator
num++

在前缀表示法中,一元运算符出现在其操作数之前:

<prefix-unary-operator> <operand>

以下是使用前缀一元运算符的示例。请注意,Java 中的++运算符既可以用作前缀,也可以用作后缀运算符:

// ++ is a Java unary operator and num is an operand
++num

二元运算符使用中缀符号。运算符出现在两个操作数之间。使用中缀二元运算符的语法如下:

<first-operand> <infix-binary-operator> <second-operand>

下面是一个使用+作为中缀二元运算符的例子:

// 10 is the first operand, + is a binary operator, and 15 is the second operand
10 + 15

像二元运算符一样,三元运算符也使用中缀符号。使用中缀三元运算符的语法如下:

<first-operand> <operator1> <second-operand> <operator2> <third-operand>

这里,<operator1><operator2>都是三元运算符。

以下是在 Java 中使用三元运算符的示例:

// isSunday is the first operand, ? is the first part of ternary operator,
// holiday is the second operand,: is the second part of ternary operator, noHoliday is the
// third operand
isSunday ? holiday : noHoliday

根据操作员执行的操作类型,您可以将他们分为以下几类:

  • 算术运算符

  • 关系运算符

  • 逻辑运算符

  • 按位运算符

Java 有一个很大的操作符列表。本章讨论了大多数 Java 操作符。在后面的章节中,当上下文合适时,我们会讨论其中的一些。在随后的章节中享受学习 Java 操作符的漫长旅程吧!

赋值运算符

赋值运算符(=)用于给变量赋值。它是一个二元运算符。它需要两个操作数。右操作数的值赋给左操作数。左侧操作数必须是变量,例如:

int num;
num = 25;

这里,num = 25使用赋值运算符=。在这个例子中,25是右边的操作数,num是左边的操作数,是一个变量。

Java 确保赋值运算符右侧操作数的值与左侧操作数的数据类型赋值兼容;否则,会发生编译时错误。在引用变量的情况下,如果右边操作数表示的对象与指定为左边操作数的引用变量的赋值不兼容,您可能能够编译源代码并得到运行时错误。例如,byteshortchar类型的值与int数据类型的赋值是兼容的,因此下面的代码片段是有效的:

byte b = 5;
char c = 'a';
short s = -200;
int i = 10;
i = b; // OK. byte b is assignment compatible to int i
i = c; // OK. char c is assignment compatible to int i
i = s; // OK. short s is assignment compatible to int i

然而,long -to- intfloat -to- int赋值是不兼容的,因此下面的代码片段会生成编译时错误:

long big = 524L;
float f = 1.19F;
int i = 15;
i = big; // A compile-time error. long to int, assignment incompatible
i = f;   // A compile-time error. float to int, assignment incompatible

如果右侧操作数的值与左侧变量的数据类型不兼容,则右侧操作数的值必须转换为适当的类型。前面这段使用赋值运算符的代码可以通过如下强制转换来重写:

i = (int)big; // OK
i = (int)f;   // OK

一个表达式是一系列的变量、操作符和方法调用,根据 Java 编程语言的语法构造,计算为单个值。比如,num = 25就是一个表达式。使用赋值运算符的表达式也有一个值。表达式的值等于右边操作数的值。考虑下面这段代码,假设num是一个int变量:

num = 25;

这里,num = 25称为表达式,num = 25;称为语句。num = 25这个表达有两个意思:

  • 将值 25 赋给变量num

  • 产生值 25,该值等于赋值运算符右侧操作数的值

在表达式中使用赋值操作符的第二个效果(产生一个值)此时可能看起来很奇怪。您可能想知道表达式num = 25产生的值 25 会发生什么变化。我们曾经使用过表达式返回的值吗?答案是肯定的。我们使用表达式返回的值。考虑下面的表达式,它使用链式赋值操作符,假设num1num2int变量。

num1 = num2 = 25;

执行这段代码时会发生什么?首先,执行表达式num2 = 25的一部分。

如前所述,该执行将有两个效果:

  • 它将赋值 25 给num2

  • 它将产生 25 的值。换句话说,你可以说在将值 25 赋给num2后,表达式num2 = 25被值 25 代替,这就把主表达式num1 = num2 = 25变成了num1 = 25

现在,表达式num1 = 25被执行,值 25 被赋给num1,产生的值25被忽略。这样,您可以在一个表达式中为多个变量分配相同的值。在这种链式赋值表达式中可以使用任意数量的变量。

例如:

num1 = num2 = num3 = num4 = num5 = num6 = 219;

假设有两个int变量num1num2。下面的赋值num1 = num2将存储在num2中的值200赋值给num1:

int num1 = 100; // num1 is 100
int num2 = 200; // num2 is 200
num1 = num2;    // num1 is 200\. num2 is 200
num2 = 500;     // num2 is 500\. num1 is still 200

当你说num1 = num2的时候,存储在num2中的值被复制到num1,而num1num2都维护着自己的同一个值200的副本。稍后,当执行num2 = 500时,只有num2的值变为500。但是num1的值保持不变,200。现在,假设有两个引用变量,ref1ref2,它们引用同一个类的两个不同的对象。假设我们写

ref1 = ref2;

表达式ref1 = ref2的作用是,引用变量ref1ref2现在引用内存中的同一个对象——被ref2引用的对象。在这个赋值之后,两个参考变量ref1ref2同样能够操纵对象。参考变量ref1对内存中对象的更改也将被ref2观察到,反之亦然。第七章详细介绍了参考变量赋值。

声明、初始化和赋值

在 Java 程序中使用任何类型的变量之前,必须对其进行声明并赋值。假设您想要使用一个名为num1int变量。首先,您必须声明如下:

// Declaration of a variable num1
int num1;

可以在声明变量之后或者在声明变量的时候给变量赋值。当一个值在声明后被赋值给一个变量时,这就是所谓的赋值。下面这段代码声明了一个int变量num2,并将50赋给它:

int num2;   // Declaration of a variable num2
num2 = 50;  // Assignment

当一个值在声明的时候被赋给一个变量,这就是所谓的初始化。

下面的代码声明了一个int变量num3,并将其初始化为一个值100:

// Declaration of variable num3 and its initialization
int num3 = 100;

通过用逗号分隔每个变量的名称,可以在一个声明中声明多个相同类型的变量:

// Declaration of three variables num1, num2 and num3 of type int
int num1, num2, num3;

您也可以在一个声明中声明多个变量,并初始化部分或全部变量:

// Declaration of variables num1, num2 and num3\. Initialization of only num1 and num3
int num1 = 10, num2, num3 = 200;
// Declaration and initialization of variables num1, num2 and num3
int num1 = 10, num2 = 20, num3 = 30;

Java 不会让你使用一个变量,除非它已经通过初始化或者赋值的过程被赋值了。Java 隐式初始化一些上下文中声明的变量。在其他上下文中声明的变量,如果 Java 没有隐式地初始化它们,那么在使用它们之前,必须进行初始化或者赋值。我们将在第七章中讨论 Java 对变量的隐式初始化。在声明变量时初始化变量是一种好的做法。

算术运算符

算术运算符是将数值作为其操作数并执行算术运算(例如,加法和减法)来计算另一个数值的运算符。表 5-1 列出了 Java 中所有的算术运算符。表 5-1 中列出的所有运算符只能用于数值型操作数。也就是说,算术运算符的两个操作数必须是类型byteshortcharintlongfloatdouble中的一种。这些运算符不能有boolean基元类型和引用类型的操作数。以下部分详细描述了算术运算符。

表 5-1

Java 中的算术运算符列表

|

运算符

|

描述

|

类型

|

使用

|

结果

| | --- | --- | --- | --- | --- | | + | 添加 | 二进制的 | 2 + 5 | seven | | - | 减法 | 二进制的 | 5 - 2 | three | | + | 一元加号 | 一元的 | +5 | 正五。同 5。 | | - | 一元减操作 | 一元的 | -5 | 负五。 | | * | 增加 | 二进制的 | 5 * 3 | Fifteen | | / | 分开 | 二进制的 | 5 / 2 | Two | |   |   |   | 6 / 2 | three | |   |   |   | 5.0 / 2.0 | Two point five | |   |   |   | 6.0 / 2.0 | Three | | % | 系数 | 二进制的 | 5 % 3 | Two | | ++ | 增量 | 一元的 | num++ | 计算 num 的值。将 num 增加 1。 | | -- | 减量 | 一元的 | num-- | 评估为num的值。将num减 1。 | | += | 算术复合赋值 | 二进制的 | num += 5 | 将num的值加 5,并将结果赋给num。如果num是 10,num的新值将是 15。 | | -= | 算术复合赋值 | 二进制的 | num -= 3 | 从num的值中减去 3,并将结果分配给num。如果num是 10,num的新值将是 7。 | | *= | 算术复合赋值 | 二进制的 | num *= 15 | 将 15 乘以num的值,并将结果赋给num。如果num是 10,num的新值将是 150。 | | /= | 算术复合赋值 | 二进制的 | num /= 5 | 将num的值除以 5,并将结果分配给num。如果num是 10,num的新值将是 2。 | | %= | 算术复合赋值 | 二进制的 | num %= 5 | 计算num除以 5 的余数,并将结果分配给num。如果num是 12,那么num的新值将是 2。 |

加法运算符(+)

加法运算符(+)是一种算术运算符,其使用形式如下:

operand1 + operand2

加法运算符用于将两个操作数表示的两个数值相加,例如,5 + 3 等于 8。操作数可以是任何数值、数值变量、数值表达式或方法调用。每个包含加法运算符(+)的表达式都有一个数据类型。表达式的数据类型根据四个规则之一确定:

  • 如果其中一个操作数是double,另一个操作数被转换为double,整个表达式的类型为double

  • 如果其中一个操作数是float,另一个操作数被转换为float,整个表达式的类型为 float。

  • 如果其中一个操作数为long,另一个操作数被转换为long,整个表达式的类型为long

  • 如果前面三个规则都不适用,所有操作数都被转换为int,前提是它们还不属于int类型,并且整个表达式属于int类型。

这些规则有一些重要的含义。考虑一个被赋值为5byte变量b1,如下面这段代码所示:

byte b1;
b1 = 5;

当您试图将相同的值5赋给byte变量b1时,您会得到一个编译时错误,如下面的代码片段所示:

byte b1;
byte b2 = 2;
byte b3 = 3;
b1 = b2 + b3; // A compile-time error. Trying to assign 5 to b1

为什么这段代码会导致编译时错误?表达式b1 = 5b1 = b2 + b3对变量b1赋值5的效果不一样吗?是的,效果是一样的。然而,控制赋值操作的规则在两种情况下是不同的。在表达式b1 = 5中,赋值由一条规则控制,即–128 和 127 之间的任何int文字都可以被赋给一个byte变量。因为 5 介于–128 和 127 之间,所以赋值b1 = 5有效。第二个赋值b1 = b2 + b3,由第四个规则决定算术表达式的数据类型,它使用加法运算符(+)。因为表达式b2 + b3中的两个操作数都是byte类型,所以操作数b2b3首先被转换为int,然后表达式b2 + b3变成了int类型。由于b1的数据类型为byte,小于表达式b2 + b3int数据类型,赋值b1 = b2 + b3,即intbyte不兼容,这就是它产生错误的原因。在这种情况下,您需要将右边表达式的结果转换为左边操作数的数据类型:

b1 = (byte)(b2 + b3); // OK now

初学者可以尝试编写如下代码语句:

b1 = (byte) b2 + b3; // A compile-time error again

两个表达式(byte)(b2 + b3)(byte)b2 + b3是不一样的。在表达式(byte) (b2 + b3)中,首先将b2b3提升为int,然后进行加法运算,得到int类型的值 5。然后,将int5转换为byte并分配给b1

在表达式(byte)b2 + b3中,首先将b2强制转换为byte。注意,这种转换是多余的,因为b2已经是byte的类型;b2b3都晋升为int;整个表达式(byte)b2 + b3属于类型int。由于不允许使用int -to- byte赋值,表达式将不会编译。

第二个表达式(byte)b2 + b3产生的错误提出了一个有趣的问题。为什么 Java 没有先在(byte)b2 + b3中计算出b2 + b3,然后对结果应用(byte)。因为有两个操作要做,一个是对byte的强制转换,另一个是对b2b3的加法,Java 首先对b2进行强制转换,然后对加法进行强制转换。决定先进行造型,再进行添加并不是随意的。Java 中的每个操作符都有一个优先顺序。具有较高优先级的运算符首先被计算,然后才是具有较低优先级的运算符。转换运算符的优先级高于加法运算符。这就是为什么在(byte)b2 + b3.(byte)b2首先被求值的原因。你总是可以使用括号来覆盖操作符的优先级。我们通过在表达式(byte)(b2 + b3).中使用括号来覆盖 cast 操作符的优先级。考虑另一个例子:

byte b1;
b1 = 3 + 2; // Will this line of code compile?

表达式b1 = 3 + 2会编译吗?如果我们应用第四条规则来确定这个表达式的数据类型,它应该不会编译,因为32int文字。表达式3 + 2属于类型int。因为intbyte的赋值不兼容,所以表达式b1 = 3 + 2应该会给出一个错误。但是,我们的假设是错误的,表达式b1 = 3 + 2会编译好。在这种情况下,分配过程如下。

操作数32是常量,所以它们的值在编译时是已知的。因此,编译器在编译时计算表达式3 + 2的结果,并用其结果5替换3 + 2。表达式b1 = 3 + 2被编译器替换为b1 = 5。现在,你可以看到为什么 Java 没有为这个表达式给出任何错误。因为int字面量5-128127, b1 = 5的范围内,所以根据将int字面量赋值给byte变量的规则,它是一个有效的赋值。然而,如果你试图写一个表达式为b1 = 127 + 10,它肯定不会被编译,因为127 + 10的结果,也就是137,超出了byte数据类型的范围。以下是关于操作数的数据类型转换和涉及加法运算符的表达式类型确定的总结:

var = operand1 + operand2;

如果operand1operand2是编译时常量,operand1 + operand2的结果决定了这种赋值是否有效。如果operand1 + operand2的结果在变量var的数据类型范围内,表达式将被编译。否则,将生成编译器错误。如果operand1operand2是一个变量(即operand1operand2的值在编译时无法确定),表达式的数据类型根据本节开始时讨论的四个规则之一确定。以下是加法运算符正确和不正确用法的示例。注释和代码一起表明使用是否正确:

byte b1 = 2;
byte b2 = 3;
short s1 = 100;
int i = 10;
int j = 12;
float f1 = 2.5F;
double d1 = 20.0;
// OK. 125 is in the range -128 and 127
b1 = 15 + 110;
// An error. Data type of i + 5 is int and int to byte assignment is not permitted
b1 = i + 5;
b1 = (byte)(i + 5); // OK
// An error. s1 is promoted to int and s1 + 2 is of the data type int.
// int to byte assignment is not permitted
b1 = s1 + 2;
// An error. b2 is promoted to float and f1 + b2 is of the data type float.
// float to byte assignment is not permitted
b1 = f1 + b2;
// An error. f1 is promoted to double and f1 + d1 is of the data type double
b1 = f1 + d1;
// OK. i is promoted to float and i + f1 is of the data type float
f1 = i + f1;
// An error. i is promoted to double and i + d1 is of data type double.
// double to float assignment is not permitted
f1 = i + d1;
f1 = (float)(i + d1); // OK
// An error. 2.0 and 3.0 are of the type double. The result of 2.0 + 3.2 is 5.2,
// which is also of the type double. double to float assignment is not permitted.
f1 = 2.0 + 3.2;
// OK. 2.0F and 3.2F are of the type float. The result of 2.0F + 3.2F,
// which is 5.2F is of the type float.
f1 = 2.0F + 3.2F;
// OK. j is promoted to float and f1 + j is of the data type float.
// float to double assignment is permitted.
d1 = f1 + j;

减法运算符(-)

减法运算符(-)是一种算术运算符,其使用形式如下:

operand1 - operand2

减法运算符用于计算两个数的差,例如,5 - 3产生2。我们讨论的关于操作数的数字数据转换和涉及加法运算符的表达式的数据类型的确定的所有规则也适用于涉及减法运算符的表达式。以下是使用减法运算符的几个示例:

byte b1 = 5;
int i = 100;
float f1 = 2.5F;
double d1 = 15.45;
// OK. 200 - 173 will be replaced by 27.
// b1 = 27 is OK because 27 is in the range -128 and 127
b1 = 200 - 173;
// An error.  i - 27 is of the type int. int to byte assignment is not allowed
b1 = i - 27;
b1 = (byte)(i - 27);  // OK. Assigns 73 to b1
d1 = f1 - i;          // OK. Assigns -97.5 to d1

乘法运算符(*)

乘法运算符(*)是一种算术运算符,其使用形式如下:

operand1 * operand2

乘法运算符(*)用于计算两个数的乘积,例如,7 * 3产生21。我们讨论的关于操作数的数字数据转换和涉及加法运算符的表达式的数据类型的确定的所有规则也适用于涉及乘法运算符的表达式。以下是使用乘法运算符的一些示例:

byte b1 = 5;
int i = 10;
float f1 = 2.5F;
double d1 = 15.45;
// OK. 20 * 6 will be replaced by 120
// b1 = 120 is OK because 120 is in the range -128 and 127
b1 = 20 * 6;
// An error. i * 12 is of the type int. int to byte assignment is not allowed
b1 = i * 12;
b1 = (byte)(i * 12); // OK. Assigns 120 to b1
// OK. i * b1 is of the type int. int to float assignment is allowed
f1 = i * b1;
// An error. d1 * i is of type double. double to float assignment is not allowed
f1 = d1 * i;
f1 = (float)(d1 * i); // OK. Assigns 154.5 to f1

除法运算符(/)

除法运算符(/)是一种算术运算符,其使用形式如下:

operand1 / operand2

除法运算符(/)用于计算两个数的商,例如,5.0/2.0产生2.5。我们讨论的关于操作数的数字数据转换和涉及加法运算符(+)的表达式的数据类型确定的所有规则也适用于涉及除法运算符的表达式。有两种类型的划分:

  • 整数除法

  • 浮点除法

如果除法运算符的两个操作数都是整数,即byteshortcharintlong,则执行通常的除法运算,结果向零截断以表示一个整数。比如你写一个表达式5/2,除法得出2.5;小数部分0.5被忽略;结果是2。以下示例说明了整数除法:

int num;
num = 5/2; // Assigns 2 to num
num = 5/3; // Assigns 1 to num
num = 5/4; // Assigns 1 to num
num = 5/5; // Assigns 1 to num
num = 5/6; // Assigns 0 to num
num = 5/7; // Assigns 0 to num

在所有这些例子中,赋给变量num的值是一个整数。结果在所有情况下都是整数,不是因为变量num的数据类型是int。结果是一个整数,因为除法运算符的两个操作数都是整数。因为两个操作数的数据类型都是int,所以整个表达式5/3的类型都是int。因为小数部分(如 0.5,0.034)不能存储在int中,所以小数部分被忽略,结果总是整数。使用 JShell 工具是试验这些操作符的好方法。下面的jshell会话向您展示了这些表达式的结果:

jshell> int num;
num ==> 0
jshell> num = 5/2;
num ==> 2
jshell> num = 5/3;
num ==> 1
jshell> num = 5/4;
num ==> 1
jshell> num = 5/5;
num ==> 1
jshell> num = 5/6;
num ==> 0
jshell> num = 5/7;
num ==> 0

如果除法运算符的一个或两个操作数都是floatdouble,则执行浮点除法,并且结果不会被截断。这里有几个例子:

float f1;
// 15.0F and 4.0F are of float type. So, the expression 15.0F/4.0F is of the type float.
// The result 3.75F is assigned to f1.
f1 = 15.0F/4.0F;
// 15 is of type int and 4.0F is of type float. The expression 15/4.0F is of type float.
// The result 3.75F is assigned to f1.
f1 = 15/4.0F;
// An error. 15.0 is of the type double and 4.0F is of the type float. The expression
// 15.0/4.0F is of type double. The result 3.75 is of the type double and cannot be
// assigned to f1 because double to float assignment is not allowed.
f1 = 15.0/4.0F;
// OK. 3.75F is assigned to f1
f1 = (float)(15.0/4.0F);
// 15 and 4 are of type int. The expression 15/4 is of type int. An integer division
// is performed. The result 3 is assigned to f1 because int to float assignment is allowed.
f1 = 15/4;

当你试图将一个数(整数或浮点数)除以零时会发生什么?将一个数除以零的结果取决于除法的类型。如果对数字执行整数除法,除以零会导致运行时错误。如果在 Java 程序中编写一个表达式3/0,它可以很好地编译,但是当这个表达式在运行时执行时,它会给出一个错误,例如:

int i = 2;
int j = 5;
int k = 0;
i = j/k;  // A runtime error. Divide by zero
i = 0/0;  // A runtime error. Divide by zero

如果除法运算符的任一操作数是浮点数,则执行浮点除法,并且该数除以零的结果不是错误。如果被除数(在 7/2 中,7 是被除数,2 是除数)在浮点除零运算中是一个非零数,则结果是正无穷大或负无穷大。如果被除数是浮点零(例如 0.0 或 0.0F),则结果是一个NaN,例如:

float f1 = 2.5F;
double d1 = 5.6;
f1 = 5.0F/0.0F;    // Float.POSITIVE_INFINITY is assigned to f1
f1 = -5.0F/0.0F;   // Float.NEGATIVE_INFINITY is assigned to f1
f1 = -5.0F/-0.0F;  // Float.POSITIVE_INFINITY is assigned to f1
f1 = 5.0F/-0.0F;   // Float.NEGATIVE_INFINITY is assigned to f1
d1 = 5.0/0.0;      // Double.POSITIVE_INFINITY is assigned to d1
d1 = -5.0/0.0;     // Double.NEGATIVE_INFINITY is assigned to d1
d1 = -5.0/-0.0;    // Double.POSITIVE_INFINITY is assigned to d1
d1 = 5.0/-0.0;     // Double.NEGATIVE_INFINITY is assigned to d1
// 5.0F is of the type float and 0 is of the type int. 5.0F/0 is of type float.
// Float.POSITIVE_INFINITY is assigned to f1
f1 = 5.0F/0;
// A compile-time error. 5.0F is of the type float and 0.0 is of the type double
// 5.0F/0.0 is of the type double. double to float assignment is not allowed.
f1 = 5.0F/0.0;
f1 = (float)(5.0F/0.0); // f1 is assigned Float.POSITIVE_INFINITY
f1 = 0.0F/0.0F;         // Assigns Float.NaN to f1
d1 = 0.0/0.0;           // Assigns Double.NaN to d1
d1 = -0.0/0.0;          // Assigns Double.NaN to d1

模数运算符(%)

模数运算符(%)是一个算术运算符,其使用形式如下:

operand1 % operand2

模数运算符(%)也称为余数运算符。模数运算符将左边的操作数除以右边的操作数,并返回除法的余数,例如,7%5的计算结果为2。所有关于操作数的数值数据转换和涉及加法运算符(+)的表达式的数据类型确定的规则也适用于涉及模数运算符的表达式。因为模数运算符的使用涉及除法运算,所以有一些特殊的规则来确定模数运算的结果。

如果模数运算符的两个操作数都是整数,则应用以下规则来计算结果。

规则 1

如果右边的操作数为零,则出现运行时错误,例如:

int num;
num = 15 % 0; // A runtime error

规则 2

如果右侧操作数不为零,结果的符号与左侧操作数的符号相同,例如:

int num;
num = 15 % 6;   // Assigns 3 to num
num = -15 % 6;  // Assigns -3 to num
num = 15 % -6;  // Assigns 3 to num
num = -15 % -6; // Assigns -3 to num because left-hand operand is -15, which is negative
num = 5 % 7;    // Assigns 5 to num
num = 0 % 7;    // Assigns 0 to num

如果模数运算符的任一操作数是浮点数,则应用以下规则来计算结果。

规则 1

即使右边的操作数是浮点零,该操作也不会导致错误。

规则二

如果任一操作数为NaN,则结果为NaN,例如:

float f1;
double d1;
f1 = Float.NaN % 10.5F;     // Assigns Float.NaN to f1
f1 = 20.0F % Float.NaN;     // Assigns Float.NaN to f1
f1 = Float.NaN % Float.NaN; // Assigns Float.NaN to f1
// A compile-time error. The expression is of the type double.
// double to float assignment is not allowed.
f1 = Float.NaN % Double.NaN;
d1 = Float.NaN % Double.NaN; // Assigns Double.NaN to d1

规则三

如果右边的操作数为零,则结果为NaN,例如:

float f1;
f1 = 15.0F % 0.0F; // Assigns Float.NaN to f1

规则四

如果左边的操作数是无穷大,结果是NaN,例如:

float f1;
f1 = Float.POSITIVE_INFINITY % 2.1F; // Assigns Float.NaN to f1

规则五

如果前面的规则都不适用,模数运算符将返回左操作数和右操作数相除的余数。结果的符号与左侧操作数的符号相同,例如:

float f1;
double d1;
f1 = 15.5F % 6.5F;                     // Assigns 2.5F to f1
d1 = 5.5 % 15.65;                      // Assigns 5.5 to d1
d1 = 0.0 % 3.78;                       // Assigns 0.0 to d1
d1 = 85.0 % Double.POSITIVE_INFINITY;  // Assigns 85.0 to d1
d1 = -85.0 % Double.POSITIVE_INFINITY; // Assigns -85.0 to d1
d1 = 85.0 % Double.NEGATIVE_INFINITY;  // Assigns 85.0 to d1
d1 = -85.0 % Double.NEGATIVE_INFINITY; // Assigns -85.0 to d1

一元加号运算符(+)

一元加号运算符(+)是一个算术运算符,其使用形式如下:

+operand

operand必须是原始数字类型。如果operand是一个byteshortchar,一元加运算符将其提升为一个int。否则,使用该运算符没有任何效果。例如,如果有一个值为5int变量num,那么+num仍然具有相同的值5。以下示例说明了该运算符的用法:

byte b1 = 10;
byte b2 = 5;
b1 = b2;  // OK. byte to byte assignment
// A compile-time error. b2 is of the type byte. But, use of the unary plus operator on
// b2 promoted its type to int. Therefore, +b2 is of the type int.
// int (+b2) to byte (b1) assignment is not allowed.
b1 = +b2;
b1 = (byte) +b2; // OK

一元减运算符(-)

一元减号运算符(-)是一种算术运算符,其使用形式如下:

-operand

一元减运算符对其操作数的值进行算术求反。operand必须是原始数字类型。如果operand是一个byteshortchar,则它被提升为一个int。下面的例子说明了它的用法:

byte b1 = 10;
byte b2 = 5;
b1 = b2;  // OK. byte to byte assignment
// A compile-time error. b2 is of the type byte. But, use of unary minus operator (-) on
// b2 promoted its type to int. Therefore, -b2 is of type int.
// int (-b2) to byte (b1) assignment is not allowed.
b1 = -b2;
b1 = (byte) -b2; // OK

复合算术赋值运算符

五个基本算术运算符(+-*/%)中的每一个都有相应的复合算术赋值运算符。用一个例子可以更好地解释这些操作符。假设你有两个变量,num1num2:

int num1 = 100;
byte num2 = 15;

如果您想将num1的值加到num2上,您应该编写如下代码:

num2 = (byte)(num2 + num1);

您需要将num2 + num1的结果转换为byte,因为表达式的数据类型是int。使用复合算术运算符(+=)可以重写相同的效果,如下所示:

num2 += num1; // Adds the value of num1 to num2

复合算术赋值运算符以下列形式使用:

operand1 op= operand2

这里,op是算术运算符+-*/中的一种,%. operand1operand2是原始的数值数据类型,其中operand1必须是变量。前面的表达式等效于下面的表达式:

operand1 = (Type of operand1) (operand1 op operand2)

例如,

int i = 100;
i += 5.5;     // Assigns 105 to i

相当于

i = (int)(i + 5.5); // Assigns 105 to i

使用复合算术赋值运算符有两个优点:

  • operand1只被评估一次。例如,在i += 5.5中,变量i只被评估一次,而在i = (int) (i + 5.5)中,变量i被评估两次。

  • 结果在赋值前自动转换为operand1的类型。强制转换可能会导致缩小转换或标识转换。在前面的示例中,强制转换是收缩转换。表达式i + 5.5的类型为double,该表达式的结果被转换为int。所以结果double 105.5被转换成int 105。如果你写一个类似于i += 5的表达式,等价的表达式将是i = (int)(i + 5)。因为表达式i + 5的类型已经是int,所以将结果再次转换为int是一个身份转换。

复合赋值运算符+=也可以应用于String变量。在这种情况下,operand1必须是String类型,operand2可以是包括boolean在内的任何类型。例如

String str1 = "Hello";
str1 = str1 + 100;    // Assigns "Hello100" to str1

可以改写为

str1 += 100; // Assigns "Hello100" to str1

Tip

只有+=运算符可以与String左操作数一起使用。

以下是使用复合赋值运算符的示例。在示例中,复合赋值运算符的每次使用都独立于其先前使用的效果。在所有情况下,都假定变量的值保持不变,即在声明变量时赋予它们的值:

int i = 110;
float f = 120.2F;
byte b = 5;
String str = "Hello";
boolean b1 = true;
i += 10;  // Assigns 120 to i
          // A compile-time error. boolean type cannot be used with +=
          // unless left-hand operand (here i) is a String variable
i += b1;
i -= 15;  // Assigns 95 to i. Assuming i was 110
i *= 2;   // Assigns 220 to i. Assuming i was 110
i /= 2;   // Assigns 55 to i. Assuming i was 110
i /= 0;   // Run-time error. Divide by zero error
f /= 0.0; // Assigns Float.POSITIVE_INFINITY to f
i %= 3;   // Assigns 2 to i. Assuming i is 110
str += " How are you"; // Assigns "Hello How are you" to str
str += f;  // Assigns "Hello120.2" to str. Assuming  str was "Hello"
b += f;    // Assigns 125 to b. Assuming b was 5, f was 120.2
str += b1; // Assigns "Hellotrue" to str. Assuming str was "Hello"

递增(++)和递减(-)运算符

增量运算符(++)用于数值数据类型的变量,以1递增变量值,而减量运算符(--用于以1递减变量值。在这一节中,我们只讨论增量运算符。同样的讨论也适用于递减运算符,唯一的区别是它将值递减1而不是递增1。假设有一个int变量i声明如下:

int i = 100;

要将i的值增加1,您可以使用以下三个表达式之一:

i = i + 1; // Assigns 101 to i
i += 1;    // Assigns 101 to i
i++;       // Assigns 101 to i

增量运算符++也可用于更复杂的表达式,如

int i = 100;
int j = 50;
j = i++ + 15;  // Assigns 115 to j and i becomes 101

表达式i++ + 15计算如下:

  • i的值进行求值,右边的表达式变成100 + 15

  • 存储器中i的值增加1。所以在这个阶段,变量i在内存中的值是101

  • 表达式100 + 15被评估,并且结果 115 被分配给j

有两种增量运算符:

  • 后缀增量运算符,例如i++

  • 前缀递增运算符,例如++i

++出现在其操作数之后时,称为后缀增量运算符。当++出现在其操作数之前时,称为前缀递增运算符。后缀和前缀增量运算符之间的唯一区别是它们使用操作数当前值和操作数值增量的顺序。后缀增量首先使用操作数的当前值,然后增加操作数的值,正如您在表达式j = i++ + 15中看到的。因为i++使用了一个后缀增量运算符,所以首先用i的当前值来计算表达式i++ + 15(例如100 + 15))的值。分配给j的值是115。然后,i的值增加了1.。如果使用前缀增量运算符重写该表达式,结果将会不同:

int i = 100;
int j = 50;
j = ++i + 15;   // Assigns 116 to j and i becomes 101

在这种情况下,表达式++i + 15的计算如下:

  • 因为++i使用了前缀递增运算符,首先,i的值在内存中由1递增。因此,i的值就是101

  • 表达式中使用了i的当前值,即101;这个表情变成了101 + 15

  • 表达式101 + 15被求值,结果116被赋值给j

注意,在对两个表达式i++ + 15++i + 15求值后,i的值是相同的,为101。然而,分配给j的值不同。如果你在一个简单的表达式中使用增量操作符++,如i++++i,你看不出使用后缀或前缀操作符有什么不同。

对于 Java 初学者来说,有一个难题。该难题包括增量运算符的使用,如下所示:

int i = 15;
i = i++; // What will be the value of i after this assignment?

猜猜i = i++执行后i的值会是多少?如果你的猜测是 16,那你就错了。以下是如何计算表达式的解释:

  • i++被评估。因为i++使用了后缀增量运算符,所以在表达式中使用了i的当前值。i的当前值为 15。这个表情变成了i = 15

  • 作为i++的第二个效果,i的值在内存中以1递增。此时i的值在内存中是 16。

  • 对表达式i = 15求值,并将值15赋给i。内存中变量i的值是 15,那就是最终值。事实上,变量i在前一步中观察到一个值 16,但是这一步用 15 覆盖了那个值。所以i = i++执行后变量i的最终值会是15,而不是 16。

在这个例子中,操作的顺序很重要。值得注意的是,在i++的情况下,一旦在表达式中使用了i的当前值,变量i的值就会增加。为了使这一点更清楚,考虑下面的例子:

int i = 10;
i = i++ + i;   // Assigns 21 to i
i = 10;
i = ++i + i++; // Assigns 22 to i

还有后缀和前缀递减运算符,例如i--, --i。以下是使用后缀和前缀减量运算符的几个示例:

int i = 15;
int j = 16;
i--;
--i;
i = 10;
i = i--;       // Assigns 10 to i
i = 10;
j = i-- + 10;  // Assigns 20 to j and 9 to i
i = 10;
j = --i + 10;  // Assigns 19 to j and 9 to i

关于递增和递减运算符的使用,有两点需要记住:

  • 递增和递减运算符的操作数必须是变量。例如,表达式5++是不正确的,因为++与常量一起使用。

  • 使用++--运算符的表达式的结果是一个值,而不是一个变量。例如,i++评估为一个值,所以我们不能使用i++作为赋值操作符的左边或者需要变量的地方。

字符串串联运算符(+)

+运算符在 Java 中被重载。如果一个操作符被用来执行一个以上的函数,就说它是重载的。到目前为止,您已经看到了它作为算术加法运算符将两个数相加的用法。它也可以用来连接两个字符串。两个字符串,例如,"abc""xyz",可以使用+操作符作为"abc" + "xyz"连接起来,产生一个新的字符串"abcxyz"。字符串串联的另一个示例如下:

String str1 = "Hello";
String str2 = " Alekhya";
String str3 = str1 + str2; // Assigns "Hello Alekhya" to str3

字符串串联运算符也可用于将一个基元和一个引用数据类型值串联成一个字符串。在这一节中,我们只讨论字符串和原始数据类型的连接。当+操作符的任一操作数是字符串时,它执行字符串连接。当+的两个操作数都是数字时,它执行数字加法。考虑以下代码片段:

int num = 26;
String str1 = "Alphabets";
String str2 = num + str1; // Assigns "26Alphabets" to str2

当执行表达式num + str1时,+操作符充当字符串连接操作符,因为它右边的操作数str1是一个String。在numstr1串接之前,num被它的字符串表示代替,也就是"26"。现在,表达式变成了"26" + str1,产生了"26Alphabets"。表 5-2 列出了原始数据类型值的字符串表示。

表 5-2

原始数据类型值的字符串表示形式

|

数据类型

|

价值

|

字符串表示

| | --- | --- | --- | | int, short, byte, long | 1678 | "1678" | | 0 | "0" | | char | 'A' | "A" | | '\u0041'``(Unicode escape sequence) | "A" | | boolean | True | "true" | | False | "false" | | float | 2.5 | "2.5" | | 0.0F | "0.0" | | -0.0F | "-0.0" | | Float.POSITIVE_INFINITY | "Infinity" | | Float.NEGATIVE_INFINITY | "-Infinity" | | Float.NaN | "NaN" | | double | 89.12 | "89.12" | | 0.0 | "0.0" | | -0.0 | "-0.0" | | Double.POSITIVE_INFINITY | "Infinity" | | Double.NEGATIVE_INFINITY | "-Infinity" | | Double.NaN | "NaN" |

如果一个String变量包含了null引用,那么连接操作符使用一个字符串"null"。以下示例说明了在字符串串联中使用基本数据类型值的字符串表示形式:

boolean b1 = true;
boolean b2 = false;
int num = 365;
double d = -0.0;
char c = 'A';
String str1;
String str2 = null;
str1 = b1 + " friends";    // Assigns "true friends" to str1
str1 = b2 + " identity";   // Assigns "false identity" to str1
// Assigns "null and void" to str1\. Because str2 is null, it is replaced
// by a string "null" by the string concatenation operator
str1 = str2 + " and void";
str1 = num + " days";      // Assigns "365 days" to str1
str1 = d + " zero";        // Assigns "-0.0 zero" to str1
str1 = Double.NaN + " is absurd"; // Assigns "NaN is absurd" to str1
str1 = c + " is a letter"; // Assigns "A is a letter" to str1
str1 = "This is " + b1;    // Assigns "This is true" to str1
// Assigns "Beyond Infinity" to str1
str1 = "Beyond " + Float.POSITIVE_INFINITY

确定使用多个+操作符和字符串的表达式的结果有时可能会令人困惑。下面这个表达式的结果会是什么?

 12 + 15 + " men"

结果会是"1215 men还是27 men"?找到正确答案的关键是找到哪个+是算术运算符,哪个+是字符串串联运算符。

如果两个操作数都是数字,+运算符执行加法。如果任一操作数是字符串,+操作符执行字符串连接。除非使用括号覆盖,否则表达式的执行从左到右进行。在表达式12 + 15 + " men"中,左起第一个+1215进行加法运算,得到27。之后,表达式减少到27 + " men"。现在,+操作符执行一个字符串连接,因为右边的操作数" men"是一个字符串,它产生了"27 men"。考虑下面这段代码:

int num1 = 12;
int num2 = 15;
String str1 = " men";
String str2;

我们想使用三个变量num1num2str1以及+操作符创建一个字符串"1215 men"。我们想把结果赋给str2。这是第一次尝试:

str2 = num1 + num2 + str1;

这条语句将把"27 men"赋值给str2。另一个解决方案是将num2 + str1放在括号中:

str2 = num1 + (num2 + str1); // Assigns "1215 men" to str2

首先计算括号中的表达式。首先对表达式(num2 + str1)求值,将表达式简化为num1 + "15 men",然后表达式求值为"1215 men"。另一种方法是在表达式的开头放置一个空字符串:

str2 = "" + num1 + num2 + str1; // Assigns "1215 men" to str1

在这种情况下,首先对"" + num1求值,得到"12",从而将表达式简化为"12" + num2 + str1。现在,"12" + num2被求值,结果是"1215"。现在,表达式被简化为"1215" + " men",结果是一个字符串"1215 men"。您也可以在表达式中的num1num2之间放置一个空字符串,以获得相同的结果:

str2 = num1 + "" + num2 + str1; // Assigns "1215 men" to str2

有时候,字符串连接比你想象的要复杂。考虑下面这段代码:

boolean b = false;
int num = 15;
String str1 = "faces";
String str2 = b + num + str1; // A compile-time error

最后一条语句生成一个编译时错误。这种说法有什么问题?您期望将字符串“false15faces"分配给str2。不是吗?让我们来分析一下b + num + str1这个表达。左起第一个+运算符是算术运算符还是字符串串联运算符?对于一个要成为字符串连接操作符的+操作符来说,它必须至少有一个操作数是字符串。因为bnum都不是字符串,所以b + num + str1中左起的第一个+运算符不是字符串连接运算符。

是算术加法运算符吗?它的操作数类型为boolean ( b)和int ( num)。你已经知道算术加法运算符(+)不能有boolean操作数。表达式b + num中出现的boolean操作数导致了编译时错误。一个boolean不能加到一个数字上。然而,如果另一个操作数是一个字符串,那么+操作符作为一个字符串连接操作符在boolean上工作。若要更正此错误,可以按如下方式重写表达式:

str2 = b + (num + str1);     // OK. Assigns "false15faces" to str2
str2 = "" + b + num + str1;  // OK. Assigns "false15faces" to str2
str2 = b + "" + num + str1;  // OK. Assigns "false15faces" to str2

您使用println()print()方法在标准输出上打印一条消息,如下所示:

System.out.println("Prints a new line at the end of text");
System.out.print("Does not print a new line at the end of text");

如果您使用System.out.println()方法在控制台上打印文本,在打印完文本后,它还会在文本末尾打印一个新的行字符。使用println()print()的唯一区别是前者在文本末尾打印一个新行,而后者不打印。println()print()方法被重载。到目前为止,您只看到了它们在字符串参数中的使用。您可以将任何 Java 数据类型参数传递给这两个方法。以下代码片段说明了如何将 Java 基元类型作为参数传递给这些方法:

int num = 156;
// Prints 156 on the console
System.out.println(num);
// Prints, Value of num = 156, on the console
System.out.println("Value of num = " + num);
// Prints a new line character on the console
System.out.println();

清单 5-2 包含一个完整的程序来演示算术运算符和字符串连接运算符的使用。

// ArithOperator.java
package com.jdojo.operator;
class ArithOperator {
    public static void main(String[] args) {
        int num = 120;
        double realNum = 25.5F;
        double veryBigNum = 25.8 / 0.0;
        double garbage = 0.0 / 0.0;
        boolean test = true;
        // Print the value of num
        System.out.println("num = " + num);
        // Print the value of realNum
        System.out.println("realNum = " + realNum);
        // Print the value of veryBigNum
        System.out.println("veryBigNum = " + veryBigNum);
        // Print the value of garbage
        System.out.println("garbage = " + garbage);
        // Print the value of test
        System.out.println("test = " + test);
        // Print the maximum value of int type
        System.out.println("Maximum int = " + Integer.MAX_VALUE);
        // Print the maximum value of double type
        System.out.println("Maximum double = " + Double.MAX_VALUE);
        // Print the sum of two numbers
        System.out.println("12.5 + 100 = " + (12.5 + 100));
        // Print the difference of two numbers
        System.out.println("12.5 - 100 = " + (12.5 - 100));
        // Print the multiplication of two numbers
        System.out.println("12.5 * 100 = " + (12.5 * 100));
        // Print the result of division
        System.out.println("12.5 / 100 = " + (12.5 / 100));
        // Print the result of modulus
        System.out.println("12.5 % 100 = " + (12.5 % 100));
        // Print the result of string concatenation
        System.out.println("\"abc\" + \"xyz\" = " + "\"" + ("abc" + "xyz") + "\"");
    }
}

num = 120
realNum = 25.5
veryBigNum = Infinity
garbage = NaN
test = true
Maximum int = 2147483647
Maximum double = 1.7976931348623157E308
12.5 + 100 = 112.5
12.5 - 100 = -87.5
12.5 * 100 = 1250.0
12.5 / 100 = 0.125
12.5 % 100 = 12.5
"abc" + "xyz" = "abcxyz"

Listing 5-2An Example of Using Java Operators

关系运算符

关系运算符比较其操作数的值。这种比较的几个例子是相等、不相等、大于、小于等的比较。Java 支持七种关系运算符,其中六种在表 5-3 中列出。第七个是instanceof操作符,我们将在本书后面讨论。

表 5-3

Java 中的关系运算符列表

|

运算符

|

意义

|

类型

|

使用

|

结果

| | --- | --- | --- | --- | --- | | == | 等于 | 二进制的 | 3 == 2 | false | | != | 不等于 | 二进制的 | 3 != 2 | true | | > | 大于 | 二进制的 | 3 > 2 | true | | >= | 大于或等于 | 二进制的 | 3 >= 2 | true | | < | 不到 | 二进制的 | 3 < 2 | false | | <= | 小于或等于 | 二进制的 | 3 <= 2 | false |

所有关系运算符都是二元运算符。也就是说,它们有两个操作数。关系运算符产生的结果总是一个boolean值:truefalse

相等运算符(==)

等式运算符(==)用于以下形式:

operand1 == operand2

相等运算符用于测试两个操作数是否相等。它使用以下规则:

  • 两个操作数都必须是基元类型或引用类型。不允许混合操作数类型。

  • 对于原语操作数,如果两个操作数表示相同的值,则返回true;否则返回false。操作数必须都是数字或者都是boolean。不允许混合使用数字和boolean类型。

  • 对于引用操作数,如果两个操作数都引用内存中的同一个对象,则返回true;否则返回false

假设有一个名为iint变量,声明如下:

int i = 10;

现在,i == 10将测试i是否等于 10。因为i等于10,所以表达式i == 10将计算为true。考虑另一个例子:

int i;
int j;
int k;
boolean b;
i = j = k = 15;    // Assign 15 to i, j and k
b = (i == j == k); // A compile-time error

在这个例子中,您试图测试三个变量ijk是否具有相同的值,而表达式(i == j == k)导致了一个错误。为什么会出现错误?表达式(i == j == k)的计算如下:

  • 首先,在表达式i == j == k中对i == j求值。因为ij的值相同,都是 15,所以表达式i == j返回true

  • 第一步将表达式i == j == k简化为true == k。这是一个错误,因为==操作符的操作数属于booleanint类型。不能用等号运算符混合boolean和数字类型的操作数。

当相等运算符的操作数是浮点类型时,以下规则适用。

规则 1

负零(–0.0)和正零(0.0)被认为是相等的。回想一下–0.0 和 0.0 在内存中的存储方式是不同的:

double d1 = 0.0;
double d2 = -0.0;
boolean b = (d1 == d2); // Assigns true to b

规则二

正负不定式被认为是不相等的:

double d1 = Double.POSITIVE_INFINITY;
double d2 = Double.NEGATIVE_INFINITY;
boolean b = (d1 == d2); // Assigns false to b

规则 3

如果任一操作数为NaN,相等测试返回false:

double d1 = Double.NaN;
double d2 = 5.5;
boolean b = (d1 == d2); // Assigns false to b

注意,即使两个操作数都是NaN,相等运算符也会返回false:

d1 = Double.NaN;
d2 = Double.NaN;
b = (d1 == d2); // Assigns false to b

如何测试存储在floatdouble变量中的值是否是NaN?如果你写下面这段代码来测试一个double变量d1的值是否为NaN,它将总是返回false:

double d1 = Double.NaN;
boolean b = (d1 == Double.NaN); // Assigns false to b. Incorrect way of testing

FloatDouble类有一个isNaN()方法,分别接受一个float和一个double参数。如果参数为NaN,则返回true;否则返回false。例如,为了测试d1是否为NaN,前面的表达式可以重写如下:

double d1 = Double.NaN;
// Assigns true to b. Correct way to test for a NaN value
b = Double.isNaN(d1);

您不应该使用==操作符来测试两个字符串是否相等,例如:

String str1 = new String("Hello");
String str2 = new String("Hello");
boolean b;
b = (str1 == str2); // Assigns false to b

Java 中的new操作符总是在内存中创建一个新对象。所以,str1str2指的是内存中两个不同的对象,这也是str1 == str2求值为false的原因。诚然,内存中的两个String对象具有相同的内容。每当==操作符与引用变量一起使用时,它总是比较其操作数所引用的对象的引用。要比较由两个String变量str1str2表示的文本,应该使用String类的equals()方法,如下所示:

// Assigns true to b because str1 and str2 have the same text "Hello"
b = str1.equals(str2);
// Assigns true to b because str1 and str2 have the same text "Hello"
b = str2.equals(str1);

我们将在第十五章讨论更多关于字符串比较的内容,包括字符串。

不等式运算符(!=)

不等式运算符(!=)以如下形式使用:

operand1 != operand2

如果operand1operand2不相等,不等式运算符返回true。否则返回false。不等式运算符(!=)的操作数的数据类型规则与等式运算符(==)相同。以下是使用不等式运算符的几个示例:

int i = 15;
int j = 10;
int k = 15;
boolean b;
b = (i != j);        // Assigns true to b
b = (i != k);        // Assigns false to b
b = (true != true);  // Assigns false to b
b = (true != false); // Assigns true to b

如果任一操作数为NaN ( floatdouble),则不等式运算符返回true。如果d1是浮点变量(doublefloat),当且仅当d1NaN时,d1 == d1返回false,而d1 != d1返回true

大于运算符(>)

“大于”运算符(>)以下列形式使用:

operand1 > operand2

如果operand1的值大于operand2的值,则“大于”运算符返回true。否则返回false。大于运算符只能用于原始数字数据类型。如果任一操作数为NaN ( floatdouble),则返回false。以下是使用此运算符的几个示例:

int i = 10;
int j = 15;
double d1 = Double.NaN;
boolean b;
b = (i > j); // Assigns false to b
b = (j > i); // Assigns true to b
// A compile-time error. > cannot be used with boolean operands
b = (true > false);
b = (d1 > Double.NaN); // Assigns false to b
String str1 = "Hello";
String str2 = "How is Java?";
// A compile-time error. > cannot be used with reference type operands str1 and str2
b = (str1 > str2);

如果要测试String str1中的字符数是否大于str2中的字符数,应该使用String类的length()方法。String类的length()方法返回字符串中的字符数,例如:

i = str1.length(); // Assigns 5 to i. "Hello" has 5 characters
b = (str1.length() > str2.length()); // Assigns false to b
b = (str2.length() > str1.length()); // Assigns true to b

大于或等于运算符(> =)

“大于或等于”运算符(>=)用于以下形式:

operand1 >= operand2

如果operand1的值大于或等于operand2的值,则“大于或等于”运算符返回true。否则返回false。大于或等于运算符只能用于原始数字数据类型。如果任一操作数是NaN ( floatdouble),大于或等于运算符返回false。以下是使用此运算符的几个示例:

int i = 10;
int j = 10;
boolean b;
b = (i >= j);  // Assigns true to b
b = (j >= i);  // Assigns true to b

小于运算符(

“小于运算符”(<)用于以下形式:

operand1 < operand2

如果operand1小于operand2,则“小于运算符”返回true。否则返回false。运算符只能用于原始数值数据类型。如果任一操作数是NaN ( floatdouble),小于运算符返回false。以下是使用此运算符的几个示例:

int i = 10;
int j = 15;
double d1 = Double.NaN;
boolean b;
b = (i < j);           // Assigns true to b
b = (j < i);           // Assigns false to b
// A compile-time error. < cannot be used with boolean operands
b = (true < false);
b = (d1 < Double.NaN); // Assigns false to b

小于或等于运算符(< =)

“小于或等于运算符”(<=)在此表格中使用:

operand1 <= operand2

如果operand1的值小于或等于operand2的值,则“小于或等于运算符”返回true。否则返回false。运算符只能用于原始数值数据类型。如果任一操作数是NaN ( floatdouble),小于或等于运算符返回false。以下是使用此运算符的几个示例:

int i = 10;
int j = 10;
int k = 15;
boolean b;
b = (i <= j);  // Assigns true to b
b = (j <= i);  // Assigns true to b
b = (j <= k);  // Assigns true to b
b = (k <= j);  // Assigns false to b

布尔逻辑运算符

布尔逻辑运算符获取boolean操作数,对其应用布尔逻辑,并产生一个boolean值。表 5-4 列出了 Java 中可用的boolean逻辑运算符。所有的boolean逻辑运算符只能与boolean操作数一起使用。后续部分将详细解释这些运算符的用法。

表 5-4

布尔逻辑运算符列表

|

运算符

|

意义

|

类型

|

使用

|

结果

| | --- | --- | --- | --- | --- | | ! | 逻辑非 | 一元的 | !true | false | | && | 短路和 | 二进制的 | true && true | true | | & | 逻辑“与” | 二进制的 | true & true | true | | &#124;&#124; | 短路或 | 二进制的 | true &#124;&#124; false | true | | &#124; | 逻辑或 | 二进制的 | true &#124; false | true | | ^ | 逻辑异或(异或) | 二进制的 | true ^ true | false | | &= | 和分配 | 二进制的 | test &= true |   | | &#124;= | 或分配 | 二进制的 | test &#124;= true |   | | ^= | 异或赋值 | 二进制的 | test ^= true |   |

逻辑非运算符(!)

逻辑NOT运算符(!)用于以下形式:

!operand

如果operandfalse,操作员返回true,如果operandtrue,操作员返回false:

boolean b;
b = !true;    // Assigns false to b
b = !false;   // Assigns true to b
int i = 10;
int j = 15;
boolean b1 = true;
b = !b1;      // Assigns false to b
b = !(i > j); // Assigns true to b, because i > j returns false

假设您想要将一个boolean变量b的值更改为true,如果其当前值为false,如果其当前值为true,则更改为false。这可以通过以下方式实现:

b = !b; // Assigns true to b if it was false and false if it was true

逻辑短路和运算符(&&)

逻辑短路AND运算符(&&)用于以下形式:

operand1 && operand2

如果两个操作数都是true,则&&运算符返回true。如果任一操作数为false,则返回false。它被称为短路操作符AND,因为如果operand1(左边的操作数)计算结果为false,它将返回false而不计算operand2(右边的操作数):

int i = 10;
int j = 15;
boolean b = (i > 5 && j > 10);  // Assigns true to b

在这个表达式中,首先计算i > 5,然后返回true。因为左边的操作数被求值为true,所以右边的操作数也被求值。右边的操作数j > 10被求值,它也返回true。现在,表情简化为true && true。因为两个操作数都是true,所以最后的结果是true。考虑另一个例子:

int i = 10;
int j = 15;
boolean b = (i > 20 && j > 10);  // Assigns false to b

表达式i > 20返回false。这个表达简化为false && j > 10。因为左边的操作数是false,右边的操作数j > 10不求值,&&返回false。然而,在这个例子中没有办法证明右边的操作数j > 10没有被求值。考虑另一个例子来证明这一点。我们已经讨论过赋值操作符(=)。如果numint类型的变量,num = 10返回值10:

int num = 10;
boolean b = ((num = 50) > 5); // Assigns true to b

注意这个例子中圆括号的使用。在表达式((num = 50) > 5)中,首先计算(num = 50)表达式。它将50赋给num并返回50,将表达式简化为(50 > 5),?? 又返回true。如果在执行num = 50表达式后使用num的值,其值将为50。记住这一点,考虑下面的代码片段:

int i = 10;
int j = 10;
boolean b = (i > 5 && ((j = 20) > 15));
System.out.println("b = " + b);
System.out.println("i = " + i);
System.out.println("j = " + j);
b = true
i = 10
j = 20

由于左边的操作数i > 5被赋值为true,右边的操作数((j = 20) > 15)被赋值,变量j被赋值为 20。如果我们改变这个代码,那么左边的操作数计算为false,右边的操作数将不被计算,并且j的值将保持为10。更改后的代码如下:

int i = 10;
int j = 10;
// ((j = 20) > 5) is not evaluated because i > 25 returns false
boolean b = (i > 25 && ((j = 20) > 15));
System.out.println ("b = " + b);
System.out.println ("i = " + i);
System.out.println ("j = " + j); // Will print j = 10
b = false
i = 10
j = 10

逻辑与运算符(&)

逻辑AND运算符(&)用于以下形式:

operand1 & operand2

如果两个操作数都是true,则逻辑AND运算符返回true。如果任一操作数为false,则返回false。逻辑AND操作符(&)的工作方式与逻辑短路AND操作符(&&)相同,除了一点不同——逻辑AND操作符(&)计算其右边的操作数,即使其左边的操作数计算结果为false:

int i = 10;
int j = 15;
boolean b;
b = (i > 5 & j > 10);            // Assigns true to b
b = (i > 25 & ((j = 20) > 15));  // ((j = 20) > 5) is evaluated even if i > 25 returns false
System.out.println ("b = " + b);
System.out.println ("i = " + i);
System.out.println ("j = " + j); // Will print j = 20
b = false
i = 10
j = 20

逻辑短路或运算符(||)

逻辑短路OR运算符(||)用于以下形式:

operand1 || operand2

如果任一操作数为true,逻辑短路OR运算符返回true。如果两个操作数都是false,则返回false。它被称为短路OR操作符,因为如果operand1计算结果为true,它将返回true而不计算operand2:

int i = 10;
int j = 15;
boolean b = (i > 5 || j > 10); // Assigns true to b

在这个表达式中,首先计算i > 5,然后返回true。因为左边的操作数求值为true,右边的操作数不求值,表达式(i > 5 || j > 10)返回true。考虑另一个例子:

int i = 10;
int j = 15;
boolean b = (i > 20 || j > 10); // Assigns true to b

表达式i > 20返回false。这个表达简化为false || j > 10。因为||的左边操作数是false,右边操作数j > 10被求值,返回true,整个表达式返回true

逻辑或运算符(|)

逻辑OR运算符(|)用于以下形式:

operand1 | operand2

如果任一操作数为true,逻辑OR运算符返回true。如果两个操作数都是false,则返回false。逻辑OR操作符的工作方式与逻辑短路OR操作符相同,除了一点不同——逻辑OR操作符计算其右边的操作数,即使其左边的操作数计算结果为true:

int i = 10;
int j = 15;
boolean b = (i > 5 | j > 10); // Assigns true to b

在这个表达式中,首先计算i > 5,然后返回true。即使左边的操作数i > 5求值为true,右边的操作数j > 15仍然求值,整个表达式(i > 5 | j > 10)返回true

逻辑异或运算符(^)

逻辑XOR运算符(^)用于以下形式:

operand1 ^ operand2

如果operand1operand2不同,逻辑XOR运算符返回true。也就是说,如果其中一个操作数是true,它返回true,但不是两个都是。如果两个操作数相同,则返回false:

int i = 10;
boolean b;
b = true ^ true;      // Assigns false to b
b = true ^ false;     // Assigns true to b
b = false ^ true;     // Assigns true to b
b = false ^ false;    // Assigns false to b
b = (i > 5 ^ i < 15); // Assigns false to b

复合布尔逻辑赋值运算符

有三个复合boolean逻辑赋值操作符。注意 Java 没有任何像&&=||=这样的操作符。这些运算符以如下形式使用:

operand1 op= operand2

operand1必须是一个boolean变量,op可以是&|^。这种形式相当于写作

operand1 = operand1 op operand2

表 5-5 显示了复合逻辑赋值运算符及其等价物。

表 5-5

复合逻辑赋值运算符及其等价形式

|

表示

|

相当于

| | --- | --- | | operand1 &= operand2 | operand1 = operand1 & operand2 | | operand1 &#124;= operand2 | operand1 = operand1 &#124; operand2 | | operand1 ^= operand2 | operand1 = operand1 ^ operand2 |

如果两个操作数的值都是true&=返回true。否则,返回false:

boolean b = true;
b &= true;  // Assigns true to b
b &= false; // Assigns false to b

如果任一操作数的计算结果为true,则|=返回true。否则,返回false:

boolean b = false;
b |= true;  // Assigns true to b
b |= false; // Assigns false to b

如果两个操作数的值不同,也就是说,其中一个操作数是true而不是两个都是,^=返回true。否则,返回false:

boolean b = true;
b ^= true;  // Assigns false to b
b ^= false; // Assigns true to b

三元运算符(?:)

Java 有一个条件运算符。它被称为三元运算符。它需要三个操作数。它以这种形式使用:

boolean-expression ? true-expression : false-expression

两个符号?:组成三元运算符。如果boolean-expression求值为true,则求值为true-expression;否则,它评估false-expression

假设你有三个整数变量num1num2minNum。您希望将num1num2中的最小值分配给minNum。您可以使用三元运算符来实现这一点:

int num1 = 50;
int num2 = 25;
// Assigns num2 to minNum, because num2 is less than num1
int minNum = (num1 < num2 ? num1 : num2);

按位运算符

按位运算符是一种使用整数操作数的位模式对其执行操作的运算符。表 5-6 中列出了 Java 中的位运算符。

表 5-6

按位运算符列表

|

运算符

|

意义

|

类型

|

使用

|

结果

| | --- | --- | --- | --- | --- | | & | 按位“与” | 二进制的 | 25 & 24 | 24 | | &#124; | 按位或 | 二进制的 | 25 &#124; 2 | 27 | | ^ | 按位异或 | 二进制的 | 25 ^ 2 | 27 | | ~ | 按位非(1 的补码) | 一元的 | ~25 | -26 | | << | 左移 | 二进制的 | 25 << 2 | 100 | | >> | 有符号右移位 | 二进制的 | 25 >> 2 | 6 | | >>> | 无符号右移 | 二进制的 | 25 >>> 2 | 6 | | &=, !=, ^=, <<=, >>=, >>>= | 复合赋值位运算符 | 二进制的 |   |   |

所有按位运算符都只处理整数。按位AND ( &)运算符对其两个操作数的相应位进行运算,如果两位都为 1,则返回1,否则返回0。注意,按位AND ( &)对各个操作数的每一位进行运算,而不是对整个操作数进行运算。以下是使用按位AND ( &)运算符的所有位组合的结果:

1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0

考虑下面这段 Java 代码:

int i = 13 & 3;

13 & 3的值计算如下。为了清楚起见,32 位以 8 位块的形式示出。在内存中,所有 32 位都是连续的:

13         00000000 00000000 00000000 00001101
 3         00000000 00000000 00000000 00000011
----------------------------------------------
13 & 3 -   00000000 00000000 00000000 00000001 (Equal to decimal 1)

因此,13 & 3为 1,赋给前面一段代码中的i

按位OR ( |)对其操作数的相应位进行运算,如果任一位为 1,则返回1,否则返回0。以下是使用按位OR ( |)运算符的所有位组合的结果:

1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

13 | 3的值可以计算如下。13 | 3的结果是15:

13       00000000 00000000 00000000 00001101
 3       00000000 00000000 00000000 00000011
--------------------------------------------
13 | 3   00000000 00000000 00000000 00001111 (Equal to decimal 15)

按位XOR ( ^)对其操作数的相应位进行运算,如果只有一位为 1,则返回1。否则返回0。以下是使用按位XOR ( ^)运算符的所有位组合的结果:

1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0

13 ^ 3的值可以计算如下。13 ^ 3的结果是14:

13      00000000 00000000 00000000 00001101
 3      00000000 00000000 00000000 00000011
-------------------------------------------
13 ^ 3  00000000 00000000 00000000 00001110 (Equal to decimal 14)

按位NOT ( ~)对其操作数的每一位进行运算。它将位反转,即 1 变为 0,0 变为 1。它也被称为按位 补码运算符。它计算操作数的 1 的补码。以下是使用按位NOT ( ~)运算符的所有位组合的结果:

~1 = 0
~0 = 1

~13的值可以计算如下。~13的结果是-14:

13     00000000 00000000 00000000 00001101
------------------------------------------
~13    11111111 11111111 11111111 11110010  (Equal to decimal -14)

按位左移运算符(<<)将所有位向左移动指定为其右侧操作数的位数。它在低位插入零。向右移动 1 位的效果等同于将数字乘以 2。因此,9 << 1会产生18,而9 << 2会产生36。计算13 << 4的程序如图 5-1 所示。

img/323069_3_En_5_Fig1_HTML.png

图 5-1

计算 13 << 4

13 << 35的结果是什么?你可能已经猜到了零。然而,事实并非如此。实际上只用 32 位来表示13,因为13被认为是int的字面,而int占据了 32 位。在int中,您只能将所有位向左移动31位。如果按位左移运算符(< int)的左操作数,则只有右操作数的 5 个低位值被用作要移位的位数。例如,在13 << 35中,右操作数(35)可以用二进制表示如下:

00000000000000000000000000100011

35 中的 5 个低位是 00011,等于 3。所以你写13 << 35就相当于写13 << 3。对于按位左移运算符的所有正右操作数,您可以用 32 取右操作数的模,这将是要移位的最终位数。因此,13 << 35可以认为是13 << (35 % 32),与13 << 3相同。如果左边的操作数是long,右边操作数的前 6 个低位的值被用作要移位的位数:

long  val = 13;
long result;
result = val << 35;

由于val是一个long35的 6 个低位比特,即 100011,将被用作要移位的数字。图 5-2 显示了用于计算13 >> 4-13 >> 4的步骤。

img/323069_3_En_5_Fig2_HTML.png

图 5-2

计算 13 >> 4 和-13 >> 4

按位有符号右移位运算符(>>)将所有位向右移位指定为其右侧操作数的数字。如果左边操作数的最高有效位是 1(对于负数),则在移位操作后,所有高位都填充 1。如果最高有效位是 0(对于正数),所有高位都用 0 填充。因为右移位运算(>>)后的符号位保持不变,所以称为有符号右移位运算符。例如,13 >> 4的结果为零,如图 5-2 所示。还要注意的是,在-13 >> 4的情况下,所有 4 个高位都是 1,因为在-13中,最高有效位是 1。-13 >> 4的结果是-1

无符号右移位运算符(>>>)的工作方式与有符号右移位运算符(>>)相同,除了一点不同——它总是用零填充高位。如图所示,13 >>> 4的结果是零,而-13 >>> 4的结果是268435455。没有无符号左移运算符:

13         00000000 00000000 00000000  00001101
13 >>> 4   00000000 00000000 00000000  00000000 1101
-13        11111111 11111111 11111111  11110011
-13 >>> 4  00001111 11111111 11111111  11111111 0011

复合按位赋值运算符以下列形式使用:

operand1 op= operand2

这里,op是位运算符&|^<<>>之一,>>>. operand1operand2是原始整数数据类型,其中operand1必须是变量。前面的表达式等效于下面的表达式:

operand1 = (Type of operand1) (operand1 op operand2)

假设有两个int变量,ij,表 5-7 列出了复合按位赋值运算符的等价表达式。

表 5-7

复合按位赋值运算符列表

|

表示

|

相当于

| | --- | --- | | i &= j | i = i & j | | i &#124;= j | i = i &#124; j | | i ^= j | i = i ^ j | | i <<= j | i = i << j | | i >>= j | i = i >> j | | i >>>= j | i = i >>> j |

运算符优先级

考虑下面这段代码:

int result;
// What will be the value assigned to result?
result = 10 + 8 / 2;

最后一条语句执行后,赋给变量result的值会是什么?会是9还是14?要看先做的操作。如果先执行加法10 + 8,则为9。如果先执行除法8/2,则为14。Java 中的所有表达式都是根据运算符优先级层次结构进行计算的,该层次结构建立了控制表达式计算顺序的规则。优先级较高的运算符在优先级较低的运算符之前进行计算。如果运算符具有相同的优先级,则表达式从左向右计算。乘法、除法和余数运算符的优先级高于加法和减法运算符。因此,在前面的表达式中,首先对8/2求值,这将表达式简化为10 + 4,进而产生14

考虑另一个表达式:

result = 10 * 5 / 2;

表达式10 * 5 / 2使用两个运算符:一个乘法运算符和一个除法运算符。两个运算符具有相同的优先级。表达式从左到右计算。首先对表达式10 * 5求值,然后对表达式50 / 2求值。整个表达式求值为25。如果要先执行除法,必须使用括号。括号具有最高优先级,因此,括号内的表达式首先被求值。您可以使用括号重写前面的代码:

result = 10 * (5 / 2); // Assigns 20 to result. Why?

也可以使用嵌套括号。在嵌套括号中,首先计算最内层括号的表达式。表 5-8 按优先级顺序列出了 Java 运算符。同一级别的运算符具有相同的优先级。表 5-8 列出了一些我们还没有讨论的操作符。我们将在后面的其他章节中讨论它们。在表中,“级别”列中的值越低,表示优先级越高。

表 5-8

Java 运算符及其优先级

|

水平

|

运算符符号

|

执行的操作

| | --- | --- | --- | | one | ++ | 前增量或后增量 | | -- | 前减量或后减量 | | +- | 一元加号,一元减号 | | ~ | 逐位补码 | | ! | 逻辑非 | | (type) | 演员阵容 | | Two | */% | 乘法,除法,剩余物 | | three | +- | 加法、减法 | | + | 串并置 | | four | << | 左移 | | >> | 有符号右移位 | | >>> | 无符号右移 | | five | < | 不到 | | <= | 小于或等于 | | > | 大于 | | >= | 大于或等于 | | instanceof | 类型比较 | | six | == | 价值相等 | | != | 不等于 | | seven | & | 按位 AND | | & | 逻辑与 | | eight | ^ | 按位异或 | | ^ | 逻辑异或 | | nine | &#124; | 按位或 | | &#124; | 逻辑或 | | Ten | && | 逻辑短路和 | | Eleven | &#124;&#124; | 逻辑短路或 | | Twelve | ? : | 三元算子 | | Thirteen | = | 分配 | | +=-=*=/=%=<<=>>=>>>=&=&#124;=^= | 复合赋值 |

摘要

运算符是一种符号,用于对其操作数执行某种类型的计算。Java 包含一组丰富的操作符。根据操作数的数量,运算符分为一元、二元和三元。根据操作数类型和它们对操作数执行的操作,它们被分类为算术、关系、逻辑或按位。

算术运算符是将数值作为其操作数并执行算术运算(例如,加法和减法)来计算另一个数值的运算符。Java 中算术运算符的几个例子是+-*/

在 Java 中,+运算符是重载的。当它的一个操作数是一个String时,它执行一个字符串连接。例如,一个表达式50 + " States"产生另一个字符串"50 States"

关系运算符比较其操作数的值,返回一个boolean值— truefalse。关系运算符的几个例子是==!=>>=<<=

布尔逻辑运算符获取boolean操作数,对其应用布尔逻辑,并产生一个boolean值。Java 中布尔逻辑运算符的几个例子是&&||&

Java 支持三元运算符(? :),它接受三个操作数。它也被称为条件运算符。如果第一个操作数计算结果为true,则计算第二个操作数并返回;否则,计算并返回第三个操作数。

按位运算符是一种使用整数操作数的位模式对其执行操作的运算符。Java 中按位运算符的两个例子是&|

如果可以在多个上下文中使用一个运算符来执行不同类型的计算,则该运算符称为重载。Java 包含一个重载的+操作符。它被用作算术加法运算符和字符串连接运算符。与 C++不同,Java 不允许开发人员重载运算符。

Java 中的每个操作符相对于其他操作符都有优先顺序。如果一个表达式中出现多个运算符,则优先级较高的运算符的操作数优先于优先级较低的运算符的操作数。

EXERCISES

  1. 什么是运营商?什么是一元、二元和三元运算符?举例说明 Java 中每种类型的运算符。

  2. 前缀、后缀和中缀运算符之间有什么区别?举一个 Java 中这种运算符的例子。

  3. 什么是算术运算符,它们接受什么类型的操作数,产生什么类型的结果?

  4. 举出两个 Java 中的运算符,它们只接受Boolean操作数并产生一个布尔值。

  5. 两个运算符有什么区别:===

  6. Consider the following snippet of code:

    boolean done;
    /* Some code goes here */
    your-code-goes-here;
    
    

    使用Boolean逻辑运算符,反转存储在done变量中的当前值。也就是说,写一条语句,如果变量done的当前值是false,则将true赋值给变量done,如果变量done的当前值是true,则将false赋值给变量done

  7. Consider the following snippet of code:

    int x = 23;
    int y = ++x % 3;
    
    

    这段代码执行后,y的值会是什么?

  8. Consider the following snippet of code:

    int x = 23;
    x = x++ % x;
    
    

    这段代码执行后,x的值会是什么?用执行的步骤解释你的答案,解释在第二条语句的执行过程中x的值是如何变化的。

  9. 解释为什么下面的代码片段不能编译:

    int x = 10;
    boolean yes = (x = 20);
    
    
  10. 当执行下面的代码片段时,赋给名为yes的变量的值是什么?

```java
int x = 10;
boolean yes = (x == 20);

```

11. 当执行下面的代码片段时,y的值会是什么?

```java
int x = 19;
int y = x > 10 ? 69 : 68;

```

12. 您有一个名为x的短变量,其声明和初始化如下:

short x = -19;

您希望使用以下语句将19赋值给x,这两条语句都不会编译:

x = -x;
x = -1 * x;

你将如何重写这两条语句来编译它们?下面的语句试图修复这些语句中的编译时错误,但是没有将19赋值给x,这是怎么回事?

  1. 当执行下面的代码片段时,输出会是什么?

    boolean b = true;
    String str = !b +" is not " + b;
    System.out.println(str);
    
    
  2. 当执行下面的代码片段时,输出会是什么?

    boolean b = true;
    String str = (b ^= b) + " is " + b;
    System.out.println(str);
    
    
  3. 当您执行下面的代码片段时,输出会是什么?

    int x = 10;
    int y = x++;
    int z = ++x;
    System.out.println("x = " + x + ", y = " + y + ", z = " + z);
    
    
  4. 使用三元运算符(? :)和按位 and 运算符(&)完成第二条语句,这将生成一条消息"x is odd"。您的代码必须以任意顺序包含以下标记:x&==?:"odd""even"。您可以根据需要使用额外的令牌:

    int x = 19;
    String msg = your-code-goes-here ;
    System.out.println("x is " + msg);
    
    
  5. 下列哪一个作业将无法编译,为什么?

    int i1 = 100;
    int i2 = 10.6;
    byte b1 = 90;
    byte b2 = 3L;
    short s1 = -90;
    float f1 = 12.67;
    float f2 = 0.00f;
    double d1 = 12.56;
    double d2 = 12.78d;
    boolean bn1 = true;
    boolean bn2 = 0;
    char c1 = 'A';
    char c2 = "A";
    char c3 = 0;
    char c4 = '\u0000';
    
    
  6. 写下分配给下列每个语句中已声明变量的值。如果一个语句产生了编译时错误,解释错误背后的原因,如果可能的话,提供一个修复错误的解决方案:

    int i1 = 10/4;
    int i2 = 10.0/4.0;
    int i3 = 0/0;
    long l1 = 10/4;
    long l2 = 10.0/4.0;
    float f1 = 10/4;
    float f2 = 10.0/4.0;
    double d1 = 10/4;
    double d2 = 10.0/4.0;
    double d3 = 0/0;
    double d4 = 0/0.0;
    double d5 = 2.9/0.0;
    
    
  7. 完成下面的代码片段,它将把x的二进制补码分配给y。您必须使用按位运算符:

    int x = 19;
    int y = your-code-goes-here;
    
    
  8. 以下代码片段的输出会是什么?

    int x = 19;
    int y = (~x + 1) + x;
    System.out.println(y);
    
    
x -= x;