一文搞懂参数传递原理

一文搞懂参数传递原理

前言

最近一年多的时间陆续接触了一些对我来说陌生的语言,主要就是 Python 和 Go,期间为了快速实现需求只是依葫芦画瓢的撸代码;并没有深究一些细节与原理。

就拿参数传递一事来说各个语言的实现细节各不相同,但又有类似之处;在许多新手入门时容易搞不清楚,导致犯一些低级错误。

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 也能查看到对应的变化。

这里理解起来可能会比较绕,但我们记住一点就行:

传递引用类型的数据时,传递的并不是引用本身,依然是值;只是这个值 是内存地址罢了。

因为把相同的内存地址传过去了,所以对数据的操作依然会影响到外部。

所以同理,类似于这样的代码也会影响到外部原始数据:

@Test public void testList(){ List<Integer> list = new ArrayList<>(); list.add(1); addList(list); System.out.println(list); } private void addList(List<Integer> list) { list.add(2); } [1, 2]

那如果是这样的代码:

@Test public void test02(){ Car car1 = new Car("benz"); modifyCar(car1); System.out.println(String.format("最终结果 main car1==%s", car1)); } private void modifyCar(Car car2) { System.out.println(String.format("修改之前 car2==%s", car2)); car2 = new Car("bmw"); System.out.println(String.format("修改之后 car2==%s", car2)); }

假设 Java 是引用传递那最终的结果应该是打印 bmw 才对。

修改之前 car2==Car{name='benz'} 修改之后 car2==Car{name='bmw'} 最终结果 main car1==Car{name='benz'}

从结果又能佐证这里依然是值传递。

一文搞懂参数传递原理

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygggd.html