Java中前缀和后缀++运算符之间的区别

时间:2015-05-18 07:44:18

标签: java jvm operators bytecode

有一些问题(如Java: Prefix/postfix of increment/decrement operators?),但我询问postfix和前缀++运算符之间的一般区别(我知道那部分),但关于它们在Java规范级别之间的基本差异。

具体来说,前缀和后缀++运算符其他与运算符优先级之间是否有任何区别(可能是javac将命令转换为字节码或JVM运行方式的方式)字节码)?

例如,以下代码必然运行相同(在每个JVM中):

for (int i = 0; i < X; i++) { ... }

for (int i = 0; i < X; ++i) { ... }

JLS中是否有任何内容定义这两个语句在每个平台,Java编译器,JVM等上以完全相同的方式运行,或者这些2个语句可能(甚至理论上)可能以不同的方式运行?

5 个答案:

答案 0 :(得分:3)

是的,它会运行相同。它将编译为相同的字节码,因此无论JVM是什么,JVM都会注意到它没有区别。

您可以自己检查一下:

public class TestIncrement {
    public void testPost(int X) {
        for (int i = 0; i < X; i++) {
            System.out.println(i);
        }
    }

    public void testPre(int X) {
        for (int i = 0; i < X; ++i) {
            System.out.println(i);
        }
    }
}

使用JavaC编译器以相同的方式编译这两种方法:

public void testPost(int);
Code:
   0: iconst_0
   1: istore_2
   2: iload_2
   3: iload_1
   4: if_icmpge     20
   7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
  10: iload_2
  11: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
  14: iinc          2, 1
  17: goto          2
  20: return

或ECJ编译器:

public void testPost(int);
Code:
  0: iconst_0
  1: istore_2
  2: goto          15
  5: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
  8: iload_2
  9: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
 12: iinc          2, 1
 15: iload_2
 16: iload_1
 17: if_icmplt     5
 20: return

不同的编译器会生成不同的字节码,但在这两种情况下,两种方法的字节码都相同。

答案 1 :(得分:2)

Java的一个要点是代码将在每个JVM上运行相同。虽然编译器可以为相同的源代码生成不同的字节码,但它们需要遵循一组规则,就像不同的JVM一样,以确保事情正常运行。

没有未定义或平台相关的行为,例如C。

答案 2 :(得分:2)

规范的相关部分是:

  

15.15.1. Prefix Increment Operator ++

     

[...]

     

在运行时,如果操作数表达式的评估突然完成,则前缀增量表达式会因同样的原因突然完成,并且不会发生增量。否则,将值1添加到变量的值中,并将总和存储回变量中。 [...]
  前缀增量表达式的值是存储新值后变量的值。

  

15.14.2. Postfix Increment Operator ++

     

[...]

     

在运行时,如果操作数表达式的评估突然完成,则后缀增量表达式会因同样的原因突然完成,并且不会发生增量。否则,将值1添加到变量的值中,并将总和存储回变量中。 [...]
  后缀增量表达式的值是存储新值之前的变量的值。

因此, only 差异是在表达式上下文中使用时的结果值。在for循环的update子句的上下文中使用它时,相关部分是:

  

14.14.1.2. Iteration of for Statement

     

[...]

     

首先,如果存在 ForUpdate 部分,则从左到右依次计算表达式;它们的值(如果有的话)被丢弃。如果由于某种原因对任何表达式的评估突然完成,则for语句因同样的原因而突然完成;任何突然完成的 ForUpdate 语句表达式都不会被评估。

但是,这种差异在于不可观察的行为,因此,代码通常被编译为不首先产生值,因此没有区别。

答案是,代码可能有差异,例如:当天真地编译时,可观察到的行为保证是相同的。

答案 3 :(得分:1)

来自Oracle documentation

  

在每次迭代循环之后调用 increment 表达式;这个表达式增加递减一个值是完全可以接受的。

来自Oracle documentation on JLS

  

首先,如果存在 ForUpdate 部分,则从左到右依次计算表达式;它们的价值(如果有的话)被丢弃。

在迭代之后调用并且丢弃返回值时,两个选项之间不应该有任何区别。也许字节码不完全相同,但它应该是等价的。

答案 4 :(得分:0)

这里已有很好的答案,但我想尝试从另一个角度回答这个问题。我将只讨论for循环必须如何工作,使用与Java相同类型的for循环的任何语言。

它有四个部分:

  • 初始化部分
  • 条件部分
  • 循环变量修改部分
  • 循环的身体部位

任何编译器都需要单独处理它们,它们无法通过以任何方式连接它们来进行优化。 (无论如何,依据我。)每个部分都必须被视为某种孤立的实体。我们只对本讨论中的循环变量修改部分感兴趣。

由于它是一个孤立的实体,因此使用i++--i无关紧要。没有Java编译器,其他语言的任何其他编译器或任何语言解析器都可以在这方面以不同方式处理for循环。

这意味着答案必须是:是的,它们必须以相同的方式运行。