介绍
最近在B站看面试题,碰到了这么一道题目1(参考 提供了链接),虽然感觉出题人是憨憨,题目出得没啥意思,转念一想,既然看到了并且花了好些时间才弄明白,那就顺便水一篇博客。
题目如下:
public class Main {
public static void main(String[] args) {
int i = 1;
i = i++;
int j = i++;
int k = i + ++i * i++;
System.out.println("i=" + i);
System.out.println("j=" + j);
System.out.println("k=" + k);
}
}
给出程序运行结果。参考所提供的链接视频中给出了答题思路,但是我没听清楚,本文展示我的分析过程。
首先,对生成的class文件进行反汇编,同时保留额外的编译信息:
javap -v Main.class
Classfile /D:/Private/Code/Java/Hello/target/classes/top/hellooooo/interview/Main.class
Last modified 26 Oct 2020; size 1170 bytes
MD5 checksum c05c1e0da276f3927cba38f6d89d57f7
Compiled from "Main.java"
public class top.hellooooo.interview.Main
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #7 // top/hellooooo/interview/Main
super_class: #8 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 3
Constant pool:
#1 = Methodref #8.#27 // java/lang/Object."<init>":()V
#2 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
#3 = InvokeDynamic #0:#33 // #0:makeConcatWithConstants:(I)Ljava/lang/String;
#4 = Methodref #34.#35 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = InvokeDynamic #1:#33 // #1:makeConcatWithConstants:(I)Ljava/lang/String;
#6 = InvokeDynamic #2:#33 // #2:makeConcatWithConstants:(I)Ljava/lang/String;
#7 = Class #38 // top/hellooooo/interview/Main
#8 = Class #39 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 Ltop/hellooooo/interview/Main;
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 args
#19 = Utf8 [Ljava/lang/String;
#20 = Utf8 i
#21 = Utf8 I
#22 = Utf8 j
#23 = Utf8 k
#24 = Utf8 MethodParameters
#25 = Utf8 SourceFile
#26 = Utf8 Main.java
#27 = NameAndType #9:#10 // "<init>":()V
#28 = Class #40 // java/lang/System
#29 = NameAndType #41:#42 // out:Ljava/io/PrintStream;
#30 = Utf8 BootstrapMethods
#31 = MethodHandle 6:#43 // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/in
voke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite
;
#32 = String #44 // i=\u0001
#33 = NameAndType #45:#46 // makeConcatWithConstants:(I)Ljava/lang/String;
#34 = Class #47 // java/io/PrintStream
#35 = NameAndType #48:#49 // println:(Ljava/lang/String;)V
#36 = String #50 // j=\u0001
#37 = String #51 // k=\u0001
#38 = Utf8 top/hellooooo/interview/Main
#39 = Utf8 java/lang/Object
#40 = Utf8 java/lang/System
#41 = Utf8 out
#42 = Utf8 Ljava/io/PrintStream;
#43 = Methodref #52.#53 // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandle
s$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#44 = Utf8 i=\u0001
#45 = Utf8 makeConcatWithConstants
#46 = Utf8 (I)Ljava/lang/String;
#47 = Utf8 java/io/PrintStream
#48 = Utf8 println
#49 = Utf8 (Ljava/lang/String;)V
#50 = Utf8 j=\u0001
#51 = Utf8 k=\u0001
#52 = Class #54 // java/lang/invoke/StringConcatFactory
#53 = NameAndType #45:#58 // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang
/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#54 = Utf8 java/lang/invoke/StringConcatFactory
#55 = Class #60 // java/lang/invoke/MethodHandles$Lookup
#56 = Utf8 Lookup
#57 = Utf8 InnerClasses
#58 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljav
a/lang/Object;)Ljava/lang/invoke/CallSite;
#59 = Class #61 // java/lang/invoke/MethodHandles
#60 = Utf8 java/lang/invoke/MethodHandles$Lookup
#61 = Utf8 java/lang/invoke/MethodHandles
{
public top.hellooooo.interview.Main();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 8: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Ltop/hellooooo/interview/Main;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=4, args_size=1
0: iconst_1
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: iload_1
8: iinc 1, 1
11: istore_2
12: iload_1
13: iinc 1, 1
16: iload_1
17: iload_1
18: iinc 1, 1
21: imul
22: iadd
23: istore_3
24: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
27: iload_1
28: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
33: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
39: iload_2
40: invokedynamic #5, 0 // InvokeDynamic #1:makeConcatWithConstants:(I)Ljava/lang/String;
45: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
48: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
51: iload_3
52: invokedynamic #6, 0 // InvokeDynamic #2:makeConcatWithConstants:(I)Ljava/lang/String;
57: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
60: return
LineNumberTable:
line 10: 0
line 11: 2
line 12: 7
line 13: 12
line 14: 24
line 15: 36
line 16: 48
line 17: 60
LocalVariableTable:
Start Length Slot Name Signature
0 61 0 args [Ljava/lang/String;
2 59 1 i I
12 49 2 j I
24 37 3 k I
MethodParameters:
Name Flags
args
}
SourceFile: "Main.java"
InnerClasses:
public static final #56= #55 of #59; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #31 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang
/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#32 i=\u0001
1: #31 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang
/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#36 j=\u0001
2: #31 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang
/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#37 k=\u0001
取出main方法逻辑:
0: iconst_1
1: istore_1
2: iload_1
3: iinc 1, 1
6: istore_1
7: iload_1
8: iinc 1, 1
11: istore_2
12: iload_1
13: iinc 1, 1
16: iload_1
17: iload_1
18: iinc 1, 1
21: imul
22: iadd
23: istore_3
JVM指令
instruction | description |
---|---|
iconst_1 | 将int型1推送至栈顶 |
istore_1 | 将栈顶int型数值存入第二个本地变量 |
iload_1 | 将第二个int型本地变量推送至栈顶 |
iinc | 把一个常量值加到一个int类型的局部变量上 |
istore_2 | 将栈顶int型数值存入第三个本地变量 |
imul | 将栈顶两int型数值相乘并将结果压入栈顶 |
iadd | 将栈顶两int型数值相加并将结果压入栈顶 |
istore_3 | 将栈顶int型数值存入第四个本地变量 |
解析
本题主要涉及JVM Frame的Local Variables以及Operand Stacks(可看The Java® VirtualMachine Specification 2.6.1以及2.6.2)
Operation | LocalVariables(index:value) | OperandStack(top->buttom) |
---|---|---|
0: iconst_1 | 1 | |
1: istore_1 | 2:1(取出栈顶元素存入本地变量表) | null |
2: iload_1 | 2:1 | 1 |
3: iinc 1, 1 | 2:2 | 1 |
6: istore_1 | 2:1 | null |
7: iload_1 | 2:1 | 1 |
8: iinc 1, 1 | 2:2 | 1 |
11: istore_2 | 2:2 3:1 | null |
12: iload_1 | 2:2 3:1 | 2 |
13: iinc 1, 1 | 2:3 3:1 | |
16: iload_1 | 3 2 | |
17: iload_1 | 3 3 2 | |
18: iinc 1, 1 | 2:4 3:1 | |
21: imul | 9 2 | |
22: iadd | 11 | |
23: istore_3 | 2:4 3:1 4:11 | null |
所以最后输出的结果为:4 1 11
以上仅我一人看法,如发现错误烦请留言帮忙指出。
小结
这个小结是从视频结尾看到的,感觉非常不错,所以转载过来.视频就是参考1
- =右边的从左到右加载值一次压入操作数栈
- 自增,自减操作都是直接修改变量的值,不经过操作数栈
- 最后的赋值之前,临时结果也是存储在操作数栈中