Java
基本类型传递
先拿我最熟悉的 Java
来说,我相信应该没人会写这样的代码:
@Test public void testBasic() { int a = 10; modifyBasic(a); System.out.println(String.format("最终结果 main a==%s", a)); } private void modifyBasic(int aa) { System.out.println(String.format("修改之前 aa==%s", aa)); aa = 20; System.out.println(String.format("修改之后 aa==%s", aa)); }
输出结果:
修改之前 aa==10 修改之后 aa==20 最终结果 main a==10
不过从这段代码的目的来看应该是想要修改 a
的值,从直觉上来说如果修改成功也是能理解的。
至于结果与预期不符合的根本原因是理解错了参数的值传递与引用传递。
在这之前还是先明确下值传递与引用传递的区别:
这里咱们先抛出结论,Java
采用的是值传递;这样也能解释为什么上文的例子没有成功修改原始数据。
参考下图更好理解:
当发生函数调用的时候 a
将自己传入到 modifyBasic
方法中,同时将自己的值复制了一份并赋值给了一个新变量 aa
从图中可以看出这是 a
和 aa
两个变量没有一毛钱关系,所以对 aa
的修改并不会影响到 a
。
有点类似于我把苹果给了老婆,她把苹果削好了;但我手里这颗并没有变化,因为她只是从餐盘里拿了一颗一模一样的苹果削好了。
如果我想要她那颗,只能让她把削好的苹果给我;也就类似于使用方法的返回值。
a = modifyBasic(a);
引用类型传递
下面来看看引用类型的传递:
private class Car{ private String name; public Car(String name) { this.name = name; } @Override public String toString() { return "Car{" + "name='" + name + '\'' + '}'; } } @Test public void test01(){ Car car1 = new Car("benz"); modifyCar1(car1); System.out.println(String.format("最终结果 main car1==%s", car1)); } private void modifyCar1(Car car){ System.out.println(String.format("修改之前 car==%s", car)); car.name = "bwm"; System.out.println(String.format("修改之后 car==%s", car)); }
在这个例子里先创建了一个 benz
的 car1
,通过一个方法修改为 bmw
那最开始的 car1
会受到影响嘛?
修改之前 car==Car{name='benz'} 修改之后 car==Car{name='bwm'} 最终结果 main car1==Car{name='bwm'}
结果可能会与部分人预期相反,这样的修改却是可以影响到原有数据的?这岂不是和值传递
不符,看样子这是引用传递
吧?
别急,通过下图分析后大家就能明白:
在 test01
方法中我们创建了一个 car1
的对象,该对象存放于堆内存中,假设内存地址为 0x1102
,于是 car1
这个变量便应用了这块内存地址。
当我们调用 modifyCar1
这个方法的时候会在该方法栈中创建一个变量 car
,接下来重点到了:
这个 car
变量是由原本的入参 car1
复制而来,所以它所对应的堆内存依然是 0x1102
;
所以当我们通过 car
这个变量修改了数据后,本质上修改的是同一块堆内存中的数据。从而原本引用了这块内存地址的 car1
也能查看到对应的变化。
这里理解起来可能会比较绕,但我们记住一点就行:
传递引用类型的数据时,传递的并不是引用本身,依然是值;只是这个值
是内存地址罢了。
因为把相同的内存地址传过去了,所以对数据的操作依然会影响到外部。