关于Java4种自增方法的不同
4种自增的方法
- i++
- ++i
- i+=1
- i=i+1
示例代码
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
1
0
1
解释说明
- 第一种
i=i+1
:首先=
的优先级小于+
,所以该表达式会首先进行加法操作,然后再进行赋值操作 - 第二种
i+=1
:第一种方法的优化版本?; - 第三种
i++
:先参与操作,后自增 - 第四种
++i
:先自增,后参与操作
以上都是大家都熟知的想法
从字节码的角度
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
}
| 指令码 | 操作码(助记符)| 操作数 | 描述(栈指操作数栈)| | —- | —- | —- | —- | | 0x03 | iconst_0 | 无 | 0(int)值入栈 | | 0x04 | iconst_1 | 无 | 1(int)值入栈 | | 0x1a | iload_0 | 无 | 从局部变量0中装载int类型值入栈 | | 0x3b | istore_0 | 无 | 将栈顶int类型值保存到局部变量0中 | | 0x60 | iadd | 无 | 将栈顶两int类型数相加,结果入栈 | | 0xb2 | getstatic | indexbyte1 indexbyte2 | 获取静态字段的值 | | 0x84 | iinc | indexbyte constbyte | 将整数值constbyte加到indexbyte指定的int类型的局部变量中 | | 0xb8 | invokestatic | indexbyte1 indexbyte2 | 调用静态方法 | | 0xb6 | invokevirtual | indexbyte1 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少的,这样是不是可以说,前者的运行效率会高于后者呢?