直接看下面的两段代码:[Java] 纯文本查看 复制代码 public static void main(String[] args) {
int i = 1;
i = i++;
System.out.println("i=" + i);
}
输出结果:
[Java] 纯文本查看 复制代码 public static void main(String[] args) {
int i = 1;
int b = 0;
b = i++;
System.out.println("i=" + i);
}
输出结果:
为什么会出现这种问题呢?
我们需要分析下他的代码。我说的代码并不是java代码,而是在jvm中运行的代码也就是class文件。但是我们无法阅读class文件,就需要用java的一个命令来反编译
这个命令就是 javap -c
编译完成后我们查看代码:
[AppleScript] 纯文本查看 复制代码 public static void main(java.lang.String[]);
Code:
0: iconst_1 //常量1入栈
1: istore_1 //将栈顶出栈,即i=1;
2: iload_1 //复制i变量的值入栈
3: iinc 1, 1 //局部变量的值+1
6: istore_1 //将栈顶出栈,即i=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 //复制i变量的值入栈 这时i=1???还是2????
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
}
看完这个我们就需要大概了解下代码里出现的命令了。
iconst_n :定义常量0,在字节码里,-1~5可以表示为iconst_n形式,也就是用常量来表示,不在这个范围的数,则要用bipush n或sipush n来表示,也就是将数存入栈中
istore_n :指令可以从栈顶取一个整形数据存储在局部变量区中,n代表在局部变量区中的位置,只能取0,1,2,3,其它的数,则要用istore指令
这两个比较常见
下面附一张表格
| | | | | Loads the int value 0 onto the stack
| | | Store int value into variable 1
| | | Store int value into variable 2
| | | Increment local variable #index by signed byte const
| | | Loads an int value from variable 1
| | | |
为了搞清楚这个问题 我们一行一行的读下代码
[Java] 纯文本查看 复制代码
java代码:
public static void main(String[] args) {
int i = 1;
i = i++;
System.out.println("i=" + i);
}
class反编译代码:
0: iconst_1 //常量1入栈
1: istore_1 //将栈顶出栈,即i=1;
//前两句的意思就是 i=1
2: iload_1 //复制i变量的值入栈
3: iinc 1, 1 //局部变量的值+1(注意这里是局部变量的值,栈中的值没有改变还是1)
6: istore_1 //将栈顶出栈,即i=1;
//这里其实执行的是i = i++;这句话
//首先把 i的值入栈然后 执行i++ 最后把i入栈的值取出来
//这里我们要注意 入栈时i的值为1 执行i++(即iinc之后
//栈中的值没有改变,改变的只是变量的值。
//这之后又把栈中的值赋值给变量所以最后i的值还是为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 //复制i变量的值入栈 这时i=1 所以最后输出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
我们再来分析
[Java] 纯文本查看 复制代码 java代码:
public static void main(String[] args) {
int i = 1;
int b = 0;
b = i++;
System.out.println("i=" + i);
}
class反编译代码:
0: iconst_1 //常量1入栈
1: istore_1 //将栈顶出栈,即i=1;
//前两句的意思就是 i=1
2: iconst_0 //常量0入栈
3: istore_2 //将栈顶出栈,即b=0;
//这两句的意思就是 b=0
4: iload_1 //复制i变量的值入栈
5: iinc 1, 1 //局部变量的值+1
8: istore_2 //将栈顶出栈,即b=1;
//这里其实执行的是b = i++;这句话
//首先把 i的值入栈然后 执行i++ 最后把i入栈的值取出来
//这里我们要注意 入栈时i的值为1 执行i++(即iinc之后
//栈中的值没有改变,改变的只是变量的值。
//这之后又把栈中的值赋值给变量b所以最后b的值为1,变量i的值为2)
9: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
12: new #3 // class java/lang/StringBuilder
15: dup
16: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
19: ldc #5 // String i=
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: iload_1 //复制i变量的值入栈 i=2
25: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
28: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
34: return
至此 我们就可以清楚地知道问题的答案了。
|