看一些JAVA基础题的时候,经常看到这个问题,很多人的解释是:i++最快,i+=1其次,i=i+1最慢。下面通过Sun JDK编译出来的字节码验证一下这个问题。
为了让编译出来的字节码便于阅读,将这三个表达式分别写在一个方法里面:
1 private void a(){ 2 int i=0; 3 i += 1; 4 } 5 6 private void b(){ 7 int i=0; 8 i++; 9 } 10 11 private void c(){ 12 int i=0; 13 i=i+1; 14 }
编译之后(SUN JDK1.7),再看编译出来的字节码(直接把.class文件用IDE打开,或者用javap -v -p classFileName命令):
// Method descriptor #6 ()V // Stack: 1, Locals: 2 private void a(); 0 iconst_0 1 istore_1 [i] 2 iinc 1 1 [i] 5 return Line numbers: [pc: 0, line: 15] [pc: 2, line: 16] [pc: 5, line: 17] Local variable table: [pc: 0, pc: 6] local: this index: 0 type: com.leo.javabasis.Add [pc: 2, pc: 6] local: i index: 1 type: int // Method descriptor #6 ()V // Stack: 1, Locals: 2 private void b(); 0 iconst_0 1 istore_1 [i] 2 iinc 1 1 [i] 5 return Line numbers: [pc: 0, line: 20] [pc: 2, line: 21] [pc: 5, line: 22] Local variable table: [pc: 0, pc: 6] local: this index: 0 type: com.leo.javabasis.Add [pc: 2, pc: 6] local: i index: 1 type: int // Method descriptor #6 ()V // Stack: 1, Locals: 2 private void c(); 0 iconst_0 1 istore_1 [i] 2 iinc 1 1 [i] 5 return Line numbers: [pc: 0, line: 25] [pc: 2, line: 26] [pc: 5, line: 27] Local variable table: [pc: 0, pc: 6] local: this index: 0 type: com.leo.javabasis.Add [pc: 2, pc: 6] local: i index: 1 type: int
这三个方法的字节码是一样的(除了行号),先稍微解释一下这段字节码指令:
iconst_0 //整数常量0入栈
istore_1 //将栈上的值出栈,保存到序号为1的局部变量(也就是i)
iinc 1 1 //将序号为1的局部变量增加1,此指令语法为iinc index const。
是不是SUN JDK在某一个版本之前是没有对这个问题进行优化?找了一个能用的SUN JDK 1.3再试了一下(1.1, 1.2跑不动了),编译出来的结果,方法a、b的字节码和1.7下是一样的。但是c的就不一样了:
Method void c() 0 iconst_0 1 istore_1 2 iload_1 3 iconst_1 4 iadd 5 istore_1 6 return
指令行2-5的作用分别是:
iload_1 //将序号为1的局部整数变量入栈
iconst_1 //整数常量1入栈
iadd //使用栈上的两个整数值出栈进行加法运算,将结果入栈
istore_1 //将栈上的整数值保存到序号为1的局部变量
这种方式同样达到了相加的目的,但是相比直接iinc指令,还是绕了很多,效率自然不会好。
由此看来,在某一个版本之后,编译器是对这个问题进行了优化。关于谁的效率好的问题,在现在的SUN JDK版本中已经没有差别了(即使是使用1.7的JDK编译出的1.3JRE兼容的字节码,方法c也是优化过的),
但是曾经某个时代确实是有差别的。