深入浅出Java中参数传递的原理
前言
今天,想和大家聊聊关于java中的参数传递的原理,参数的传递有两种,值传递和引用传递。
值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
基本类型传递
先来看看下面这段最基本的代码:
@Test
public void test() {
int n = 10;
test01(n);
System.out.println("最终结果n==" + n);
}
private void test01(int m) {
System.out.println("修改之前m==" + m);
m = 20;
System.out.println("修改之后m==" + m);
}
输出结果:
修改之前m==10
修改之后m==20
最终结果n==10
如果跟你预期的不同,那我想你还是没有理解参数的值传递与引用传递的原理。
结合生活中的场景,深入理解一下值传递和引用传递:
你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
下面我们来画图更好的理解上述代码的例子:
当发生函数调用的时候 n
将自己传入到 test01
方法中,同时将自己的值复制了一份并赋值给了一个新变量 m
从图中可以看出这是 n
和 m
两个变量没有一毛钱关系(m只是n的复制品),所以对 m
的修改并不会影响到 n
。
如果想要改变n
的值,可以使用方法的返回值:
n = test01(n);
引用类型传递
下面来看看引用类型的传递:
public class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
@Test
public void test() {
Dog dog1 = new Dog("小白");
test01(dog1);
System.out.println("最终结果dog==" + dog1);
}
private void test01(Dog dog) {
System.out.println("修改之前 dog==" + dog);
dog.setName("小黑");
System.out.println("修改之后 dog==" + dog);
}
输出结果:
修改之前 dog==Dog{name='小白'}
修改之后 dog==Dog{name='小黑'}
最终结果dog1==Dog{name='小黑'}
为了方便大家理解,还是画图来分析一下:
在 test
方法中我们创建了一个 dog1
的对象,该对象存放于堆内存中,假设内存地址为 0x1120
,于是 dog1
这个变量便应用了这块内存地址。
当我们调用 test01
这个方法的时候会在该方法栈中创建一个变量 dog
,这个 dog
变量是由原本的入参 dog1
复制而来,所以它所对应的堆内存依然是 0x1120
;
所以当我们通过 dog
这个变量修改了数据后,本质上修改的是同一块堆内存中的数据。从而原本引用了这块内存地址的 dog1
也能查看到对应的变化。
如果不理解上面的话,那么记住下面的两句话就行了:
传递引用类型的数据时,传递的并不是引用本身,依然是值;只是这个值是内存地址罢了。
因为把相同的内存地址传过去了,所以对数据的操作依然会影响到外部。
那我们继续看看下面的代码,这种情况能否改变参数的值
@Test
public void test() {
Dog dog1 = new Dog("小白");
test01(dog1);
System.out.println("最终结果dog1==" + dog1);
}
private void test01(Dog dog) {
System.out.println("修改之前 dog==" + dog);
dog = new Dog("小黑");
System.out.println("修改之后 dog==" + dog);
}
输出结果:
修改之前 dog==Dog{name='小白'}
修改之后 dog==Dog{name='小黑'}
最终结果dog1==Dog{name='小白'}
假设 Java
是引用传递那最终的结果应该是打印 小黑
才对,从结果看这里依然是值传递。
还是画图来分析一下:
如果是引用传递,原本的 0x1120
应该是被直接替换为新创建的 0x1121
才对;而实际情况如上图所示,dog
直接重新引用了一个对象dog = new Dog("小黑")
,两个对象之间互不干扰。
小结
Java中参数传递其实还是值传递的,只不过对于引用类型参数,值的内容是对象的引用。