为什么javac创建了一个未使用的带有后缀的变量++&引用;接线员在上课吗?

为什么javac创建了一个未使用的带有后缀的变量++&引用;接线员在上课吗?,java,compiler-optimization,Java,Compiler Optimization,受此启发,我开始了一项小小的研究 我能够确定使用原语时,编译器会将前缀++I重写为I++: 之前: public class PrefixIncrement { public static void main(String args[]) { for(Integer i = 0; i < 100; ++i) { System.out.println(i); } } } public class PostfixI

受此启发,我开始了一项小小的研究

我能够确定使用原语时,编译器会将前缀
++I
重写为
I++

之前:

public class PrefixIncrement {
    public static void main(String args[]) {
        for(Integer i = 0; i < 100; ++i) {
            System.out.println(i);
        }   
    }
}
public class PostfixIncrement {
    public static void main(String args[]) {
        for( Integer i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}
============================按请求:javap输出=======================

使用
Integer
类的后缀增量

{
  public com.foo.PostfixIncrement();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>
":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   Lcom/matt/PostfixIncrement;

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_0
         1: invokestatic  #2                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
         4: astore_1
         5: aload_1
         6: invokevirtual #3                  // Method java/lang/Integer.intVal
ue:()I
         9: bipush        100
        11: if_icmpge     40
        14: getstatic     #4                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
        17: aload_1
        18: invokevirtual #5                  // Method java/io/PrintStream.prin
tln:(Ljava/lang/Object;)V
        21: aload_1
        22: astore_2
        23: aload_1
        24: invokevirtual #3                  // Method java/lang/Integer.intVal
ue:()I
        27: iconst_1
        28: iadd
        29: invokestatic  #2                  // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
        32: dup
        33: astore_1
        34: astore_3
        35: aload_2
        36: pop
        37: goto          5
        40: return
      LineNumberTable:
        line 5: 0
        line 6: 14
        line 5: 21
        line 8: 40
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               5      35     1     i   Ljava/lang/Integer;
               0      41     0  args   [Ljava/lang/String;
      StackMapTable: number_of_entries = 2
           frame_type = 252 /* append */
             offset_delta = 5
        locals = [ class java/lang/Integer ]
           frame_type = 250 /* chop */
          offset_delta = 34

}
{
public com.foo.postfix增量();
旗帜:ACC_PUBLIC
代码:
堆栈=1,局部变量=1,参数大小=1
0:aload_0
1:invokespecial#1//方法java/lang/Object。“
“:()V
4:返回
LineNumberTable:
第3行:0
LocalVariableTable:
起始长度插槽名称签名
0 5 0此Lcom/matt/POSTFIX增量;
公共静态void main(java.lang.String[]);
标志:ACC_公共,ACC_静态
代码:
堆栈=2,局部变量=4,参数大小=1
0:iconst_0
1:invokestatic#2//方法java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
4:astore_1
5:aload_1
6:invokevirtual#3//方法java/lang/Integer.intVal
ue:()我
9:bipush 100
11:if_icmpge 40
14:getstatic#4//Field java/lang/System.out:Ljav
a/io/PrintStream;
17:aload_1
18:invokevirtual#5//方法java/io/PrintStream.prin
tln:(Ljava/lang/Object;)V
21:aload_1
22:astore_2
23:aload_1
24:invokevirtual#3//方法java/lang/Integer.intVal
ue:()我
27:iconst_1
28:iadd
29:invokestatic#2//方法java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
32:dup
33:astore_1
34:astore_3
35:aload_2
36:流行音乐
37:goto 5
40:返回
LineNumberTable:
第5行:0
第6行:14
第5行:21
第8行:40
LocalVariableTable:
起始长度插槽名称签名
5351i Ljava/lang/Integer;
0 41 0 args[Ljava/lang/String;
StackMapTable:条目数=2
框架类型=252/*追加*/
偏移量δ=5
局部变量=[类java/lang/Integer]
框架类型=250/*印章*/
偏移量δ=34
}

前缀++的int版本的语义是:
++i
更简单:
i=i+1;返回i

i++
意味着更复杂的事情:
inti$=i;i=i+1;返回i$

当int被包装成整数时,使用的编译器显然不能生成足够智能的代码。它实际上保持了
i
(上面的
i$
)的旧值。没有理由如此愚蠢,因为
i++
没有在该位置使用

for (...; ...; (void)[[ i++ ]] ) {
在第一个实例中生成

Integer i$ = i;
i = Integer.valueOf(i.intValue() + 1);
"return" i$ to a void context
drop variable i$ where i$ is unused
本应简化为:

i = Integer.valueOf(i.intValue() + 1);

我怀疑这两个临时变量,
localInteger1
localInteger2
是用来处理一般装箱/拆箱规则的占位符。不看反编译的字节码,它可能会揭示一些未从编译的java代码中重新解释的细节,也不看其他一些不太重要的情况,我只是在猜测

但是,很明显,它们在循环
ForUpdate
表达式之前和之后都保留了
i
的值

考虑这种情况:

Integer i;
for( i = 0; i < 100; i++) {
   System.out.println(i);
}
System.out.println(i); // what is the value of `i` here?
整数i;
对于(i=0;i<100;i++){
系统输出打印LN(i);
}
System.out.println(i);//这里'i'的值是多少?
在Java代码中,除了声明的类型是
Integer
而不是
int
之外,它与原语的情况相同


编译器必须在循环执行期间和之后跟踪
i
的值,此时它将是
100
。我怀疑在实现循环和处理原语
int
时会出现一些情况,但处理对象引用更新包括更复杂的情况,因此需要他有两个临时值。

正如您在类的
javap
输出(使用
javac
1.8.0\u 60编译)中看到的,变量实际上并不存在:

public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=2, locals=2, args_size=1
     0: iconst_0
     1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     4: astore_1
     5: aload_1
     6: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
     9: bipush        100
    11: if_icmpge     34
    14: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
    17: aload_1
    18: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
    21: aload_1
    22: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
    25: iconst_1
    26: iadd
    27: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    30: astore_1
    31: goto          5
    34: return
(局部变量与
*加载
*存储
指令一起使用,如您所见,只涉及一个局部变量。)


这个问题似乎是由反编译器引起的,它可能不太支持在
中对
语句进行装箱。因此
javac
没有任何错误,这只是反编译器的错误。不必担心性能影响或任何事,除非您计划进行反向工程并保存反编译的代码:p.

什么原始代码是否具有
Integer
类?这才是重要的,您没有在此处显示。您是否查看了生成的字节码?反编译不一定是实际源代码的准确表示。@Jeroenvanevel-->我用“Integer”版本更新了它。短版本只是将“int”替换为for循环声明中的“整数”。算术运算不依赖自动装箱。请改用内置类型(
int
)。依赖自动装箱将导致隐藏的性能问题,如您所看到的问题。您还应该发布
javap
输出。有时反编译器会插入疯狂的局部变量,而这些变量在遇到堆栈操作(如
DUP
)时并不存在,它们的语义在Java中无法表达。您依赖反编译器输出以推断int case++i正在变成i++;这并不是真正发生的事情——当javac编译它们时,它将发出多个字节码;有些与处理v
i = Integer.valueOf(i.intValue() + 1);
Integer i;
for( i = 0; i < 100; i++) {
   System.out.println(i);
}
System.out.println(i); // what is the value of `i` here?
public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=2, locals=2, args_size=1
     0: iconst_0
     1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
     4: astore_1
     5: aload_1
     6: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
     9: bipush        100
    11: if_icmpge     34
    14: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
    17: aload_1
    18: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
    21: aload_1
    22: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
    25: iconst_1
    26: iadd
    27: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    30: astore_1
    31: goto          5
    34: return