算是温习吧。问题比较基础,但是也比较重要。(虽然是C,但是很经典)

for循环可以说在每个程序中都少不了的,语句头包括三个部分:初始化,判读条件,一个表达式。

但是这三个部分的执行顺序是什么,这是我们所关心的,也是本文所要讨论的。

下面先看一段简单的代码。

[cpp] view plaincopy

 
  1. #include <stdio.h>
  2. int main()
  3. {
  4. int x, a;
  5. for (x=0,a=0; a<=1 && !x++; a++)
  6. {
  7. a++;
  8. }
  9. printf("%d", a);
  10. return 0;
  11. }

代码很简单,可是打印结果呢?

有的朋友可能会想当然的认为,  打印结果是1.

可是,真的是1吗?   复制到编译器上跑跑,奇迹般的发现,其实是2.

下面就分析下这简单代码。

第一步:初始化定义,x = 0, a = 0;

第二步:a小于1, x 的非为1,符合循环条件

第三步:x++后x自增为1.

第四步:进入循环体,a++,a自增为1.

第五步:执行循环语句头中的a++,a自增为2.

第六步:执行循环语句头的判读条件,a=2>1,故不满足,不执行循环语句。

第七步:打印a的值为2.

怎么样,现在豁然开朗了吧。(如果还是不懂...那就抱歉了。)

从这个例子,我们可以得出如下结论:

执行顺序是:初始化--判断--函数体——然后for循环的第3句--判断——函数体---for循环的第三句--判断……

下面解答几点疑惑:

1、第一次例如初始化i=0,是否还执行后面的i++或++i?

第一次先初始化,然后判断,i++就不执行了

2、第一次完了以后,初始化这句就不用了,是先执行i++还是限制性判断语句呢?

先执行i++

3、现在知道是先执行i++,再执行判断语句,i++不是后++吗,例如i为1,那i++后执行判断语句,i是几呢?

i是2.

4、好像i++和++i在for()里时结果都一样。

都一样,for语句头的执行顺序决定了这两个写法都一样,都是i++或这++i这两句执行完了再进行判断,此时(两种情况)i已经加1了。

分析了这么多,相信大家也都懂了。

留个经典的例子,让大家自己去分析吧。

[cpp] view
plain
copy

 
  1. #include <stdio.h>
  2. int main( )
  3. {
  4. int i = 0, j = 10;
  5. for(i=0; i<j; i++,j--)
  6. {
  7. if(j==10)
  8. {
  9. printf("%d,%d ",i,j);
  10. j--;
  11. }
  12. j++;
  13. }
  14. printf("%d,%d ",i,j);
  15. return 0;
  16. }

打印结果:

0,10 9,9 

如果不懂的,可以参照下面这个对上述例子逐句打印的例子,应该会有助于理解。

[cpp] view
plain
copy

 
  1. #include <stdio.h>
  2. int print1(int i,int j)
  3. {
  4. printf("执行判断条件,i=%d,j=%d \n",i,j);
  5. return 1;
  6. }
  7. int main( )
  8. {
  9. int i = 0, j = 10;
  10. for(i=0;((print1(i,j)) && (i<j));
  11. i++,j--,printf("执行for()中的第三个语句,i=%d,j=%d \n",i,j))
  12. {
  13. if(j==10)
  14. {
  15. printf("%d,%d ",i,j);
  16. j--;
  17. }
  18. j++;
  19. printf("执行for循环体中的最后一句,i=%d,j=%d\n ",i,j);
  20. }
  21. printf(" %d,%d ",i,j);
  22. return 0;
  23. }

打印结果:

执行判断条件,i=0,j=10 

0,10 执行for循环体中的最后一句,i=0,j=10

 执行for()中的第三个语句,i=1,j=9 

执行判断条件,i=1,j=9 

执行for循环体中的最后一句,i=1,j=10

 执行for()中的第三个语句,i=2,j=9 

执行判断条件,i=2,j=9 

执行for循环体中的最后一句,i=2,j=10

 执行for()中的第三个语句,i=3,j=9 

执行判断条件,i=3,j=9 

执行for循环体中的最后一句,i=3,j=10

 执行for()中的第三个语句,i=4,j=9 

执行判断条件,i=4,j=9 

执行for循环体中的最后一句,i=4,j=10

 执行for()中的第三个语句,i=5,j=9 

执行判断条件,i=5,j=9 

执行for循环体中的最后一句,i=5,j=10

 执行for()中的第三个语句,i=6,j=9 

执行判断条件,i=6,j=9 

执行for循环体中的最后一句,i=6,j=10

 执行for()中的第三个语句,i=7,j=9 

执行判断条件,i=7,j=9 

执行for循环体中的最后一句,i=7,j=10

 执行for()中的第三个语句,i=8,j=9 

