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

简介: 最近一年多的时间陆续接触了一些对我来说陌生的语言,主要就是 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 也能查看到对应的变化。


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


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


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


相关文章
|
Cloud Native Linux 数据中心
【Docker】一 Docker简介
Docker是一个开源的容器引擎,它可以帮助我们更快地交付应用。Docker可将应用程序和基础设施层 隔离,并且能将基础设施当作程序一样进行管理。使用Docker,可更快地打包、测试以及部署应用程 序,并可减少从编写到部署运行代码的周期。
376 1
【Docker】一 Docker简介
idea使用记录
idea使用记录
287 0
idea使用记录
|
数据安全/隐私保护 C++
派生类的访问控制和类型兼容规则
派生类继承了基类的全部数据成员和除了构造、析构函数之外的全部函数成员,但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员,其访问属性由继承方式控制。 基类的成员可以有 public、protected 和 private 三种。基类的自身成员可以对基类中任何一个其他成员进行访问,但是通过基类的对象就只能访问该类的公有成员。
397 0
|
4天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
14天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
8天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
567 211
|
4天前
|
编解码 Linux 数据安全/隐私保护
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
教程分享免费视频压缩软件,免费视频压缩,视频压缩免费,附压缩方法及学习教程
228 138
|
存储 人工智能 监控
从代码生成到自主决策:打造一个Coding驱动的“自我编程”Agent
本文介绍了一种基于LLM的“自我编程”Agent系统,通过代码驱动实现复杂逻辑。该Agent以Python为执行引擎,结合Py4j实现Java与Python交互,支持多工具调用、记忆分层与上下文工程,具备感知、认知、表达、自我评估等能力模块,目标是打造可进化的“1.5线”智能助手。
800 59