关于Java4种自增方法的不同

关于Java4种自增方法的不同

三页半 Lv2

4种自增的方法

  • i++
  • ++i
  • i+=1
  • i=i+1

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class HelloWorld{

public static void main(String[] args){
method1();// i=i+1
method2();// i+=1
method3();// i++
method4();// ++i
}

public static void method1(){
int i = 0;
System.out.println(i = i + 1);
}

public static void method2(){
int i = 0;
System.out.println(i += 1);
}

public static void method3(){
int i = 0;
System.out.println(i++);
}

public static void method4(){
int i = 0;
System.out.println(++i);
}
}

运行结果

1
2
3
4
1
1
0
1

解释说明

  • 第一种i=i+1:首先=的优先级小于+,所以该表达式会首先进行加法操作,然后再进行赋值操作
  • 第二种i+=1:第一种方法的优化版本?;
  • 第三种i++:先参与操作,后自增
  • 第四种++i:先自增,后参与操作

以上都是大家都熟知的想法

从字节码的角度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method method1:()V
3: invokestatic #3 // Method method2:()V
6: invokestatic #4 // Method method3:()V
9: invokestatic #5 // Method method4:()V
12: return

public static void method1();
Code:
0: iconst_0
1: istore_0
2: iload_0
3: iconst_1
4: iadd
5: istore_0
6: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
9: iload_0
10: invokevirtual #7 // Method java/io/PrintStream.println:(I)V
13: return

public static void method2();
Code:
0: iconst_0
1: istore_0
2: iinc 0, 1
5: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
8: iload_0
9: invokevirtual #7 // Method java/io/PrintStream.println:(I)V
12: return

public static void method3();
Code:
0: iconst_0
1: istore_0
2: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
5: iload_0
6: iinc 0, 1
9: invokevirtual #7 // Method java/io/PrintStream.println:(I)V
12: return

public static void method4();
Code:
0: iconst_0
1: istore_0
2: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
5: iinc 0, 1
8: iload_0
9: invokevirtual #7 // Method java/io/PrintStream.println:(I)V
12: return
}
指令码操作码(助记符)操作数描述(栈指操作数栈)
0x03iconst_00(int)值入栈
0x04iconst_11(int)值入栈
0x1aiload_0从局部变量0中装载int类型值入栈
0x3bistore_0将栈顶int类型值保存到局部变量0中
0x60iadd将栈顶两int类型数相加,结果入栈
0xb2getstaticindexbyte1 indexbyte2获取静态字段的值
0x84iincindexbyte constbyte将整数值constbyte加到indexbyte指定的int类型的局部变量中
0xb8invokestaticindexbyte1 indexbyte2调用静态方法
0xb6invokevirtualindexbyte1 indexbyte2运行时方法绑定调用方法

method1

解释一下操作符:

  • 将常量0压入操作数栈
  • 将栈顶的int类型的数保存到局部变量表的第0个索引的位置
  • 从局部变量表的第0个索引的位置,将整数压入栈中
  • 将常量1压入操作数栈
  • 将栈顶的两个int类型数相加,将结果1压入操作数栈
  • 将结果1存储到局部变量的第0个索引的位置
  • 获取静态字段PrintStream的值
  • 将局部变量表第0个索引位置的值压入栈
  • 调用实例方法print输出结果
  • 方法结束返回

method2 method3 method4

我们可以看到剩下的三个方法的操作码是一致的,唯一不同的是操作码的执行顺序。而且多了一个新指令,该指令之后带有2个数值,分别是0和1,第一个参数是局部变量表的索引值,也就是说局部变量表第0索引处存放着一个int类型的值,第二个参数就是自增的大小,将索引0处的int值加1,得到结果。注意,这个操作是在局部变量表上操作的,而不是在操作数栈上。

这时候我们看一下i++++i的操作码,i++是先将局部变量表0处的值载入到操作数栈,然后才将局部变量表0处的值加1,接下来调用方法print的时候输出的是操作数栈的值,此时值为0,所以最终输出的结果就如我们所知一致,输出0。

++i的话就正好与i++相反先加1,然后将值载入操作数栈,再调用print方法输出,得到的结果当然是1。

总结

其实,但从操作码来看,无论是i++还是++i还是i+=1的操作码的个数都是比i=i+1少的,这样是不是可以说,前者的运行效率会高于后者呢?

  • Title: 关于Java4种自增方法的不同
  • Author: 三页半
  • Created at : 2018-10-10 00:00:00
  • Updated at : 2024-08-14 14:54:39
  • Link: https://smallclover.github.io/2018/10/10/2018-10-10-Java自增方法的解析/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments