Java 运算符优先级准则

Posted

技术标签:

【中文标题】Java 运算符优先级准则【英文标题】:Java operator precedence guidelines 【发布时间】:2011-01-09 09:50:27 【问题描述】:

误解Java 运算符优先级 是常见问题和细微错误的根源。我很感兴趣地了解到,即使是 Java Language Specification 也说:“建议代码不要严重依赖此规范。” JLS §15.7更喜欢清晰而不是聪明,这方面有什么有用的指导方针吗?

这里有一些关于该主题的资源:

JLS Operators JLS Precedence Java Glossary Princeton Oracle Tutorial Conversions and Promotions Java Operator Precedence Evaluation Order and Precedence Usenet discussion

欢迎补充或更正。

【问题讨论】:

如有疑问,请添加更多括号。 当括号太多时,重构。 JLS 引用(在上下文中)鼓励分解具有多个副作用(或子表达式中的副作用)的语句,以避免左侧和右子表达式。这不能通过添加括号来完成。换句话说,JLS 建议不要依赖 Java 的 Evaluation Order,而不是反对依赖运算符优先级。 below 和 here 解释了它们之间的区别。 ((When)(in)((doubt)(,)(add more)((parentheses)?)) 不,谢谢。请添加最低金额。 @KevinPanko:为example。 【参考方案1】:

就“现实世界”而言,可以这么说:

足够多的程序员知道乘法/除法优先于加法/减法,这是数学上的惯例 几乎没有程序员能记住任何其他优先规则

所以,除了 */+- 的具体情况外,我真的只是使用括号来明确定义预期的优先级。

【讨论】:

你的意思是括号还是圆括号? 程序员很难记住的原因是每种语言略有不同。 您还可以依赖关系运算符(<<===>=', >')优先于逻辑运算符(&&||。) @Neil - 在与 IT 人员交谈时,最好坚持使用传统的 IT/印刷术语。 '()' 是圆括号(或圆括号),'[]' 是方括号(或方括号),'' 是大括号(或花括号)。 在运算符优先级的上下文中,“括号”实际上只能表示“圆括号”。【参考方案2】:

JLS 没有给出明确的运算符优先级表;当JLS 描述各种运算符时暗示了这一点。例如ShiftExpression 的语法是这样的:

ShiftExpression:
    AdditiveExpression
    ShiftExpression << AdditiveExpression
    ShiftExpression >> AdditiveExpression
    ShiftExpression >>> AdditiveExpression

这意味着加法运算符(+-)的优先级高于左结合移位运算符(&lt;&lt;&gt;&gt;&gt;&gt;&gt;)。

【讨论】:

【参考方案3】:

引用(来自Java Language Specification §15.7)应在Evaluation Order 的上下文中阅读。正如here 所讨论的,该部分涉及求值顺序,它与运算符precedence(或关联性)无关。

优先级和关联性影响表达式树的结构(即哪些运算符作用于哪些操作数),而“评估顺序”仅影响表达式的顺序在计算表达式时遍历树。除非某些子表达式具有影响其他子表达式结果(或副作用)的副作用,否则求值顺序(或“遍历顺序”)不会产生任何影响。

例如,如果 x==1 最初,表达式 ++x/++x 将计算为 2/3(计算结果为 0),因为 Java 具有从左到右的计算顺序。如果 Java 中的计算顺序是从右到左,x 在计算分子之前会增加两次,并且表达式将计算为 3/2(计算结果为 1)。如果未定义评估顺序,则表达式可能会评估为这些结果中的任何一个。

有问题的引用,连同它的上下文,......

Java 编程语言保证 运算符似乎以特定的评估顺序进行评估, 即从左到右。

建议代码不要过度依赖本规范。 当每个表达式最多包含一侧时,代码通常会更清晰 效果,作为其最外层的操作

...不鼓励读者依赖 Java 的 求值顺序的从左到右(如上例所示)。它不鼓励不必要的括号。

编辑:资源:Java operator precedence table,它还用作 JLS 部分的索引,其中包含推断每个优先级的句法语法。

【讨论】:

求值顺序是指子表达式求值的顺序,例如(a+b)/(c+d)。保证先左后右。它丝毫不会影响 / 运算符本身的评估方式。您关于 2/3 与 3/2 的示例完全不正确。它也与树遍历无关:它是生成代码的一个属性,其中根本没有表达式树,只有 RPN。 @EJP 好吧,RPN 只是编码expression tree 的一种方式,顺序处理 RPN 相当于我所指的遍历。至于您断言我的示例“完全不正确”,您实际上是否声称 ++x/++x 在 LTR 和 RTL 评估顺序下会评估为相同的结果? 感谢您提供这个有用的座谈会。我打算专注于“语言如何尊重括号明确指示的评估顺序和运算符优先级隐含指示的顺序”,但我看到两者(括号和优先级)都必须在Evaluation Order 的整体上下文中考虑。 Isak:我认为 EJP 没有这样的说法。 EJP:我推断 Isak 是在假设性地写作。 外交+1 @trashgod。人们对你的问题的答案有不同的看法。我只是认为在引用中指出 JLS 没有提供您认为的意见可能会很有用。 @EJP 不止一次地指导了我,所以我通常认为是我的无知,而不是他的错误。我已经编辑了我的问题以包含您引用的链接并修改了您的答案以强调更大的背景。您可以用引用此答案的评论替换您对该问题的评论。【参考方案4】:

另外,不要忘记逻辑 && 和 ||是快捷操作符,避免类似:

sideeffect1() || sideeffect2()

如果 sideeffect1() 的计算结果为 true,则 sideeffect2() 不会被执行。 && 和 false 也是如此。这与优先级并不完全相关,但在这些极端情况下,关联性 也可能是一个重要的方面,通常情况下确实无关紧要(至少就我而言)

【讨论】:

在 Java 中,&amp;| 运算符可以是布尔逻辑(以及按位整数)。这些是非短路运算符。使用|| 而不是| 是对条件操作数求值的显式请求,很像?:【参考方案5】:

在我看来,事实是'大多数程序员'认为'大多数其他程序员'不知道或不记得运算符优先级,所以他们沉迷于被'放纵地称为'防御性编程'的东西。插入缺少的括号',只是为了'澄清'。记住这些三年级的东西是否是一个真正的问题是另一个问题。同样可以说,所有这些完全是浪费时间,如果有任何事情会使事情变得更糟。我自己的观点是,应尽可能避免冗余语法,并且计算机程序员应该了解他们正在编程的语言,并可能提高他们对同事的期望。

【讨论】:

对不起,对我来说这是有争议的。我更同意 JLS 15.7。他们会改变顺序吗?谁知道。但我可以告诉你,我曾参与过同时使用 C++ 和 Java 代码的项目,它们在优先级上存在细微差别(例如:已定义优先级与未定义优先级),我们的大多数开发人员在评估时更愿意确定评估顺序可能有一天会编写 Java 代码,然后是 C++。 @OgrePsalm33 (a) 当然它是“争论的”:如果你的意思是“有争议的”,这个问题就引发了争论; (b) JLS 15.7 不是关于运算符优先级,而是关于评估顺序; (c) 不,它们永远不会改变运算符的优先级; (d) 在 Java 或 C++ 或我知道的任何其他语言中都没有未定义的运算符优先级:不可能用未定义的语法构造解析器; (e) 括号不控制评估顺序。【参考方案6】:

另一个相关的错误来源是舍入错误如何累积。本身不是运算符优先顺序问题,而是在以算术等效方式重新排列操作数后得到不同结果时的惊喜来源。这是 David Goldberg 的What Every Computer Scientist Should Know About Floating-Point Arithmetic 的 sun.com 版本。

【讨论】:

以上是关于Java 运算符优先级准则的主要内容,如果未能解决你的问题,请参考以下文章

java运算符优先级问题

Java运算符优先级谁知道?

java中位运算符的优先级

在java运算符&&,,==,+中优先级最高的是

java运算优先级的问题

Java 运算符优先级