执行判断条件,i=8,j=9 

执行for循环体中的最后一句,i=8,j=10

 执行for()中的第三个语句,i=9,j=9 

执行判断条件,i=9,j=9 

 9,9 

下面讲述for循环在遍历过程中的使用

J2SE 1.5提供了另一种形式的for循环。借助这种形式的for循环,可以用更简单地方式来遍历数组和Collection等类型的对象。本文介绍使用这种循环的具体方式,说明如何自行定义能被这样遍历的类,并解释和这一机制的一些常见问题。

在Java程序中,要“逐一处理”――或者说,“遍历”――某一个数组或Collection中的元素的时候,一般会使用一个for循环来实现(当然,用其它种类的循环也不是不可以,只是不知道是因为for这个词的长度比较短,还是因为for这个词的含义和这种操作比较配,在这种时候for循环比其它循环常用得多)。

对于遍历数组,这个循环一般是采取这样的写法:

清单1:遍历数组的传统方式

/* 建立一个数组 */

int[] integers = {1, 2, 3, 4};

/* 开始遍历 */

for (int j = 0; j<integers.length; j++){

int i = integers[j];

System.out.println(i);

}

而对于遍历Collection对象,这个循环则通常是采用这样的形式

清单2:遍历Collection对象的传统方式

/* 建立一个Collection */

String[] strings = {"A", "B", "C", "D"};

Collection stringList = java.util.Arrays.asList(strings);

/* 开始遍历 */

for (Iterator itr = stringList.iterator(); itr.hasNext();) {

Object str = itr.next();

System.out.println(str);

}

而在Java语言的最新版本――J2SE 1.5中,引入了另一种形式的for循环。借助这种形式的for循环,现在可以用一种更简单地方式来进行遍历的工作。

1. 第二种for循环

不严格的说,Java的第二种for循环基本是这样的格式:

for (循环变量类型 循环变量名称 : 要被遍历的对象) 循环体

借助这种语法,遍历一个数组的操作就可以采取这样的写法:

清单3:遍历数组的简单方式

/* 建立一个数组 */

int[] integers = {1, 2, 3, 4};

/* 开始遍历 */

for (int i : integers) {

System.out.println(i);/* 依次输出“1”、“2”、“3”、“4” */

}

这里所用的for循环,会在编译期间被看成是这样的形式:

清单4:遍历数组的简单方式的等价代码

/* 建立一个数组 */

int[] integers = {1, 2, 3, 4};

/* 开始遍历 */

for (int 变量名甲 = 0; 变量名甲<integers.length; 变量名甲++)="" {

System.out.println(integers[变量名甲]);/* 依次输出“1”、“2”、“3”、“4” */

}

这里的“变量名甲”是一个由编译器自动生成的不会造成混乱的名字。

而遍历一个Collection的操作也就可以采用这样的写法:

清单5:遍历Collection的简单方式

/* 建立一个Collection */

String[] strings = {"A", "B", "C", "D"};

Collection list = java.util.Arrays.asList(strings);

/* 开始遍历 */

for (Object str : list) {

System.out.println(str);/* 依次输出“A”、“B”、“C”、“D” */

}

这里所用的for循环,则会在编译期间被看成是这样的形式:

清单6:遍历Collection的简单方式的等价代码

/* 建立一个Collection */

String[] strings = {"A", "B", "C", "D"};

Collection stringList = java.util.Arrays.asList(strings);

/* 开始遍历 */

for (Iterator 变量名乙 = list.iterator(); 变量名乙.hasNext();) {

Object str = 变量名乙.next();

System.out.println(str);/* 依次输出“A”、“B”、“C”、“D” */

}

这里的“变量名乙”也是一个由编译器自动生成的不会造成混乱的名字。

因为在编译期间,J2SE 1.5的编译器会把这种形式的for循环,看成是对应的传统形式,所以不必担心出现性能方面的问题。

不用“foreach”和“in”的原因

Java采用“for”(而不是意义更明确的“foreach”)来引导这种一般被叫做“for-each循环”的循环,并使用“:”(而不是意义更明确的“in”)来分割循环变量名称和要被遍历的对象。这样作的主要原因,是为了避免因为引入新的关键字,造成兼容性方面的问题――在Java语言中,不允许把关键字当作变量名来使用,虽然使用“foreach”这名字的情况并不是非常多,但是“in”却是一个经常用来表示输入流的名字(例如java.lang.System类里,就有一个名字叫做“in”的static属性,表示“标准输入流”)。

的确可以通过巧妙的设计语法,让关键字只在特定的上下文中有特殊的含义,来允许它们也作为普通的标识符来使用。不过这种会使语法变复杂的策略,并没有得到广泛的采用。

