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循环内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mac Book中Java环境变量设置的方法

    Mac Book中Java环境变量设置的方法

    本文给大家介绍mac book 中设置java环境变量的方法,非常不错,具有参考借鉴价值,需要的朋友参考下
    2017-04-04
  • java判断今天,昨天,前天,不能用秒间隔的简单实例

    java判断今天,昨天,前天,不能用秒间隔的简单实例

    下面小编就为大家带来一篇java判断今天,昨天,前天,不能用秒间隔的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • 浅谈Java父子类加载顺序

    浅谈Java父子类加载顺序

    本文主要介绍了Java父子类加载顺序,那么这么长怎么记呀?本文就帮大家总结一下,更有利于记忆,感兴趣的可以了解一下
    2021-08-08
  • 详解Java多线程编程中的线程同步方法

    详解Java多线程编程中的线程同步方法

    这篇文章主要介绍了Java多线程编程中的线程同步方法,使用synchronized关键字创建线程同步方法是实现线程同步的关键,需要的朋友可以参考下
    2016-05-05
  • Java中的IP地址和InetAddress类使用详解

    Java中的IP地址和InetAddress类使用详解

    这篇文章主要介绍了Java中的IP地址和InetAddress类使用详解,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-10-10
  • Java 利用DeferredResult实现http轮询实时返回数据接口

    Java 利用DeferredResult实现http轮询实时返回数据接口

    这篇文章主要介绍了Java 利用 DeferredResult 实现 http 轮询实时返回数据接口,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • JDBC连接Mysql的5种方式实例总结

    JDBC连接Mysql的5种方式实例总结

    JDBC是Java DataBase Connectivity技术的简称,是一种可用于执行 SQL语句的Java API,下面这篇文章主要给大家介绍了关于JDBC连接Mysql的5种方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-04-04
  • JDK14新特性之switch表达式的实现

    JDK14新特性之switch表达式的实现

    这篇文章主要介绍了JDK14新特性之switch表达式的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • java 语句块的使用详解及实例

    java 语句块的使用详解及实例

    这篇文章主要介绍了java 用语句块的正确方法实例详解的相关资料,需要的朋友可以参考下
    2017-01-01
  • Java实现基于token认证的方法示例

    Java实现基于token认证的方法示例

    这篇文章主要介绍了Java实现基于token认证的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08

最新评论