一文搞懂参数传递原理(上)

简介: 最近一年多的时间陆续接触了一些对我来说陌生的语言,主要就是 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 从图中可以看出这是 aaa 两个变量没有一毛钱关系,所以对 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));
    }


在这个例子里先创建了一个 benzcar1,通过一个方法修改为 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 也能查看到对应的变化。


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


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


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


相关文章
|
4月前
|
存储 运维 前端开发
【面试题】吃透Promise?先实现一个再说(包含所有方法)(一)
【面试题】吃透Promise?先实现一个再说(包含所有方法)(一)
|
2月前
|
算法 编译器 C++
【C++ 函数 基本教程 第二篇 】深度剖析C++:作用域与函数查找机制
【C++ 函数 基本教程 第二篇 】深度剖析C++:作用域与函数查找机制
34 0
|
5月前
|
前端开发
【前端学习】—箭头函数和普通函数的区别(十四)
【前端学习】—箭头函数和普通函数的区别(十四)
|
6月前
一个案例搞懂解构
一个案例搞懂解构
对于async和await的使用方式、作用效果不怎么理解 ?没关系,初步看这篇就够了
对于async和await的使用方式、作用效果不怎么理解 ?没关系,初步看这篇就够了
|
11月前
|
存储 编译器 C语言
【C】函数真的难嘛?其实一点也不难,原理很简单。
# 什么是函数 程序是由多个零件组合而成的,而函数就是这种“零件”的一个较小单位。 ## main函数和库函数 C语言程序中,main函数是必不可少的。程序运行的时候,会执行main函数的主题部分。main函数中使用了printf、scanf、puts等函数。由C语言提供的这些为数众多的函数称为库函数。 ## 什么是函数 当然,我们也可以自己创建函数。而实际上,我们也必须亲自动手创建各种函数。下面我们来自己创建一个简单的函数。 创建一个函数,接收两个整数参数,返回较大整数的值。 printf函数和scanf函数等创建得比较好得函数,即使不知道其内容,只要了解使用方法,也可以轻松使用。 ## 函
|
11月前
|
存储 JavaScript 前端开发
📕 重学JavaScript:函数的入参(arguments)是什么类型?我能怎么使用它?
arguments是一个特殊的对象👏,它可以存储我们给函数传递的所有参数。只不过它的属性从0开始排,依次为0,1,2…最后还有callee和length属性。我们也把这样的对象称为类数组。
72 0
|
前端开发
前端学习案例2-闭包的形式
前端学习案例2-闭包的形式
29 0
前端学习案例2-闭包的形式
|
前端开发
前端学习案例1-闭包的形式
前端学习案例1-闭包的形式
38 0
前端学习案例1-闭包的形式
|
前端开发
前端学习案例4-闭包定义4
前端学习案例4-闭包定义4
52 0
前端学习案例4-闭包定义4