你真的了解For循环吗?一道For循环Java面试题引发的思考
疑问
代码复现
下面的例子和面试题上面的大同小异,是个非常简单的例子。首先这个代码是可以编译通过的,也可以正常执行的。那么执行结果是什么呢?会跟我们猜想的一样吗?
/**
* Created by baiguantao on 2017/10/20.
*/
public class T {
public static boolean testA(char a){
System.out.print(a);
return true;
}
/**
* for循环的一些疑问
* @param args
*/
public static void main(String[] args) {
int i=0;
for (testA('a');testA('b')&&(i<2);testA('c')) {
i++;
testA('d');
}
}
}
- 执行结果
abdcbdcb
那么问题来了,为什么是这个结果呢?我们可以借助javap命令反编译我们刚才编译的T.class进行分析。 如果对jvm不了解的可以参阅JVM基础。
反编译
- 先贴出原版的字节码反编译后的代码,后边会对反编译的文件进行逐行解析,那么我们先来看看上述类反编译后的样子吧。如下所示:
C:\Users\temp\IdeaProjects\mix_learn\target\classes>javap -c T.class
Compiled from "T.java"
public class T {
public T();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static boolean testA(char);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iload_0
4: invokevirtual #3 // Method java/io/PrintStream.print:(C)V
7: iconst_1
8: ireturn
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: bipush 97
4: invokestatic #4 // Method testA:(C)Z
7: pop
8: bipush 98
10: invokestatic #4 // Method testA:(C)Z
13: ifeq 39
16: iload_1
17: iconst_2
18: if_icmpge 39
21: iinc 1, 1
24: bipush 100
26: invokestatic #4 // Method testA:(C)Z
29: pop
30: bipush 99
32: invokestatic #4 // Method testA:(C)Z
35: pop
36: goto 8
39: return
}
- 说明版本
对反编译后的文件是不是一脸懵逼,没太看懂是什么意思呢?没关系,下面我们进行逐行分析。
C:\Users\temp\IdeaProjects\mix_learn\target\classes>javap -c T.class
Compiled from "T.java"
public class T {
public T(); // 这里是默认生成的无参构造函数部分开始
Code:
0: aload_0 //表示对this的操作
1: invokespecial #1 // Method java/lang/Object."<init>":()V 调用特殊实例方法
4: return // 返回结果
// 这里是默认生成的无参构造函数部分结束
public static boolean testA(char);// 这里是我们写入的静态方法
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; System.out调用类方法
3: iload_0 //从局部变量表中加载int型的数据到操作数栈
4: invokevirtual #3 // Method java/io/PrintStream.print:(C)V 调用实例方法
7: iconst_1 //int类型0进栈
8: ireturn // 返回结果
public static void main(java.lang.String[]);
Code:
0: iconst_0 //int类型0进栈
1: istore_1 // int类型1出栈
2: bipush 97 // byte型常量97(a)进栈
4: invokestatic #4 // Method testA:(C)Z 执行静态方法testA
7: pop // 栈顶数值出栈(不能是long/double)
8: bipush 98 // byte型常量98(b)进栈
10: invokestatic #4 // Method testA:(C)Z 执行静态方法testA
13: ifeq 39 //判断语句 是否相等 循环结束 跳转到39
16: iload_1 //从局部变量表中加载int型的数据到操作数栈
17: iconst_2 //int类型2进栈
18: if_icmpge 39 //比较栈顶两int型数值大小,当结果大于等于0时跳转到39的位置
21: iinc 1, 1 //给局部变量表的1号位置的int值增加1
24: bipush 100 // byte型常量100(d)进栈
26: invokestatic #4 // Method testA:(C)Z 执行静态方法testA
29: pop // 栈顶数值出栈(不能是long/double)
30: bipush 99 // byte型常量99(c)进栈
32: invokestatic #4 // Method testA:(C)Z 执行静态方法testA
35: pop // 栈顶数值出栈(不能是long/double)
36: goto 8 // 重新循环 到8的位置
39: return //退出循环
}
- 流程图
总结
从反编译文件以及流程图中我们可以看出for循环执行的顺序是:
- testA(a)
- testA('b')
- testA('d')
- testA('c')
- testA('b')
- testA('d')
- testA('c')
- testA('b')
所以我们的执行输出结果是:abdcbdcb
最后
不对之处还望大家指正。
作者 ricky
交流群:244930845