曾经没怎么多想过for each有什么特殊的地方,以为就是for循环的简便写法,直到今天写力扣发现了不对劲,使用for each就是过不了,而用正规for循环就过了,决定来好好了解一下for each。
而有时候写增强for循环又可以修改属性
最后发现,是否能理解for each的修改与是否理解Java是值传递有很大的关系。
(完整代码放在最后)
决定从基本数据类型和引用数据类型两种类型来看。
首先简单看一下for each的遍历:
自定义了一个Person类,有name(String)和age(int)两个属性。
用int数组代表基本数据类型的数组,用String类和Person类代表引用数据类型的数组
输出结果都放在了注释里面:
public class Main {
public static void main(String[] args) {
// 1.能成功遍历基本数据类型的数组元素
int[] nums = { 1, 2, 3, 4, 5 };
for (int i : nums) {
System.out.print(i + "\t");
}
// 输出:1 2 3 4 5
System.out.println();
// 2.能成功遍历引用数据类型的数组元素
String[] strs = { "acd", "qwe", "oiu" };
for (String string : strs) {
System.out.print(string + "\t");
}
// 输出:acd qwe oiu
System.out.println();
Person[] persons = { new Person("张三", 12), new Person("李四", 33), new Person("王五", 90) };
for (Person person : persons) {
System.out.print(person + "\t");
}
// 输出:Person [name=张三, age=12] Person [name=李四, age=33] Person [name=王五, age=90]
System.out.println("-----------------------------------------------------------");
}
class Person {
String name;
int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
再来看看使用for each来修改数据:(和上面代码重复的内容不再写)
public class Main {
public static void main(String[] args) {
//这些数据没有变
int[] nums = { 1, 2, 3, 4, 5 };
String[] strs = { "acd", "qwe", "oiu" };
Person[] persons = { new Person("张三", 12), new Person("李四", 33), new Person("王五", 90) };
// 3.修改基本数据类型的数组元素的值
for (int i : nums) {
if (i == 4) {
i = 233;
System.out.println("已修改");
}
}
for (int i : nums) {
System.out.print(i + "\t");
}
// 输出:1 2 3 4 5
// 说明nums数组并未修改
System.out.println();
// 4.修改引用数据类型的数组元素的值
for (String string : strs) {
if (string.equals("acd")) {
string = "hello world";
System.out.println("已修改");
}
}
for (String string : strs) {
System.out.print(string + "\t");
}
// 输出:acd qwe oiu
// 说明str数组并未修改
System.out.println();
for (Person person : persons) {
if (person.name.equals("张三")) {
person.age = 100;
System.out.println("已修改");
}
}
for (Person person : persons) {
System.out.print(person + "\t");
}
// 输出:Person [name=张三,age=100] Person [name=李四,age=33] Person [name=王五,age=90]
// 说明persons数组有修改
System.out.println();
}
}
结果:int数组和String数组多没能成功修改,而只有Person数组成功修改了
为什么Person数组能修改成功呢?因为Java是值传递。能理解这个非常重要!
(理解的朋友可以跳过下面这一段)
首先什么是值传递,什么是引用传递呢?
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
主要区别就在于是否将实际参数复制
那为什么说Java是值传递呢?按照这么说,Java其实是将实际参数复制过传递到函数中的呀!
有人举例说把int等基本数据类型作为参数,并不能修改实参的值,能说明Java是值传递,而传递Person这种自定义的类,在函数中修改属性之后,在主函数中调用却能成功修改,这并不能说明Java是值传递,反而像引用传递呀?
曾经看到一个例子:
所以说在子函数中修改属性,这个属性并非是传递进来的参数。传递进来的参数是钥匙!而不是会被砸的电视机!
Java是值传递可以理解为,你拿着你朋友家的钥匙又复刻了一把,所以现在有两把钥匙。你和你朋友都能用两把钥匙打开朋友家的门!
意思就是有两个引用同时指向了同一个地址,第二个复制的引用正是子函数中的局部变量。无论是用哪一个引用对属性修改,都能直接在内存中修改,于是会出现传递Person类修改属性会看到成功修改的结果。
如何体现值传递的特征呢——修改传递过来的值,并不会影响原来的对象:
就好比,你在复刻的钥匙上写下自己的名字,你朋友的钥匙上并不会出现你的名字。
就意味着你用子函数中的引用去指向一个新对象,而主函数中的引用并不会去指向那个新对象一样,还是指向原来的对象。
所以说Java是值传递,而并发引用传递!
for(元素类型t 元素变量x : 遍历对象obj){
引用了x的java语句;
}
增强for循环的元素变量x,就是一个局部变量,它是引用数组当前元素引用的副本(就相当于上文所说的你复刻朋友的钥匙),或者是基本数据类型的值的副本。
可以这么理解:
int[] nums = { 1, 2, 3, 4, 5 };
for (int i : nums) {
if (i == 4) {
i = 233;
}
}
//相当于:
for(int j=0;j<nums.length;j++){
int i=nums[j];
if(i==4){
i=233;
}
//所以说修改对于原数组根本没有任何影响
}
对于String数组相当于:
for (int j = 0; j < strs.length; j++) {
String i = strs[j];
if (i.equals("acd")) {
i = "hello world";
}
}
//String类是不可变对象,所以这里也没有任何影响
//输出:acd qwe oiu
对于Person类相当于:
for(int j=0;j<persons.length;j++) {
Person i=persons[j];
if (i.name.equals("张三")) {
i.age = 100; //这里改变的是persons[j].age,而不是persons[j]本身,所以能成功改变
}
}
如果是这样:
for (int j = 0; j < persons.length; j++) {
Person i = persons[j];
if (i.name.equals("李四")) {
i = new Person("A", 80); //i指向一个新对象,输出结果也毫无变化,不会输出(A,80)
}
}
总而言之:如果想要修改元素就用正规for循环,不要使用增强for循环。
所有的代码:
public class Main {
public static void main(String[] args) {
// 1.能成功遍历基本数据类型的数组元素
int[] nums = { 1, 2, 3, 4, 5 };
for (int i : nums) {
System.out.print(i + "\t");
}
// 输出:1 2 3 4 5
System.out.println();
// 2.能成功遍历引用数据类型的数组元素
String[] strs = { "acd", "qwe", "oiu" };
for (String string : strs) {
System.out.print(string + "\t");
}
// 输出:acd qwe oiu
System.out.println();
Person[] persons = { new Person("张三", 12), new Person("李四", 33), new Person("王五", 90) };
for (Person person : persons) {
System.out.print(person + "\t");
}
// 输出:Person [name=张三, age=12] Person [name=李四, age=33] Person [name=王五, age=90]
System.out.println();
System.out.println("-----------------------------------------------------------");
// 3.修改基本数据类型的数组元素的值
for (int i : nums) {
if (i == 4) {
i = 233;
System.out.println("已修改");
}
}
for (int i : nums) {
System.out.print(i + "\t");
}
// 输出:1 2 3 4 5
// 说明nums数组并未修改
System.out.println();
// 4.修改引用数据类型的数组元素的值
for (String string : strs) {
if (string.equals("acd")) {
string = "hello world";
System.out.println("已修改");
}
}
for (String string : strs) {
System.out.print(string + "\t");
}
// 输出:acd qwe oiu
// 说明str数组并未修改
System.out.println();
for (Person person : persons) {
if (person.name.equals("张三")) {
person.age = 100;
System.out.println("已修改");
}
}
for (Person person : persons) {
System.out.print(person + "\t");
}
// 输出:Person [name=张三,age=100] Person [name=李四,age=33] Person [name=王五,age=90]
// 说明persons数组有修改
System.out.println();
System.out.println("-----------------------------------------------------------");
// int数组相当于:
for (int j = 0; j < nums.length; j++) {
int i = nums[j];
if (i == 4) {
i = 233;
}
// 所以说修改对于原数组根本没有任何影响
}
for (int i : nums) {
System.out.print(i + "\t");
}
System.out.println();
// String类相当于
for (int j = 0; j < strs.length; j++) {
String i = strs[j];
if (i.equals("acd")) {
i = "hello world";
}
}
for (String string : strs) {
System.out.print(string + "\t");
}
System.out.println();
// Person类相当于:
persons = { new Person("张三", 12), new Person("李四", 33), new Person("王五", 90) };
for (int j = 0; j < persons.length; j++) {
Person i = persons[j];
if (i.name.equals("张三")) {
i.age = 100; // 这里改变的是persons[j].age,而不是persons[j]本身,所以能成功改变
}
if (i.name.equals("李四")) {
i = new Person("A", 80); // i指向一个新对象,输出结果也毫无变化,不会输出(A,80)
}
}
for (Person person : persons) {
System.out.print(person + "\t");
}
}
}
class Person {
String name;
int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}