3.3小节曾经讲到:Java语言中有3种循环,其中一种是for循环。for循环又可以分为普通for循环和增强型for循环。本小节首先讲解普通for循环的使用。
3.4.1 普通for循环
在3.3小节中列举了两个使用while循环完成重复性操作的例子。细心的读者可能已经发现了一个规律:程序中在使用while时,绝大多数情况下总是要做两个操作:
- 声明并初始化循环变量,这个操作一般都是在while循环之前完成
- 修改循环变量的值,这个操作一般都是在每次执行循环语句时放在最后进行
此外,按照while循环的语法要求,while循环必须要设置一个执行条件。人们在经过大量实践后发现:声明并初始化循环变量、设置循环条件、修改循环变量这3个操作基本上成了使用while循环的“标配操作”,久而久之几乎成了使用while循环的固定操作套路和模式。
其实,Java语言中有一种专门为这种编程套路设计的循环,它就是本节要要讲解的for循环。以下是for循环最常见的使用方式:
for(声明并初始化循环变量;循环条件;修改循环变量) 语句; |
通过观察不难发现:同while一样,for循环也有一对小括号,但它的小括号中包含了3项内容,这3项内容就是之前总结的使用while循环的3个“标配操作”,它们之间以分号隔开。需要特别提醒各位读者:这3个标配操作从逻辑上讲有先后依赖关系,因此它们的摆放顺序不可以更改。
初学Java语言的读者可以简单的把for循环理解为while循环的“变异版本”,所有while循环能完成的操作,for循环也都可以完成。在3.3小节中曾经讲解过如何使用while循环按顺序打印array数组全部元素。既然使用for循环是while循环的“变异版本”,那么for同样可以完成这个操作,套用上面介绍的for循环最常用的使用方法,打印数组元素的程序如下:
【例03_05用for循环打印数组元素】
Exam03_05.java
public class Exam03_05 {
public static void main(String[] args) {
int[] array = { 1, 3, 5, 7, 9 };
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]); // 打印下标为i的元素
}
}
}
读者可对比【例03_03】和【例03_05】的运行结果,可以发现它们的运行结果是完全相同的。
以上讲解了for循环最常见的使用方法,并且通过【例03_05】让各位读者体验了如何使用for循环完成与while循环相同的操作。其实,想要真正掌握for循环的用法,还必须深入学习它的工作原理。在讲解for循环工作原理之前,需要特别提醒各位读者,上文出现的:
for(声明并初始化循环变量;循环条件;修改循环变量) 语句; |
它并不是for循环的标准语法格式,它只是for循环的最常见使用方式。for循环的真正语法格式如下:
for(表达式①;表达式②; 表达式③) 语句; |
从上面的格式中可以看出,for循环的小括号中包含3个表达式,这3个表达式之间用分号隔开。for循环的工作过程是:虚拟机首先执行表达式①,此处要特别强调:表达式①的执行是无条件的,也就是说它一定会被执行,并且只执行1次。根据表达式①的执行时机和特点,程序员一般都会用表达式①来完成声明和初始化循环变量的操作。执行完表达式①后,虚拟机会计算表达式② ,如果表达式② 的值是true,那么就执行循环语句,执行完循环语句以后再去执行表达式③。反过来,如果表达式②的值是false,虚拟机不会执行循环语句,同样也不会执行表达式③,而是直接结束循环。之前已经强调:表达式①只会被执行1次,因此第1轮循环结束之后,在后面轮次的循环中,虚拟机都不会再次执行表达式①,而是每次都直接计算表达式②的值是否为true,如果为true则再次执行循环语句和表达式③,否则退出循环。程序员一般都会用表达式②来当作循环条件,而用表达式③来完成修改循环变量的操作。for循环就是这样不断的判断循环条件,不断的执行循环语句和表达式③,直到表达式②的值变成false才退出循环。
其实,表达式①并不一定要用来声明和初始化循环变量,表达式③也不一定要用来修改循环变量,它们完全可以做其他任何操作。这3个表达式甚至可以不出现在for循环的小括号中,但用来分隔3个表达式的分号不能省略。如果表达式①和表达式③没有出现,那么在执行它们的时候,虚拟机就什么操作也不做。而如果表达式②没有出现,虚拟机则不做任何判断,直接执行循环语句和表达式③。以下例子演示了一个“非常规”的for循环。
【例03_06非常规for循环】
Exam03_06.java
public class Exam03_06 {
public static void main(String[] args) {
int[] array = { 1, 3, 5, 7, 9 };
int i = 0;// 声明并初始化循环变量
for (System.out.println("执行表达式①"); i < array.length;) {
System.out.println(array[i]);
i++;// 修改循环变量
}
}
}
【例03_06】的运行结果如图3-4所示:
图3-4 【例03_06】运行效果
在【例03_06】的for循环中,表达式①并没有用来声明和初始化循环变量,而是完成了一个输出操作。这个输出操作可以说与整个循环没有任何关系。之所以把它放在表达式①的位置,就是为了演示表达式①其实可以用来做任何操作,即使这个操作与for循环无关也可以。而通常情况下在表达式①处完成的声明并初始化循环变量的操作被移到了for循环之前进行。另外,表达式③压根就没有出现,通常在表达式③处完成的修改循环变量的操作被放到了循环当中进行,它成了循环语句的最后一条,但与把它放在表达式③的位置上的执行效果相同。
本节用较大篇幅讲解for循环的工作原理,并演示了一个在实际开发过程中几乎不可能出现的“非常规”for循环例子,目的就是为了让读者从根本上的理解for循环的执行原理。很多面试题中都会出现“不按套路书写”的for循环,并且让面试者写出运行结果。如果面试者完全掌握了for循环的执行原理,那么无论for循环的书写形式如何变化,都可以做出正确的回答。
各位读者在使用for循环时还需要注意3个细节:
- 尽量不要使用浮点型数据充当循环变量
- 如果在for循环的小括号中声明循环变量,那么这个循环变量的作用域仅限于该for循环,在循环之外该循环变量失效
- 如果需要声明并操作多个循环变量,在表达式①和表达式③中用逗号隔开各循环变量的声明和操作语句,例如:
for(int i=0,j=1;i<5 && j<20; i++,j*=2)
3.4.2 增强型for循环
在正式讲解增强型for循环之前,首先介绍一个概念--“遍历”。所谓遍历其实就是按照某种特定的顺序依次访问数组中的每一个元素。例如前面的小节中,使用循环的方式依次打印array数组中的每一个元素,就是一种典型遍历数组操作。
经过大量实践人们又发现:程序中使用for循环完成对数组从头到尾正向遍历的操作非常多,并且使用for循环完成正向遍历数组都遵循一个固定不变的编程格式:
for(int i=0;i<数组名.length;i++){ 访问数组元素的语句; } |
通过以上格式不难看出,如果用for循环正向遍历数组,它小括号中永远都是几个固定不变的操作:循环变量i的初始值总int型的,它的初始值总是从0开始,而循环条件也总是“i<数组名.length”,并且对循环变量的操作也都是完成自增。既然在程序中使用for循环正向遍历数组的操作出现的次数非常多,并且它的编程格式又是固定不变的,那么是否能让这个遍历操作变得更简单呢?
从JDK1.5开始,Java语言专门推出了一种新型的循环,它可以很方便的完成正向遍历数组的操作,这种循环叫做“增强型for循环”。增强型for循环的格式如下:
for(数据类型 当前元素名称:数组名称){ 访问数组元素的语句; } |
可以看到:在增强型for循环的小括号中,原有的那些用来实现正向遍历数组的固定操作都不见了。之所以能把这些固定操作省略掉,就是因为虚拟机已经“熟知”了这几步固定操作的套路,无需写在小括号中。小括号中取而代之的是当前元素名称和数组名称,它们之间用冒号隔开。
此处需要解释一下什么是“当前元素名称”。在使用普通的for循环操作数组时,如果数组的名称是array,并且以循环变量i来表示数组下标,那么每一轮循环所访问的那个数组元素就要用array[i]来表示。而增强型for循环中,根本就没有定义表示数组下标的变量i,因此也就无法以“数组名[下标]”的格式表示出当前这一轮循环所访问的那个数组元素。为了表示出本轮循环要访问的那个数组元素,增强型for循环要求程序员必须为这个数组元素起一个临时性的名字,这个名字就是“当前元素名称”。此外,增强型for循环的小括号中还必须指定数组名称,这样虚拟机才知道这个增强型for循环所遍历的是哪一个数组。下面的例子展示了如何使用增强型for循环正向遍历数组:
【例03_07 使用增强型for循环正向遍历数组】
Exam03_07.java
public class Exam03_07 {
public static void main(String[] args) {
int[] array = { 1, 3, 5, 7, 9 };
//使用增强型for循环正向遍历array数组,当前元素名称为x
for (int x : array) {
System.out.print(x + " ");// 使用print()方法实现不换行打印
}
}
}
首先强调一个细节:程序中打印当前元素使用的是print()方法而不是之前一直使用的println()方法。print()方法在每次打印数据之后,并不自动换到下一行,所以连续使用这个方法所打印的数据都出现在控制台的同一行中。
观察【例03_07】的程序代码不难看出,使用增强型for循环时,无需指出数组下标的初始值是多少,也无需设定循环条件。虚拟机会自动从下标为0的元素开始,按顺序一直访问到数组中最后一个元素。每次访问一个数组元素时,都把这个元素命名为x,因此打印当前元素时,只需要打印x即可。x后面的” ”表示一个空格,这使得每两次打印的数组元素以空格相间隔,不会紧挨在一起。【例03_07】的运行结果为:
1 3 5 7 9
增强型for循环的用法并不难掌握,但很多读者可能会有一个疑问:程序中已经指定了array数组的类型是int型,为什么定义当前元素名称时还要在它的前面再次加上一个int关键字呢?这个问题的本质其实就是:为什么已知当前元素是int型,为什么还要在循环中再次定义其类型呢?这个类型关键字能否省略呢?
首先,增强型for循环中当前元素之前的类型关键字不可省略。其次,在当前元素名称之前添加类型关键字,是希望在整个循环中虚拟机把每一个元素当作什么类型的数据来对待。例如,在【例03_07】中,如果把循环代码写成:
for(double x:array)
那么在循环中,虚拟机就会在循环中把每一个数组元素当作double型数据来看待,这样程序运行的结果就会变为:
1.0 3.0 5.0 7.0 9.0
当然,程序员也并不能随意的指定虚拟机把数组元素当作某种类型。例如,数组如果本身是double型,如果程序员指定虚拟机把元素当作int型数据,就会使得数组元素的小数部分丢失,出现精度损失的现象。这种情况会直接导致程序出现语法错误。
另外还需要强调一个非常重要的细节:增强型for循环无法修改数组元素的值。以下是一个试图用增强型for循环的例子:
【例03_08使用增强型for循环修改数组元素】
Exam03_08.java
public class Exam03_08 {
public static void main(String[] args) {
int[] array = { 1, 3, 5, 7, 9 };
//使用增强型for循环修改array数组元素的值
for (int x : array) {
x = x + 2;
}
//重新打印array数组的元素
for (int x : array) {
System.out.print(x + " ");
}
}
}
在【例03_08】中,出现了两个增强型for循环,第一个循环中各每一个当前元素都加了2并且重新赋值给当前元素。第二个循环再次打印了原数组的每个元素,该程序的运行结果是:
1 3 5 7 9
这说明增强型for循环并不能修改数组元素的值。增强型for循环之所以不能修改数组元素的值,就是因为每次访问数组元素时,都是把数组元素先存储在一个临时变量中,这个临时变量就是当前元素,它只是数组元素的一个副本。增强型for循环所操作的就是这个副本而不是数组元素本身,因此无法修改数组元素的值。如果需要在循环中修改数组元素,还需要使用普通循环,并且在循环中以“数组名[下标]”的格式操作数组元素。
通过以上对for循环的深入剖析可知,增强型for循环优点是:代码结构简单,在实现正向遍历数组时,程序员根本不用担心数组下标越界以及死循环的出现。同样,增强型for循也有很大的局限性,具体表现为:
- 只能正向完成数组遍历,无法实现逆向遍历数组。
- 无法访问特定的元素,比如仅访问下标为偶数的那些元素
- 无法修改数组元素的值
除此文字版教程外,小伙伴们还可以点击这里观看我在本站的视频课程学习Java。
标签:第三章,增强型,元素,循环,数组,array,表达式 From: https://blog.51cto.com/mugexuetang/5978693