文档章节

你真的了解For循环吗?一道For循环Java面试题引发的思考

路上有你0314
 路上有你0314
发布于 2017/10/23 11:51
字数 1050
阅读 2023
收藏 35

疑问

最近群友抛出了一个面试题,就是下图中的第二题,是关于一个for循环的执行结果的问题,他的代码的执行结果是什么呢? 

代码复现

下面的例子和面试题上面的大同小异,是个非常简单的例子。首先这个代码是可以编译通过的,也可以正常执行的。那么执行结果是什么呢?会跟我们猜想的一样吗?

/**
 * 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循环执行流程

 

总结

从反编译文件以及流程图中我们可以看出for循环执行的顺序是:

  • testA(a)
  • testA('b')
  • testA('d')
  • testA('c')
  • testA('b')
  • testA('d')
  • testA('c')
  • testA('b')

所以我们的执行输出结果是:abdcbdcb

最后

不对之处还望大家指正。

作者 ricky

交流群:244930845

© 著作权归作者所有

上一篇: JAVA9琐碎特性
下一篇: JVM基础命令
路上有你0314
粉丝 50
博文 22
码字总数 45074
作品 0
郑州
程序员
私信 提问
加载中

评论(32)

n
ny_kylin
for(表达式1;表达式2;表达式3){
循环语句
}
首先执行表达式1,一般是进行变量初始化操作,然后执行表达式2,即对循环条件进行判断,如果结果为真,则执行循环体;循环体执行完毕后,执行表达式3,改变循环变量的值,再次执行表达式2;结果为真,继续循环;如果结果为假,则终止循环,执行后面的语句。
贤狼罗兰斯
贤狼罗兰斯
为啥不直接复习一下for循环的定义?
Y
YingjieZh

引用来自“章某人丶”的评论

博主的探究精神令我敬佩,但这个问题感觉有点小题大做了,对于这种for循环,首先执行声明,再判断,再循环体,再迭代,再判断。。。。。就是如此。不过,借此来学习jvm还是很不错的话说。

引用来自“路上有你0314”的评论

😳 我没想到其他的验证方式,你要知道其他的验证方式 可否告知下
我说的只是for的规则而已,要验证还得看编译代码。
钟元
钟元
...首先吐槽下return竟然R大写,肯定不是个好程序猿,因为一般的程序猿都是有强迫症的。第二 如果不是编译错误的话肯定选A 首先i=0,i<2 那么i=0,1执行,最终肯定是执行到i=2的时候跳出循环,那么之前的foo('B')肯定是打印最后一个,选项中只有A最后一个打印的是B
kppom
kppom
坏了坏了,刚开始是按照伪码写的,有个goto忘了删掉了
kppom
kppom
把main函数改写成以下的形式:
testA('a');
while(true)
{
if(testA('b')&&(i<2))
{
i++;
testA('d');
testA('c');
goto point;
}
else
return;
}
就可以轻松的被理解。告诉小白这两段代码等价,死背硬记下来,背不下来的期末无条件挂科。
微笑兔
微笑兔
让我看着题的话 我肯定想不到这么深 按我的想法 我会写一个简单的 for(i=0 ; i< 10 ; i++) 来对比,下面说的可能是错的,首先在出事花的时候 i=0肯定是要被优先加载的,作为循环的开始,i<10作为循环的判断条件,而i++并不是立马执行,而是再循环体执行完后,在执行不然会影响 i 的值;而循环开始后,i = 0 ,不在被调用,只会判断i < 10,确定循环是否结束,i++记录结束值,那么这题就很好看出答案了,上升到jvm层,菜鸡的我很难理解。
捉鬼大帝
捉鬼大帝
用得着反编译吗?
int i=0;
foo('A') //true
foo('B')/*true,执行后面*/&&i<2 //true
i++//i+1
foo('D')//true
foo('C')//true
foo('B')/*true,执行后面*/&&i<2 //true
i++//i+1
foo('D')//true
foo('C')//true
foo('B')//false,后面i<2都不用执行,直接跳出循环
路上有你0314
路上有你0314 博主

引用来自“章某人丶”的评论

博主的探究精神令我敬佩,但这个问题感觉有点小题大做了,对于这种for循环,首先执行声明,再判断,再循环体,再迭代,再判断。。。。。就是如此。不过,借此来学习jvm还是很不错的话说。
😳 我没想到其他的验证方式,你要知道其他的验证方式 可否告知下
我不说话
我不说话

引用来自“xjwmore”的评论

编译错误,面试题中的foo函数的Return错了。

引用来自“路上有你0314”的评论

你要是揪它大小写的问题 它这套面试题全部编译失败了😂
程序猿就是要严谨。编译错误
提给程序员和开发者的 10 道 Java 泛型面试题

关于泛型的面试题在 Java面试中变得越来越常见,因为 Java 5问世已经有相当长的时间了,越来越多的应用已经迁移到Java 5上来了,并且几乎所有新的Java开发工作也都是在Tiger(Java 5的项目代号...

lwei
2013/10/18
13.6K
30
你真的了解JAVA的形参和实参吗?

前几天在头条上看到一道经典面试题,引发了一些思考。也是写这篇文章的导火索。 背景 请看题: 看到这个题后 瞬间觉得有坑。也觉得为什么要书写一个 方法呢?如下实现不是更简单: 输出: 完美实...

技术小能手
2018/09/19
0
0
一道数组面试题-不能使用辅助空间找重复次数的数

今天小弟去面试一个java后台职位。期间遇到了笔试题了。老实说,都很久没有做过笔试题了,之前找工作都是朋友推荐,过去聊聊技术的。今天遇到面试题,我就知道了会回答的不好的了。毕竟做面试...

浪子一号
2013/10/17
2.8K
14
金九银十,史上最强 Java 面试题整理。

以下会重新整理所有 Java 系列面试题答案、及各大互联网公司的面试经验,会从以下几个方面汇总,本文会长期更新。 Java 面试篇 史上最全 Java 面试题,带全部答案 史上最全 69 道 Spring 面试...

Java技术栈
2018/09/13
0
0
Java线程面试题 Top 50

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎。大多数待遇丰厚的Java开发职位都要求开发者...

loda0128
2015/05/29
995
1

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
今天
6
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
5
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
15
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
15
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部