引入
我们先看一个小的代码:
int i = 0 ;
int j = i ++ ;
System.out.println( "i = " + i + ","+ "j = " + j);
// 再看这个
int i= 0 ;
i = i ++ ;
System.out.println( "i = " + i );
// Console
i = 1,j = 0
i = 0
Process finished with exit code 0
笔者一开始认为下面的输出也是
i = 1
毕竟不管有没有赋值给 i , i++操作都对 i 做了++ 操作,可是现实总是残酷的对待笔者。虽然接触Java没那么长就,但好歹也是我大学的最好时光的终结者。现在对于这个问题的分析,一开始想要Debug的,后来发现,特么的,怎么Debug?所以就直接看 .class文件。
i++ 与 ++i
在分析 i++ 之前,我们经常遇到 ++i 的问题。这里笔者想说 ,我们初学者就记住一句话 ,i ++ 先赋值再运算 , ++ 先运算在赋值。因此呢,在我们的程序中,遇到了 i++ 与 ++i的单飞,都是一样的,大家都 ++ 了 , 也就没啥子区别了。这也是为什么在 for循环里面无论是 i++ ,还是 ++ i都可以的原因 , 因为到最后 i 都加了 , 这就够了。
但是仅限初学者啊,因为这个解释是最简单的,也是最直观的解释,对于其他的,还是关注一下底层的原理实现还是比较好的。
可以这么说 , 上面的 i++ 的先赋值在运算可以解决90%的问题,但是唯有那10%有点解释不通。所以我们还需要换种方式对他做介绍。之所以我们在初学的时候用上面哪种方式介绍,是因为,换种方式介绍你还回学Java吗?
从.class关注 i++ 问题
.class文件是什么
在我们一开始写HelloWorld.java的时候,我们都知道java文件在编译的时候会产生一个.class的字节码文件,他其实就是一个二进制文件,也正是因为它的存在,我们才能实现Java跨平台的交易。我们在打开这个.class文件
public Operate() {
}
public static void main(String[] args) {
int i = 0;
byte var10000 = i;
int i = i + 1;
int j = var10000;
System.out.println("i = " + i + ",j = " + j);
i = 0;
var10000 = i;
i = i + 1;
i = var10000;
System.out.println("i = " + i);
}
很遗憾,我们看到的不是二进制文件,而是经过反编译的后的文件。
i++分析
我们直接看下面的,那个折磨笔者 i = i++ 问题。
1、初始化 i= 0 操作,都没有意见吧
2、这里呢,它们引入了一个第三方变量,也就是所谓二点第三者插足,将 i 值赋给 var10000 , 此时的 var10000 = 0 ;
3、现在开始对 i 做 ++ 操作 , 即 i = i + 1 -> i = 1 ;
4、第三者又回来了,将var10000的值又给了 i 。所以 随后的输出的 i = 0 .
那现在就知道了,不是我们没有做 ++ 操作 , i 的 值也的确变成了 1 , 但是很遗憾的是因为第三者的存在,它跟第三者跑了。我终于知道为啥小三这么可恶了,程序人生程序人生呀。
字节码分析
D:\development_tools\java\jdk1.8.0_201\bin\javap.exe -c com.test.Operate
Compiled from "Operate.java"
public class com.test.Operate {
public com.test.Operate();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: new #3 // class java/lang/StringBuilder
13: dup
14: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
17: ldc #5 // String i =
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_1
23: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: return
}
Process finished with exit code 0
这里的分析就要涉及到内存的分析了。笔者还不熟悉这一块的内容,后续再补上。