当前位置: 首页 > article >正文

深入浅出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

如果跟你预期的不同,那我想你还是没有理解参数的值传递与引用传递的原理。

结合生活中的场景,深入理解一下值传递和引用传递:

你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。

你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

下面我们来画图更好的理解上述代码的例子:

image-20211128174051399

当发生函数调用的时候 n 将自己传入到 test01 方法中,同时将自己的值复制了一份并赋值给了一个新变量 m 从图中可以看出这是 nm 两个变量没有一毛钱关系(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='小黑'}

为了方便大家理解,还是画图来分析一下:

image-20211128171133053

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 是引用传递那最终的结果应该是打印 小黑 才对,从结果看这里依然是值传递。

还是画图来分析一下:

image-20211128172501300

如果是引用传递,原本的 0x1120 应该是被直接替换为新创建的 0x1121 才对;而实际情况如上图所示,dog 直接重新引用了一个对象dog = new Dog("小黑"),两个对象之间互不干扰。

小结

Java中参数传递其实还是值传递的,只不过对于引用类型参数,值的内容是对象的引用。


http://www.kler.cn/a/15687.html

相关文章:

  • IDEA leetcode插件代码模板配置,登录闪退解决
  • ubuntu安装 Pycharm
  • 【第三课】Rust变量与数据类型(二)
  • uniapp h5地址前端重定向跳转
  • 从电动汽车到车载充电器:LM317LBDR2G 线性稳压器在汽车中的多场景应用
  • 跳房子(弱化版)
  • 这4种情况,请直接跳槽!程序员跳槽指南
  • LED子系统
  • C. Multiplicity(DP + 分解因数)
  • Java多线程中Lock的使用
  • 9.pytorch lightning之数据模块LightningDataModule
  • mysql笔记
  • python实战应用讲解-【numpy数组篇】常用函数(六)(附python示例代码)
  • HPC的资料
  • 【Docker】Dockerfile简介
  • 被修饰成单栋的倾斜摄影处理思路
  • 有理函数的不定积分
  • 《港联证券》半导体复苏预期“抢跑”产业现实 细分市场缓慢回温
  • ETL工具 - Kettle 转换算子介绍
  • Linux进程间通信 - 信号(signal) 与 管道(pipe) 与 消息队列
  • 【VM服务管家】VM4.0软件使用_1.1 环境配置类
  • SpringBoot 中 4 种常用的数据库访问方式
  • Microsoft Bitlocker企业级管理部署方案
  • 在京东工作8年的程序员,35岁被裁拿到30多万的赔偿,终于自由了
  • 2023天梯赛补题
  • 华为OD机试 - 模拟商场优惠打折(Python)