“for-each循环”的悠久历史

“for-each循环”并不是一个最近才出现的控制结构。在1979正式发布的Bourne shell(第一个成熟的UNIX命令解释器)里就已经包含了这种控制结构(循环用“for”和“in”来引导,循环体则用“do”和“done”来标识)。

2. 防止在循环体里修改循环变量

在默认情况下,编译器是允许在第二种for循环的循环体里,对循环变量重新赋值的。不过,因为这种做法对循环体外面的情况丝毫没有影响,又容易造成理解代码时的困难,所以一般并不推荐使用。

Java提供了一种机制,可以在编译期间就把这样的操作封杀。具体的方法,是在循环变量类型前面加上一个“final”修饰符。这样一来,在循环体里对循环变量进行赋值,就会导致一个编译错误。借助这一机制,就可以有效的杜绝有意或无意的进行“在循环体里修改循环变量”的操作了。

清单7:禁止重新赋值

int[] integers = {1, 2, 3, 4};

for (final int i : integers) {

i = i / 2; /* 编译时出错 */

}

注意,这只是禁止了对循环变量进行重新赋值。给循环变量的属性赋值,或者调用能让循环变量的内容变化的方法,是不被禁止的。

清单8:允许修改状态

Random[] randoms = new Random[]{new Random(1), new Random(2), new Random(3)};

for (final Random r : randoms) {

r.setSeed(4);/* 将所有Random对象设成使用相同的种子 */

System.out.println(r.nextLong());/* 种子相同,第一个结果也相同 */

}

3. 类型相容问题

为了保证循环变量能在每次循环开始的时候,都被安全的赋值,J2SE 1.5对循环变量的类型有一定的限制。这些限制之下,循环变量的类型可以有这样一些选择:

循环变量的类型可以和要被遍历的对象中的元素的类型相同。例如,用int型的循环变量来遍历一个int[]型的数组,用Object型的循环变量来遍历一个Collection等

清单9:使用和要被遍历的数组中的元素相同类型的循环变量

int[] integers = {1, 2, 3, 4};

for (int i : integers) {

System.out.println(i);/* 依次输出“1”、“2”、“3”、“4” */

}

清单10:使用和要被遍历的Collection中的元素相同类型的循环变量

Collection< String> strings = new ArrayList< String>();

strings.add("A");

strings.add("B");

strings.add("C");

strings.add("D");

for (String str : integers) {

System.out.println(str);/* 依次输出“A”、“B”、“C”、“D” */

}

循环变量的类型可以是要被遍历的对象中的元素的上级类型。例如,用int型的循环变量来遍历一个byte[]型的数组,用Object型的循环变量来遍历一个Collection< String>(全部元素都是String的Collection)等。

清单11:使用要被遍历的对象中的元素的上级类型的循环变量

String[] strings = {"A", "B", "C", "D"};

Collection< String> list = java.util.Arrays.asList(strings);

for (Object str : list) {

System.out.println(str);/* 依次输出“A”、“B”、“C”、“D” */

}

循环变量的类型可以和要被遍历的对象中的元素的类型之间存在能自动转换的关系。J2SE 1.5中包含了“Autoboxing/Auto-Unboxing”的机制,允许编译器在必要的时候,自动在基本类型和它们的包裹类(Wrapper Classes)之间进行转换。因此,用Integer型的循环变量来遍历一个int[]型的数组,或者用byte型的循环变量来遍历一个Collection< Byte>,也是可行的。

清单12:使用能和要被遍历的对象中的元素的类型自动转换的类型的循环变量

int[] integers = {1, 2, 3, 4};

for (Integer i : integers) {

System.out.println(i);/* 依次输出“1”、“2”、“3”、“4” */

}

注意,这里说的“元素的类型”,是由要被遍历的对象的决定的――如果它是一个Object[]型的数组,那么元素的类型就是Object,即使里面装的都是String对象也是如此。

可以限定元素类型的Collection

截至到J2SE 1.4为止,始终无法在Java程序里限定Collection中所能保存的对象的类型――它们全部被看成是最一般的Object对象。一直到J2SE 1.5中,引入了“泛型(Generics)”机制之后,这个问题才得到了解决。现在可以用Collection< T>来表示全部元素类型都是T的Collection。

