首页 > 编程语言 >如何理解Java 中只有值传递,没有引用传递

如何理解Java 中只有值传递,没有引用传递

时间:2023-10-03 16:35:31浏览次数:41  
标签:Java name no money 传递 person 引用 null check

引用

此文来自无法消失的对象 - 值传递和引用传递

开始

直接上代码

class Person {
    String name;
    int money;
}

class Client {
    public static void main(String[] args) {
        // Create a person named Bob and he has no money.
        Person person = new Person();
        person.name = "Bob";
        person.money = 0;
        // Check the person, if he has no money, set it as null
        check(person);
        // If the person turned to null, print he has no money, otherwise print he's rich
        if (person == null) {
            System.out.println(person.name + " has no money.");
        } else {
            System.out.println(person.name + " is rich.");
        }
    }

    private static void check(Person person) {
        if (person.money <= 0) {
            person = null;
        }
    }

}

这里有一个 Person 对象,包含两个字段,name 和 money;在main函数中创建一个person对象,名字叫 Bob,拥有的钱为0。
接着调用了一个 check() 方法。这个方法的作用是:检查这个人身上的钱,如果这个人没有钱,那么就将其置为 null,可以看到,刚才设置的 money 是0,所以这个 person 对象在 check() 方法中就会被置为 null
最后,如果 person 对象为 null,就会输出 Bob has no money.。从刚才的逻辑捋下来,这里一定会输出 Bob has no money.才对。但这就是最奇怪的地方,每次运行代码,输出结果都是 Bob is rich.

验证

有人就会问,你有没有检查过,在 main() 函数中的 person 对象,和你传入 check() 函数的对象是一样的吗?
我在调用 check() 函数之前,先输出了 person 对象的地址,然后在 check() 函数中, 再次打印了传入的 person 参数的地址。

class Person {
    String name;
    int money;
}

class Client {
    public static void main(String[] args) {
        // Create a person named Bob and he has no money.
        Person person = new Person();
        person.name = "Bob";
        person.money = 0;
        System.out.println("Prepare to check the person: " + person + ", his money is: " + person.money);
        // Check the person, if he has no money, set it as null
        check(person);
        // If the person turned to null, print he has no money, otherwise print he's rich
        if (person == null) {
            System.out.println(person.name + " has no money.");
        } else {
            System.out.println(person.name + " is rich.");
        }
    }

    private static void check(Person person) {
        System.out.println("Start to check the person: " + person + ", his money is: " + person.money);
        if (person.money <= 0) {
            person = null;
        }
    }

}

Prepare to check the person: com.example.myapplication.Person@5c3bd550, his money is: 0
Start to check the person: com.example.myapplication.Person@5c3bd550, his money is: 0
Bob is rich.

可以看到,两者的内存地址是一模一样的,而且我还特意打印了 Bob 的 money 字段,一直都是0,并没有变化。

后来我又试着在 check() 函数中修改 person 对象的字段,以再次检验传入的对象和 main() 函数中的对象是不是同一个

class Client {
    public static void main(String[] args) {
        ...
        // Check the person, if he has no money, set it as null
        check(person);
        // If the person turned to null, print he has no money, otherwise print he's rich
        if (person == null) {
            System.out.println(person.name + " has no money.");
        } else {
            System.out.println(person.name + " is rich.");
        }
    }

    private static void check(Person person) {
        person.name = "Charlie";
        ...
    }

}

Charlie is rich.

我在 check() 函数中把 person 对象的 name 从 Bob 改成 Charlie,最终 main() 函数中输出了 Charlie is rich.。这更说明了两者是一模一样的,check() 函数中的改动能够反映到 main() 函数中。

解析

如果两者是一模一样的,为什么 check() 函数中将 person 对象置为 null 之后,main() 函数中的对象却没有被置为 null 呢?
这涉及到 Java 值传递的特性。在 Java 中,创建出的对象位于内存中,占据内存的一小块。在代码中传递对象时,程序不是把这块内存区域传来传去,而是传递这块内存区域的地址,只要程序知道内存地址在哪里,就能正确地读写这块区域了
这就是为什么main函数和check函数的person对象的内存地址是一样的了,在java中,向函数中传递参数时,Java 会把内存地址拷贝一份再传递。

