作者选择了 自由和开源基金作为 写给捐款计划的一部分接受捐款。
介绍
一个 operator 是一个或多个符号的组合,如已知的算术运算符 minus (-
) 和 plus (+
) 或更高级的 instanceof
. 当你将运算符应用于值或变量时,你会从操作中获得结果。
对于本教程,你必须熟悉一个 operand,即操作员所应用的值或变量。 根据操作员的数量,操作员可以分为三组。 首先,当操作中只有一个操作员时,操作员被称为 unary。 同样, binary operators 涉及两个操作员。 最后,当有三个操作员时,操作员是 ternary。 按照这种分类,本教程为每个类型的操作员分为三个主要部分。
在本教程中,您将使用所有三种类型的运算符来操纵原始数据类型,例如在数学方程式中。
前提条件
要遵循本教程,您将需要:
要在本地机器上设置,你需要以下内容:
- Java(版本 11 或更高)安装在你的机器上,由 Java 开发套件(JDK)提供的编译器。对于 Ubuntu 和 Debian,按照我们教程中的 选项 1的步骤进行编译, How To Install Java with Apt on Ubuntu 22.04 对于其他操作系统,包括 Mac 和 Windows,请参阅 download options for Java installation ]。
- 要编译和运行代码示例,本教程使用 Java Shell,这是从命令行运行的 _Read-Evaluate-Print Loop_PL(https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop
](https://andsky.com/tech/tutorials/理解数据类型-在雅瓦瓦)。
联合运营商
Unary 运算符被应用到一个运算符,这使得它们最简单。 Unary 运算符通常被用来使用,因为它们使您的代码更加简洁和可读。它们取代了需要明确描述的操作,如增加和下降的值。
接下来,您将使用统一运算符来增加和减少值,以及扭转布尔值。
增加和减少运营商
Increment 和 decrement 运算符,正如他们的名字所示,增加和减少数字. 增量运算符是两个加号符(++
)的组合,减量运算符是两个减数符(--
)。
预增和预减
当您在操作员之前使用操作员时,您将根据使用++
或--
而使用preincrementing
或predecrementing
。
<$>[info]
Info: 要跟进本教程中的示例代码,请通过运行jshell
命令在本地系统上打开Java Shell工具,然后您可以复制、粘贴或编辑示例,在jshell
提示后添加它们并按ENTER
。
若要使用预增量运算符,请在jshell
中输入下列内容:
1int theAnswer = 42;
2System.out.println("Preincrementing: " + ++theAnswer);
在第一行,您定义一个变量 theAnswer
值 42
. 在第二行,您使用 println()
方法来打印它,从而证明它是如何改变的。
上面的示例中的预加值运算器是++
,并放在答案
前面。通过使用预加值运算器的方式,您首先将答案
的值增加到43
。
1[secondary_label Output]
2theAnswer ==> 42
3Preincrementing: 43
Predecrementing 工作类似,但而不是增量,您正在减去操作数的值. 作为一个练习,修改上面的示例,以便您使用的预增量运算符 ++
,而不是预增量运算符 --
。
后增量和后减量
与前
操作员不同,在使用后,后
操作员会改变一个操作员的值,有些特殊情况下,后
或前
操作员通常被使用,但整体上,这是个人的偏好问题。
要演示post
运算符是如何工作的,您将后增量theAnswer
的值,并检查其值如何变化。
1int theAnswer = 42;
2System.out.println("Postincrementing: " + theAnswer++);
3System.out.println("Final value: " + theAnswer);
变量 theAnswer
首先等于 42
. 然后,它被打印并加倍. 在最后一行,您再次打印它,以查看其最终值。
你的产量应该是:
1[secondary_label Output]
2theAnswer ==> 42
3Postincrementing: 42
4Final value: 43
正如您所看到的,答案
在后增量过程中仍然是42
。
Postdecrementing 以相同的方式工作. 值首先被检索和使用,然后才被降级. 作为一个练习,尝试用 postdecrement 运算符 ++' 替换 postdecrement 运算符
--,甚至包括其中一个 `pre' 运算符。
非操作员
NOT
运算符,也被称为 logical complement operator,扭转了布尔运算符的值. 它由 exclamation mark!
表示。 通常,当你有一个布尔运变量或值时,你会使用NOT
运算符,并且想要用相反的值重复使用它。
以下是NOT
运算符的例子,为了简化,你会扭转true
的值:
1boolean isJavaFun = !true;
2System.out.println(isJavaFun);
您将 boolean 变量 isJavaFun
定义为 true
. 然而, NOT
操作员先于 true
;因此, true
的值被扭转为 false
. 当您运行上述代码时,下列输出将打印:
1[secondary_label Output]
2isJavaFun ==> false
3false
这就是NOT
操作员的工作方式,有时会令人困惑,很难发现,所以你应该小心使用它。
在上述情况下,您可以使用真
,而不是假
。这是正确的方法,因为它更清洁,更直观。作为一般规则,最好直接使用一个字母或方法,而不是需要额外操作的替代方案。
例如,要检查一个字符串是否包含另一个字符串,可以使用方法 contains()
. 但是,如果要检查相反的方法(即当一个字符串不包含另一个字符串时),没有其他内置方法。
假设你有字符串Java 是智能的
,你想检查是否:
- 字符串含有
智能
2 字符串不含有硬
要检查这些,您将使用以下代码:
1String javaIsSmart = "Java is smart.";
2boolean isSmartPartOfJava = javaIsSmart.contains("smart");
3boolean isHardNotPartOfJava = !javaIsSmart.contains("hard");
在第一行中,您定义了字符串
变量javaIsSmart
。在第二行中,您定义了布尔变量isSmartPartOfJava
作为从方法contains()
操作的结果 - 在这种情况下,字符串smart
是否是javaIsSmart
字符串的一部分。
当你在jshell中运行此代码时,你会得到以下输出:
1[secondary_label Output]
2javaIsSmart ==> "Java is smart."
3isSmartPartOfJava ==> true
4isHardNotPartOfJava ==> true
根据上述结果:
*「isSmartPartOfJava」是「真」,因為「smart」在「javaIsSmart」中找到。 *「isHardNotPartOfJava」也是「真」,因為「hard」不在「javaIsSmart」中找到。
在本节中,您探讨了增加、减少和使用一个操作员的 NOT 操作员. 尽管这些操作员只有一个操作员,但它们可能难以使用,正如NOT
操作员所示。
二进制操作员
二进制操作器在两个操作器上运作,通常与算术操作有关,如加和扣除,还有其他非数学相关的二进制操作器,如逻辑操作器和特殊的关系操作器instanceof
。
数值二进制操作员
这些是用于算术操作的众所周知运算符,如附加(+
)和抽取(-
)。
1int theAnswer = 40 + 2;
2System.out.println("The result is: " + theAnswer);
在第一行中,您将40
添加到2
,并将结果分配给答案
变量。
1[secondary_label Output]
2The result is: 42
<$>[注]
**注:**除算术操作外,加值符号(+
)也用于连接字符串。您在我们大多数以打印值的示例中都已经看到它在操作中,如上面的一个。在那里,使用加值符号,您已经连接了 ``结果是: 与变量
答案`。然而,这种使用加值符号是例外,在参考类型上没有其他算术运算器可以用到类似的方式。
对于额外的练习,请尝试使用其他算术运算符,您可以在 Java 文档中找到。
分配运营商
分配运算器将左运算器分配到右运算器的值. 通常,左运算器是变量,右运算器是对象的值或参考。
基本分配运算符(=
)是一个已知且常用的运算符。
1int x = 1;
在本示例中,您声明int
变量x
并分配给它值1
。
组合分配运营商
复合分配操作员(+=
, -=
, *=
, \=
)将分配与额外的算术操作(如加算或减算)相结合,这些操作员允许您避免锅板代码,特别是在易于遵循和理解的算术操作中。
例如,使用化合物 +=
分配运算器将添加和分配相结合,如下:
1int x = 1;
2int y = 1;
3x += y;
4System.out.println("x is: " + x);
在前两行中,您声明两个名为x
和y
的整数变量,两者都具有1
的值,接下来,您使用复合+=
分配重新分配x
,这意味着x
被添加到y
,然后被分配回到x
。
上面的代码将返回类似于此的输出:
1[secondary_label Output]
2x ==> 1
3y ==> 1
4$11 ==> 2
5x is: 2
根据上述输出,‘x’和‘y’获得了‘1’的值。在第三行,有一个具有随机分配的名称的临时变量(‘$11’)。
相同的代码可以在没有复合分配运算器的情况下重写,如下:
1int x = 1;
2int y = 1;
3x = x + y;
4System.out.println("x is: " + x);
与上一个例子不同的是,您写了额外的代码,以明确描述x
加上y
在第三行中的添加。
运行此代码将返回以下输出:
1[secondary_label Output]
2x ==> 1
3y ==> 1
4x ==> 2
5x is: 2
最终,在这两个例子中,x
等于2
。然而,在第二个例子中,jshell
没有打印一个临时变量名称,如$11
。
其余的复合运算符结合了减数(-=
)、倍数(*=
)和划分(/=
)以及分配。
了解复合操作员是很好的,因为它们经常使用,但是使用它们没有性能效益,所以使用复合操作员是个人选择的问题。
卡斯特操作员
您将审查的最后一个分配运算器是铸造运算器,该运算器是由围栏包围的数据类型: (数据类型)
. 铸造运算器用于铸造值,该运算器将一个数据类型解释为另一个。
然而,数据类型必须兼容。一个数据类型是否与另一个数据类型兼容,取决于它们的关系,例如一个类型是否是父母或兄弟姐妹。
在本节中,您将探讨一些常见的铸造例子和问题. 为了教育目的,您将从一个不正确和不相容的铸造开始:
1boolean y = (boolean) 1;
使用此行,您正在尝试将整数1
投放到布尔值,并将其分配到变量y
。
1[secondary_label Output]
2| Error:
3| incompatible types: int cannot be converted to boolean
4| boolean y = (boolean) 1;
5|
正如错误消息所解释的那样,您无法将int
值转换为boolean
。
现在,您将尝试使用兼容的数据类型的一个示例. 您将使用两个原始类型来存储整数: int
和 short
. 差异在于它们的容量,即存储信息可用的内存量。
将下列行添加到jshell
:
1int prize = 32767;
2short wonPrize = (short) prize;
3System.out.println("You won: " + wonPrize);
在第一行中,你将彩票奖项定义为一个int
原始类型,值为32767
。在第二行,但是,你决定一个短
原始类型将更适合赢得奖金
的价值,你将奖金
投到短
使用(短)
。
当你在jshell中运行上述代码时,输出是:
1[secondary_label Output]
2prize ==> 32767
3wonPrize ==> 32767
4You won: 32767
上面的输出证实奖金
和赢利
值已正确设置为32767
。
在int
和short
的情况下,铸造可能看起来不必要,你可能不会在现实中看到这样的铸造。
当您将具有较大的容量的数据类型投放到具有较小的容量的数据类型时,您可能会超过较小的容量限制,称为 overflow。
1int prize = 32768;
2short wonPrize = (short) prize;
3System.out.println("You won: " + wonPrize);
当您在jshell
中运行上述内容时,您将获得以下输出:
1[secondary_label Output]
2prize ==> 32768
3wonPrize ==> -32768
4You won: -32768
在这种情况下,你会失去信息并获得意想不到的结果。当投放到短
,32768
的值会变成32768
。这是因为短
的存储容量从32768
到32767
之间。当你尝试存储大于最大值的值时,你会将其压倒并从头开始。在这种情况下,当你尝试存储32768
时,你会超过最大容量(‘32767’)的值为1
。
这就是为什么上述输出似乎是意想不到的 - 最终的奖金已经成为一个负数. 这些类型的问题并不总是容易发现,所以你应该仔细使用铸造。
关系运营商
关系运算符比较两个运算符,并返回布尔结果. 如果声称关系,则结果为真
,否则结果为假
。
第一个类型的关系运算符等于==
,而不是等于!=
。它们被用来声称价值和对象的平等性。
要证明等式运算器,比较两个整数字母,实际上,它将是一个和同一个数字: 1
. 你将比较它是否等于自己,以便你可以得到一个真实
的结果。
1System.out.println(1==1);
在上面的代码中,你声称‘1’是否等于‘1’。由于数字等于‘1’,这个表达式评估为 true.因此,‘println()’打印到‘true’:
1[secondary_label Output]
2true
作为一个练习,尝试改变其中一个值,以获得错误的结果。
<$>[注]
注: 请确保区分平等的运算符 ==
和分配运算符 =
.即使你知道它们不同,很容易混淆它们。
与比较原始值相比,比较对象以平等性更为复杂,因为您正在声称两个变量是否指向相同的对象。
1Integer myAnswer = Integer.valueOf(42);
2Integer yourAnswer = Integer.valueOf(42);
3System.out.println(myAnswer == yourAnswer);
在上面的代码中,你创建了两个Integer
变量,每个变量都具有42
的值。在最后一行中,你将它们比较为平等,并打印true
如果它们是平等的。从我们之前的教程中(https://andsky.com/tech/tutorials/understanding-data-types-in-java),你可能知道Integer.valueOf()
首先检查具有相同值的对象的缓存并返回相同的对象,如果已经有这个值的对象。
当你在jshell中插入上述代码时,你会得到以下输出:
1[secondary_label Output]
2myAnswer ==> 42
3yourAnswer ==> 42
4true
这种对象比较看起来很简单,但有时它是具有挑战性的. 最令人困惑的例子是与字符串。
1String answer1 = new String("yes");
2String answer2 = new String("yes");
3System.out.println(answer1 == answer2);
首先,您声明两个新的字符串
变量(answer1
和answer2
)具有是
值,但是,您使用新
关键字来创建新的字符串
对象,因此,这两个变量并不指向相同的对象 - 它们实际上指向两个不同的对象(具有相同的值)。
当你在jshell中插入上述代码时,你会得到以下输出:
1[secondary_label Output]
2answer1 ==> "yes"
3answer2 ==> "yes"
4false
尽管answer1
和answer2
具有相同的值(是
),但它们的等级被评估为假
,这意味着它们不等同。如果您打算比较值,例如两种答案是否是肯定的,而您不感兴趣的底层对象。
在 String 的情况下,这种方法是 equals()
. 尝试更改代码以使用 equals()
(而不是 `==')如下:
1String answer1 = new String("yes");
2String answer2 = new String("yes");
3System.out.println(answer1.equals(answer2));
equals()
方法验证了对比对象中包含的字符串是否等同。
当您将此代码插入到jshell
中时,您将获得以下输出:
1[secondary_label Output]
2answer1 ==> "yes"
3answer2 ==> "yes"
4true
上面的输出与之前的输出类似,但它以true
结束,确认两个对象的值等同。
替代平等运算符,而不是等于 !=
,以类似的方式使用,但它声称两个变量或值是否不相同(或不等式)。
类似于==
和!=
,接下来的四个关系运算也来自数学:小于<
,小于或等于<=
,大于>
,大于或等于=>
。
以下是使用大于操作员的示例:
1System.out.println(4 > 5);
当你在jshell中运行此代码时,你会得到以下输出:
1[secondary_label Output]
2false
上面的代码首先比较4
是否大于5
。因为它不是,表达式评估为false
。
最后一个关系运算器是instanceof
,它评估变量是否是特定类(或子类)的实例或接口的实现,正如我们在(理解Java中的数据类型)教程中所解释的那样,接口是一个具有一组要求的抽象实体。
使用下面的示例来探索instanceof
是如何工作的:
1String greeting = "hello";
2System.out.println(greeting instanceof String);
首先,你会创建一个名为greeting
的String
变量,然后,你会评估greeting
是否是String
的实例。
当你在jshell中插入代码时,你会得到以下输出:
1[secondary_label Output]
2greeting ==> "hello"
3true
由于问候
是字符串
的实例,因此该表达式评估为true
,该表达式在屏幕上被打印为println()
。
逻辑操作员
逻辑运算符是逻辑 AND
(&
),逻辑 OR
(ʔ
),和独家 OR
(^
)。
- 逻辑
AND
是真实的,当两个值都为true
. - 逻辑
OR
是真实的,当至少一个值是true
. - 唯一的
OR
是真实的,如果一个值是true
,而另一个值是false
.
当逻辑操作符不符合上述条件时,它们是假的。
若要使用逻辑 AND (&
) 运算符,请将下面的示例粘贴到 jshell
中:
1boolean isJavaFun = true;
2boolean isJavaPowerful = true;
3System.out.println(isJavaFun & isJavaPowerful);
第一两个行将Boolean
变量isJavaFun
和isJavaPowerful
都定义为true
。在第三行中,您在isJavaFun
和isJavaPowerful
上执行一个逻辑的AND
操作,结果被打印为println()
。
当您将此代码插入到jshell
中时,您将获得以下输出:
1[secondary_label Output]
2isJavaFun ==> true
3isJavaPowerful ==> true
4true
两个变量都设置为true
,最终的true
被打印为逻辑AND
操作的结果。
要扩展你的技能,请尝试使用前面的代码示例,其中有一些变量。你可以尝试将变量值切换为真实
和假
。
逻辑操作员的扩展版本是所谓的短路逻辑操作员
(LINK0):短路AND
和短路OR
等。它们与常规逻辑AND
和OR
操作员相似,但它们有一个重要的区别:如果评估第一个操作员是足够的操作,第二个操作不被评估。因此,为了让短路AND
是真实的,周围的两侧都必须是真
。然而,如果左侧是假
,右侧不被评估。
以下是短路OR
的例子:
1boolean isJavaFun = true;
2Boolean isNullFun = null;
3System.out.println(isJavaFun || isNullFun);
首先,您将变量 isJavaFun
分配为 true
. 为了与之前的例子相一致,这个变量是原始类型的 boolean
. 然而,对于下一个变量, isNullFun
,您使用了 Boolean
参考类型,以便您可以将其分配为 null
。
当短路OR
发生在窗口中时,isNullFun
被忽略,因为左侧是true
,这足以使整个表达式为true
。
1[secondary_label Output]
2isJavaFun ==> true
3isNullFun ==> null
4true
第一行和第二行确认变量被分配为true
和null
;第三行打印true
,因为短路OR
操作返回了true
。
上面的示例是用null
来选择的,以便展示短路操作员的工作方式,并且isNullFun
不会被评估。
1boolean isJavaFun = true;
2Boolean isNullFun = null;
3System.out.println(isJavaFun | isNullFun);
常规逻辑OR
评估表达式的两面。
当你在jshell中运行此代码时,你会得到以下输出:
1isJavaFun ==> true
2isNullFun ==> null
3| Exception java.lang.NullPointerException
4| at (#3:1)
当逻辑OR
试图评估null
时,你会得到java.lang.NullPointerException
。
作为一个练习,练习使用短路&&
和``,这些都是最流行的和最有用的。
在本节中,您在一系列示例中使用了二进制操作器,从基本算法到涉及铸造和比较对象的更具挑战性的操作。
Ternary 操作员
在以前的部分中,你练习使用一个和两个操作员的操作员. 在本最后的部分中,你将使用三重操作员,这三个操作员的唯一操作员。它的语法是这样的:‘第一操作员?第二操作员:第三操作员’。第一个操作员必须是布尔式。如果它是‘真’,那么第二操作员从表达式中返回。
三位数运算器很受欢迎,并且经常使用,因为它可以节省您写复杂的陈述,如条件,并将其结果存储在临时变量中。
尝试使用以下示例的三级运算器:
1boolean isJavaFun = true;
2String shouldILearnJava = isJavaFun ? "yes" : "no";
3System.out.println("Should I learn Java: " + shouldILearnJava);
isJavaFun
设置为true
,而变量shouldILearnJava
由它在三次操作中确定,因为第一个操作isJavaFun
是真的,因此返回第二个操作,即字符串yes
。
当您运行上述代码时,您将获得以下输出:
1[secondary_label Output]
2Should I learn Java: yes
对于额外的练习,请尝试在isJavaFun
前面的NOT
操作器:
1boolean isJavaFun = true;
2String shouldILearnJava = !isJavaFun ? "yes" : "no";
3System.out.println("Should I learn java: " + shouldILearnJava);
通过扭转 isJavaFun
的值,您将其从 true
设置为 false
. 因此,三位数表达式将返回最后一个操作数,即 no
。
Ternary表达式可能令人困惑,特别是当您使用额外的操作员,如NOT
操作员时,它们可以为您保存一些锅炉板代码,这就是为什么它们很受欢迎的原因。
运营商先行
一旦你知道重要的操作员,你将能够使用甚至组合它们,但在你开始组合它们之前,你需要知道操作员优先权的规则。
操作员优先级决定了操作员在哪个顺序中被评估。由于您很可能使用多个操作员,所以了解操作员优先级很重要。
理解操作员优先级有助于你写清洁代码(https://learning.oreilly.com/library/view/clean-code-a/9780136083238/),这是现代编程的实际标准。写清洁代码意味着写可理解和可维护的代码。
例如,如下面的陈述应该避免,因为它们太混淆了:
1boolean isThisCleanCode = !true || false && true;
即使您查看操作员文档(LINK0),您也可能无法猜测最终结果(‘isThisCleanCode’是‘false’)。
以下是最重要的和最常用的优先规则,从优先规则开始:
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 它们具有最高优先地位,并在任何其他操作者面前生效。
- ** 来自数学的算术规则:** 所有在数学中有效的规则在Java中也是有效的. 例如, " / " 项比 " 增加 " 项优先。 括号优先于优先级, 也就是说, 您可以用括号组合操作, 并且会优先评价它们 。
- ** 平等和关系操作员:** 由于它们评价平等或关系,它们也使用其业务的最终价值,因此所有其他业务都必须完成。
- 逻辑
OR'和
AND'运算符(包括短路): 与三元运算符类似,它们都需要其操作的终极值,因此它们处于次要地位。 - ** 终端操作员: ** 三角运算符需要第一个操作符的最终值. 要做到这一点,所有其他行动都必须已经完成。 因此,地铁运营商有如此低的优先权.
- ** 指派运算符 : ** 最后一次评估是为了完成所有其他作业,最后的结果可以分配给左边的操作。 举个例子,想想你迄今给的每个 等号的变量。 (英语)
现在,您将使用数学问题来探索操作员优先级:
1int x = 1 + 10 / 2;
当你在jshell中插入上述代码时,你会得到以下输出:
1[secondary_label Output]
2x ==> 6
根据上述输出,x
得到6
的值,这是因为下列操作已完成,优先级下降:
10 / 2
首先被评估. 结果为5
.1
被添加到第一个操作的结果(5
). 结果为6
.- 分配操作器开始发挥并使用最终结果(
6
)将其分配给变量 `x。
虽然这个数学方程相对简单,但你总是可以让它更有说法,并确保优先级很容易理解,使用。
1int x = 1 + (10 / 2);
当您粘贴上面的jshell
时,将打印相同的结果如前所述:
1[secondary_label Output]
2x ==> 6
在最后一个例子中,您没有通过使用围绕10 / 2
的关节来改变优先级。相反,它们的目的是仅仅使它们内部的操作优先级变得更加明显。
优先级规则很有趣,花一些时间学习它们是一个好主意. 记住清洁代码范式,并考虑到不必要的插座和操作员的组合被视为代码的弱点。
结论
在本教程中,您了解了Java中的主要操作员,您写了几个测试代码片段,您看到一些与操作员相关的最有用的和有趣的场景,您还了解了清洁代码和操作员不应该被过度使用的事实。
關於Java的更多信息,請參閱我們的 如何在Java中編碼系列。