(转)java for循环的执行顺序和几种常用写法的更多相关文章

  1. for循环的执行顺序

    一边回顾基础一边记录记录做个整理,这篇关于for循环的执行顺序: for(表达式1;表达式2;表达式3) {循环体} 第一步,先对表达式1赋初值; 第二步,判别表达式2是否满足给定条件,若其值为真,满 ...

  2. Java Object 构造方法的执行顺序

    Java Object 构造方法的执行顺序 @author ixenos 为了使用类而做的准备工作包括三个步骤 1)加载:类加载器查找字节码(一般在classpath中找),从字节码创建一个Class ...

  3. [转载]for循环的执行顺序

    原文地址:for循环的执行顺序作者:想飞上天的美人鱼   for循环的执行顺序用如下表达式: for(expression1;expression2;expression3) { expression ...

  4. Java多线程之控制执行顺序

    概念: 多线程在并发环境中的正常执行顺序是随机无序的,并不能按照期盼的结果输出. 因为启动一个线程时,线程并不会立即执行,而是等待CPU的资源调度,CPU能调度哪个线程,是通过多种复杂的算法计算而来. ...

  5. java中静态代码执行顺序

    1.Java中静态变量只能在类主体中定义,不能在方法中定义. 静态变量属于类所有而不属于方法. 2. 静态块:用static申明,JVM加载类时执行,仅执行一次 构造块:类中直接用{}定义,每一次创建 ...

  6. 夯实Java基础系列7:一文读懂Java 代码块和执行顺序

    目录 Java中的构造方法 构造方法简介 构造方法实例 例 1 例 2 Java中的几种构造方法详解 普通构造方法 默认构造方法 重载构造方法 java子类构造方法调用父类构造方法 Java中的代码块 ...

  7. 夯实Java基础系列7:Java 代码块和执行顺序

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  8. C/C++语言for循环语句执行顺序

    for循环如下: ; i<; ++i) { } 执行顺序如下: 1.i=0  初始化初值 2.i<10 进行判断,如果条件为真,则继续执行 3.执行循环体代码 4.i++ 变量i自增 5. ...

  9. java 方法参数的执行顺序

    java方法的参数的执行顺序是从左到右还是从右到左呢? 写出一下测试程序: 1 import java.util.*; 2 import java.io.*; 3 public class Test ...

随机推荐

  1. Java 9 揭秘(4. 模块依赖)

    文 by / 林本托 Tips 做一个终身学习的人. 在此章节中,主要学习以下内容: 如何声明模块依赖 模块的隐式可读性意味着什么以及如何声明它 限定导出(exports)与非限定导出之间的差异 声明 ...

  2. [BZOJ3675]序列分割

    3675: [Apio2014]序列分割 Time Limit: 40 Sec  Memory Limit: 128 MB Description 小H最近迷上了一个分隔序列的游戏.在这个游戏里,小H ...

  3. linux系统中的删除操作

    #rm [-fir] 文件或者目录 参数: -f:就是force的意思,忽略不存在的文件,不会出现警告信息: -i:互动模式,在删除前会询问用户是否操作: -r:递归删除.最常用的在目录删除.这是一个 ...

  4. asp.net mvc中html helper的一大优势

    刚上手这个框架,发现其中的html helper用起来很方便,让我们这些从web form 过渡来的coder有一种使用控件的快感,嘻嘻! 言归正传,我要说的是在使用它时,系统会自动执行表单的现场恢复 ...

  5. 非滤波单目视觉slam笔记1

    非滤波单目视觉slam 主要分为以下8部分 数据类型 数据关联 初始化 位姿估计 地图维护 地图生成 失效恢复 回环检测 数据类型 直接法(稠密,半稠密) 基本原理是亮度一致性约束,\(J(x,y) ...

  6. Linux回炉复习系列文章大纲

    本人最近在回炉Linux的内容,也做了很多整理,顺便也想将整理的内容分享出来. 由于该系列文章的内容主要是复习整理而来,其中绝大多数命令都是翻译和整理man或info文档总结的,另外很多地方也没有给出 ...

  7. [编织消息框架][netty源码分析]7 Unsafe 实现类NioSocketChannelUnsafe职责与实现

    Unsafe 是channel的内部接口,从书写跟命名上看是不公开给开发者使用的,直到最后实现NioSocketChannelUnsafe也没有公开出去 public interface Channe ...

  8. word和.txt文件转html 及pdf文件, 使用poi jsoup itext心得

    word和.txt文件转html 及pdf文件, 使用poi jsoup  itext心得本人第一次写博客,有上面不足的或者需要改正的希望大家指出来,一起学习交流讨论.由于在项目中遇到了这一个问题,在 ...

  9. 为Windows资源管理器右键菜单添加菜单项

    为Windows资源管理器右键菜单添加菜单项 在Windows下命令行用的比较多,经常在资源管理器里翻到某个目录,若想要在此目录下跑命令,只能是打开cmd.exe,然后一路cd才能到达此目录. 每次都 ...

  10. hibernate,createCriteria in条件 是一个集合。list 或 数组等

    hibernate,createCriteria in条件 是一个集合.list 或 数组等 cq.in("states", new String[]{"2", ...