Java的增强for循环修改数组元素的问题小结

 更新时间:2024年02月07日 10:33:28   作者:Violet_Stray  
增强for循环的元素变量x,就是一个局部变量,它是引用数组当前元素引用的副本(就相当于上文所说的你复刻朋友的钥匙),或者是基本数据类型的值的副本,这篇文章主要介绍了Java的增强for循环修改数组元素的问题小结,需要的朋友可以参考下

曾经没怎么多想过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是值传递呢?按照这么说,Java其实是将实际参数复制过传递到函数中的呀!
有人举例说把int等基本数据类型作为参数,并不能修改实参的值,能说明Java是值传递,而传递Person这种自定义的类,在函数中修改属性之后,在主函数中调用却能成功修改,这并不能说明Java是值传递,反而像引用传递呀?

曾经看到一个例子:

  • 你的朋友借给你一把钥匙,你朋友就相当于主函数,你相当于主函数中调用的另一个函数,钥匙就是实参,你这个函数就相当于进入朋友家中,干一些事情之后离开。你拿着这把钥匙,能打开他们家的大门,于是你进了他们家,砸坏了他们家的电视机(相当于修改Person类的属性)。当这个函数结束的时候,就相当于你已经离开了你朋友的家中。你朋友拿着钥匙打开了自家的门,发现电视机被砸坏了(相当于你在输出端看到在主属性Print的Person的属性值被修改了),于是朋友把你揍了一顿。

所以说在子函数中修改属性,这个属性并非是传递进来的参数。传递进来的参数是钥匙!而不是会被砸的电视机!
Java是值传递可以理解为,你拿着你朋友家的钥匙又复刻了一把,所以现在有两把钥匙。你和你朋友都能用两把钥匙打开朋友家的门!
意思就是有两个引用同时指向了同一个地址,第二个复制的引用正是子函数中的局部变量。无论是用哪一个引用对属性修改,都能直接在内存中修改,于是会出现传递Person类修改属性会看到成功修改的结果。

如何体现值传递的特征呢——修改传递过来的值,并不会影响原来的对象:
就好比,你在复刻的钥匙上写下自己的名字,你朋友的钥匙上并不会出现你的名字。
就意味着你用子函数中的引用去指向一个新对象,而主函数中的引用并不会去指向那个新对象一样,还是指向原来的对象。

所以说Java是值传递,而并发引用传递!

回到增强for循环:

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 + "]";
	}
}

到此这篇关于关于Java的增强for循环修改数组元素的问题的文章就介绍到这了,更多相关Java增强for循环内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot整合Shiro的方法详解

    SpringBoot整合Shiro的方法详解

    Apache Shiro是一个java安全(权限)框架,Shiro可以非常容易的开发出足够好的应用,其不仅可以用在javase环境,也可以用在javaee环境。本文介绍了SpringBoot整合Shiro的方法,需要的可以参考一下
    2022-05-05
  • springboot2.2.2集成dubbo的实现方法

    springboot2.2.2集成dubbo的实现方法

    这篇文章主要介绍了springboot2.2.2集成dubbo的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • Java设计模式之观察者模式_动力节点Java学院整理

    Java设计模式之观察者模式_动力节点Java学院整理

    这篇文章给大家介绍流量java设计模式之观察者模式,定义对象间一种一对多的依赖关系,使得当每一个对象改变状态。下面通过类图和实例代码给大家介绍java设计模式之观察者模式,感兴趣的朋友一起看看吧
    2017-08-08
  • Spring bean生命周期配置过程解析

    Spring bean生命周期配置过程解析

    这篇文章主要介绍了Spring bean生命周期配置过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • java获取文件的inode标识符的方法

    java获取文件的inode标识符的方法

    这篇文章主要介绍了java获取文件的inode标识符,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 关于web项目读取classpath下面文件的心得分享

    关于web项目读取classpath下面文件的心得分享

    这篇文章主要介绍了关于web项目读取classpath下面文件的心得,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • Java基础之详细总结五种常用运算符

    Java基础之详细总结五种常用运算符

    在通常代码逻辑处理中,我们常常都会使用到运算符,今天我们就详细了解一下运算符的使用以及分类.运算符是对常量或者变量进行操作的符号,它分为算术运算符,赋值运算符,比较运算符,逻辑运算符以及位运算符.需要的朋友可以参考下
    2021-05-05
  • Java函数式开发 Optional空指针处理

    Java函数式开发 Optional空指针处理

    本文主要介绍Java函数式开发 Optional空指针处理,这里整理了相关资料,及示例代码,有兴趣的小伙伴可以参考下
    2016-09-09
  • JAVA基础-GUI

    JAVA基础-GUI

    这篇文章主要介绍了JAVA中关于GUI的相关知识,文中代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下
    2020-06-06
  • Java中使用MyBatis-Plus操作数据库的实例

    Java中使用MyBatis-Plus操作数据库的实例

    本文主要介绍了Java中使用MyBatis-Plus操作数据库的实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-02-02

最新评论