Java基础之值传递和引用传递
前言
值传递和引用传递是java基础知识中一个比较重要的概念,如果对这个概念理解不清的话,可能就会造成我们的代码出现“对象中的值被无故改变了”这种问题。本篇文章将针对这两个概念进行介绍和讲解,希望对各位读者有所帮助。
(一)值传递的例子
我们先来看下面这个例子,判断一下最终的输出会是什么?
public class Test {
public static void main(String[] args) {
int i = 10;
changeValue(i);
System.out.println(i);
}
private static void changeValue(int i){
i = 20;
}
}
最终的执行结果为10,这个结果似乎很好理解,调用changeValue
方法时,我们相当于是将10作为入参传了进去,赋值给了changeValue
方法的形参i
,至于形参i
后续的值发生变化,是不会影响到main方法中的i
的。而上面例子中调用方法时只涉及到基本数据类型的传递,这种传递方式通常也被叫做值传递。
(二)引用传递的例子1
我们再来看下面这个例子,调用的方法入参改成了对象,再来看看最终的输出结果会是什么
public class Test {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Test test = new Test();
test.setName("aaa");
changeValue(test);
System.out.println(test.getName());
}
private static void changeValue(Test test){
test.setName("bbb");
}
}
最终的输出结果如下,答案为bbb
,可能会让初学者感到不解,为何相似的操作得到的结果却不同。我们在方法中修改了Test
对象的属性,为什么会影响到main方法中Test对象的值呢?
其实答案并不难理解,我们知道对于引用数据类型,这类对象的值并不会放在栈中,而是会单独放在堆内存中,而栈中对象的值为实际对象在堆内存中的地址值(类似:0x123123123)。当我们调用changeValue(Test test)方法时,会将test对象在内存中的引用传递了进去。也就是说,此时main方法中的
test
和changeValue方法中的形参test
都指向了堆中的同一个对象。而changeValue方法在调用的过程中修改了对象在堆内存中的属性,main方法中的test变量也就会同样受到影响。在这个例子中,我们传递给方法的参数是一个引用数据类型的变量,这种传递方式通常也被称为引用传递。
image.png
(三)引用传递的例子2
我们再来看下面一个引用传递相关的例子,判断一下最终输出的值是什么
public class Test {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Test test = new Test();
test.setName("aaa");
changeValue(test);
System.out.println(test.getName());
}
private static void changeValue(Test test){
Test test1 = new Test();
test1.setName("bbb");
test = test1;
}
}
这次的执行结果为aaa
。原因也很好理解,changeValue
方法中,我们只是对test
形参的指向进行了变化,并不涉及对原Test
对象的修改。
简单的内存分析图
值传递和引用传递的本质
本质上来说,所谓的引用传递实质上也是值传递,只是传递的值是对象在内存中的地址值而已。个人认为,单纯记传入的值是基本数据类型还是引用类型并不是理解这个知识的好方法。更好的方法是理解调用方法传入参数时,代码做了什么。以第二个例子为例,我们可以把执行过程写成下面的伪代码:
(值传递的过程其实就只是一个简单的值拷贝,然后赋值给方法的形参而已)
Test test = new Test();
test.setName("aaa");
//下面是changeValue(test)执行的步骤
{
// 新建了一个Test类型的变量test1, 且把test的值赋给了变量test1
Test test1 = test;
// test的值本质上就是一个地址值
test1.setName("bbb");
}
System.out.println(test.getName());
而原对象到底会不会被修改,最为关键的地方在于是否改动了原对象在内存中的属性值,形参单纯的修改对象指向并不会对原对象造成影响。正因为传递对象时传递的是对象在内存中的地址值,所以我们在进行引用传递的时候,一定要注意,不要轻易修改原对象的属性。如果涉及到修改的操作,建议深拷贝一份对象给形参之后,再进行对象属性的修改操作。
小结
至此,对于值传递和引用传递的概念就介绍到这里,深刻理解调用方法时的赋值过程,相信可以帮助各位初学者避开很多坑。