我们知道,在Java语言中有三种循环,分别是while循环,do…while循环和for循环。其实,从JDK1.5开始,又引入了一种增强型for循环,这种新型的for循环主要是用来遍历数组或集合的,它的格式非常简单,下图展示了增强型for循环的基本结构:
增强型for循环被设计出来就是为了遍历数组或者是集合的,所以在循环中都会指定一个数组或者是集合。为了方便程序员编码,增强型for循环不再通过”下标”的形式访问数组或集合中的元素,而是把依次从数组或集合取出元素,并命名为i,这样,程序员就可以直接操作元素i,从而大大简化了代码。为了方便表述,我们把元素i称之为“当前元素”。下面来看一个简单例子:
在这个增强型for循环中,指定的数组叫做array。每次循环,都从array数组中取出一个元素并命名为i,之后打印元素i的值。这里特意提醒各位小伙伴一下:循环中出现的”i”并非数组的下标,而是一个数组元素,也就是我们前面所说的”当前元素“。这段程序运行结果是”1 2 3 “。如果使用传统的for循环也能达到同样的效果,代码如下:
仅从以上两段代码的运行结果来看,似乎增强型for循环和传统for循环没有什么区别。但是,如果把以上两段代码稍作修改,运行结果就会迥然不同。我们把第一段代码改为如下形式:
为了表述方便,我们把第一段修改后的代码称之为”代码①”。之后,把原来的第二段代码修改为以下形式:
同理,我们把修改后的第二段代码称为”代码②”。”代码①”和”代码②”都是在它们原来的版本基础之上添加了一段循环修改数组元素值的操作。
但是,再次运行程序就会发现,”代码①”的运行结果仍然是”1 2 3 ”,而”代码②”的运行结果是”3 4 5 ”,也就是说”代码①”中修改数组元素值的操作并没有起作用。这是为什么呢?就是因为:”代码①”中所谓的”当前元素”i其实是一个作用域仅限于循环自身的局部变量,每次循环开始的时候都要重新定义这个变量,并且重新用数组元素对变量i进行赋值。i的值来源于数组元素,但i本质上并不是数组元素本身,它只是数组元素的副本,因此修改i并不能改变数组元素的值。在”代码①”的第一个循环中,修改的就是局部变量i的值,而并非数组元素本身的值。因此,在”代码①”的第二个循环中打印”当前元素”,实际上是再次把数组元素的值赋值给局部变量i并且打印,既然数组元素的值并未改变,打印出的结果当然是原数组中的”1 2 3”。
再来看”代码②”,”代码②”中的第一个循环是通过下标的直接找到了真实的数组元素并且做了修改,每次操作都修改的是实实在在的数组元素,而不是数组元素的副本!所以程序运行到第二个循环中,打印的是修改后的数组元素。
以上讲解,就是为了让小伙伴们明白:增强型for循环中的那个”当前元素i”,其实是一个临时的局部变量,它只是数组元素的副本,而非数组元素本身。但是,我们所举的例子中,数组的类型属于基础数据类型。那么,如果数组并非基础类型,而是引用类型,使用增强型for循环能否修改数组元素呢?请看以下代码:
在这段代码中,首先定义了一个Num类,在Num类中有一个int型的属性x,我们通过构造方法初始化x的值。代码的Test类中,在主方法里定义了一个Num类型的数组array,这个数组显然是一个引用类型数据的数组。数组中的引用分别指向了3个Num类型的对象,这三个对象的x属性值分别是1,2,3。第一个for循环修改了数组中每个Num对象的x属性值,第二个循环依次打印出每个Num对象的x属性值,程序运行结果是在控制台上输出了”3 4 5”。这说明增强型for循环在遍历引用类型的数组时,通过”当前元素”可以修改数组中元素的状态,这又是为什么呢?原因其实也很简单,虽然”当前元素”n本质上也只是一个作用域仅限于循环内部的局部引用,但是,每次循环过程中,它都实实在在的指向了一个真实的数组元素,所以这个局部引用作用域虽小,但通过它却能够完成修改数组元素对象属性值的操作。
另外再强调一点,无论增强型for循环所遍历的是基础数据类型的数组,还是引用类型的数组(或集合),都要保证”当前元素”与数组中真实元素的“类型兼容性”,否则程序就会报错。比如说,我们通过一个增强型for循环操作一个int型数组时,可以把”当前元素”i设定为double类型,原因就是数组中int型的元素可以赋值给double类型的局部变量。但反过来,通过增强型for循环操作一个double型数组时,不能把”当前元素”i的类型设定为int类型,就是因为按照Java语法,无法完成由double到int的自动转换。同理,操作一个String型数组时,可以把”当前元素”的类型设定为Object,反之则无法通过编译。
希望通过这篇文章能够让小伙伴们能更深入了解Java增强型for循环的本质。