举个通俗的例子,对象占用的内存就像一间房子。进入这间房子需要钥匙,钥匙就是内存地址。当需要把对象传递给函数时,Java 会把钥匙复制一把,然后把复制的钥匙交给函数。
所以这个函数拿着这把复制的钥匙也能进入这间房子,也能修改里面的家具、摆件,也就是修改里面的字段。但是这个函数如果把钥匙丢掉,也就是把这个内存地址置为 null,是不会影响其他地方的。因为它拿到的只是一把复制的钥匙而已。
这就叫做值传递。与之相对的,另一种传值方式被称为引用传递。引用传递是指在调用方法时将实际参数的地址直接传递到方法中,那么在方法中对参数所进行的修改,将影响到实际参数。Java 中只有值传递,没有引用传递。

小结

当参数类型是基本数据类型时,传递的是实参的值,因此无法对实参进行修改。
当参数类型是非基本数据类型时,传递的是实参内存地址的拷贝,此时形参和实参都可以对此对象的字段进行修改,但是互相无法影响对方本身。

标签:Java,name,no,money,传递,person,引用,null,check
From: https://www.cnblogs.com/lovexiao/p/17741215.html

相关文章

  • odoo中用javascript调用model中定义好的方法
    odoo中如果前端界面要调用后台model中写好的方法,很简单。使用do_action即可,比如要调用改res.users的默认语言后执行的方法odoo.define('switch_language.SwitchLanguageMenu',function(require){"usestrict";varModel=require('web.Model');varsession......
  • mybatis出现错误 java lang NumberFormatException:For input string:A1
    使用mybatis,当使用map传参并且在iftest判断时使用map中所传的参数时,可能会产生如题的报错,具体报错信息见下图:分析这个错误,自己调试也找过度娘,“坚信”自己代码并没问题,但是问题始终无法解决。最后在一个帖子看到说iftest判断时,传入的参数跟匹配的值类型必须一致,于是调整了自己代......
  • JavaScript(一)
    javascript的组成:ECMAScript和WebAPIs(DOM、BOM) 注释:ctrl+/单行注释shift+alt+a块注释 js的输入输出语法:输出document.weite("hello")//可以带标签,在body内输出alert('alert')//弹窗警告console.log("hello")//控制台输出​输入letname=prompt("你叫:")​//aler()和pro......
  • java---多线程[(重点)上]
    15.1概念以前写的程序都是单线程,main方法程序称为主线程,主线程的结束所有的子线程都会跟着结束。多线程就代表着一个程序可以去做多件事情。线程:一个程序去做多件事情,每件事情由一个线程去完成。进程:一个进程由多个线程组成,一个进程至少有一个线程。一个进程就是一个应用程序多线......
  • java——mysql随笔——运维——日志
    黑马:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=154&vd_source=79bbd5b76bfd74c2ef1501653cee29d6 csdn:https://blog.csdn.net/weixin_44904239/article/details/130379510 ================================================================================......
  • java 通过行为参数化传递代码,来解决不断增长的需求
    1,通过定义不同的谓词接口来区分不同的苹果的重量,如果后续有更多的需求,只需要添加更多的谓词即可packageorg.example;importjava.util.ArrayList;importjava.util.List;enumColor{RED,GREEN,YELLOW}classApple{privateIntegerweight;private......
  • Java的HashMap用法
    一、HashMap了解HashMap是Java中常用的集合类之一,它实现了Map接口并继承自AbstractMap类。HashMap使用哈希表来存储键值对,通过将键映射为哈希码来进行高效的插入、查找和删除操作。以下是HashMap的常见用法和特点:键值对:HashMap允许存储任意类型的键和值。可以通过键来查......
  • java断点下载文件(整合多线程)
    技术介绍:断点下载指的是在文件下载过程中,如果下载中断或失败,比如下载到一半的时候停电了、断网了、不小心退出下载界面了等等,下一次进入下载页面可以从中断或失败的位置继续下载,而无需重新开始下载整个文件。 (注意:本文通过本地文件的拷贝来模拟文件传输的断点过程) 核心想法......
  • 基于Java的高校学生综合测评管理系统的设计与实现(亮点:选课、课程评分、各类活动申请
    (高校学生综合测评管理系统)二、我的优势2.1自己的网站网站上传的项目均为博主自己收集和开发的,质量都可以得到保障,适合自己懂一点程序开发的同学使用!2.2自己的小程序(小蔡coding)<imgsrc="https://img-blog.csdnimg.cn/img_convert/3df3eff92652bb0959df5e3d738d05c9.png"......
  • java本地文件多线程拷贝
    简单介绍:本地文件多线程拷贝是指通过多个线程同时进行文件复制操作。传统的文件复制操作往往是串行进行的,当需要复制单个大文件时,复制速度往往会比较慢。而采用多线程进行文件拷贝可以提高效率。通过同时创建多个线程,每个线程负责复制不同的文件或者不同的文件片段,可以充分利用计......