函数的一点疑惑

函数的一点疑惑

相关截图:

https://img1.sycdn.imooc.com//climg/61e5226909a65ea310640802.jpg


我感觉这个解释不太对吧,JAVA函数的参数,如果是引用类型,如例子中的Node,是对象,那么传入的就是一个地址,这个地址在函数中被改变了那么目的不久达到了吗?
  


正在回答 回答被采纳积分+1

登陆购买课程后可参与讨论,去登陆

1回答
liuyubobobo 2022-01-17 16:20:54

这个问题和这一小节所介绍的问题一模一样,请仔细理解一下,这一小节的介绍的代码错误的原因:https://class.imooc.com/lesson/1667#mid=37628 


这里的关键是,Java 的引用和 C++ 的引用不一样。Java 的引用更像是 C++ 的指针。


==========


在我的另一个课程下,有一个同学问了一个类似的问题,我有详细讲解了一遍,你可以参考。



问题:


老师你好想问您一个java 指代的问题


615630ad09b4030608800474.jpg


我在main里面创建了一个list , 也就{1, 2},  然后我把这个list传递给mystery2, 再在mystery2里main重新赋值成{2, 3},  我不明白为什么在main里面 我打印出来还是{1, 2}而不是{2, 3}


我明白array不是primitive的,所以传递给方法的其实是一个指向该array的地址,那照理说我在mystery2里面重新赋值一个{2,3}, 原本这个地址(也就是存{ 1, 2})的值应该会变成{2, 3} 才对吧, 为什么我直接在一个方法里面改变array的值外面的值就会变,而我重新赋值,它就不会变。


==========


我的回答:


我用你的例子再模拟一遍程序的运行。这里的核心在于,你在 main 里创建的 list 这个引用,和 mystery2 这个函数中的参数 list 引用,是两个变量。为了区分这一点,我把这两个参数名称变得不一样。main 里创建的叫 list,mystery2 的参数叫 argList(因为是个参数)。


class Solution {
     
    public static void mystery2(int[] argList){
        argList = new int[]{2, 3};
        System.out.println(Arrays.toString(argList));
    }
     
    public static void main(String[] args) {
        int[] list = {1, 2};
        mystery2(list);
        System.out.println(Arrays.toString(list));
    }
}


这里的核心是什么?核心在于,当我们 list 传给 mystery2 的时候发生了什么?


答案是,mystery2 创建了一个新的引用,这个新的引用叫 argList,这个新的引用 argList 的赋值是 list,即有 argList = list。此时,list 和 argList 指向了同一个内存空间,即你在 main 开辟的 {1, 2} 的内存空间。用图标是就是这样的:


list ------
          |
          |-----> {1, 2}
          |
argList----


现在,你在 argList 中运行了 argList = new int[](2, 3) 发生了什么?答案是,argList 指向了一个新的内存空间,装载着 {2, 3},但是,argList 的指向变了,不影响 list 的指向。用图标是就是这样的:


list ------
          |
          |-----> {1, 2}
           
argList---------->{2, 3}


当 mystery2 函数结束会发生什么?答案是:argList 这个在 mystery2 中定义的变量,生命周期结束,所以 argList 这个引用变量被销毁。他所指向的内存空间 {2, 3} 由于没有任何引用指向它,也将被 gc 回收。但这一切都不影响 list。


所以,在 main 中,list 依然是 {1, 2}。


这一切等效于下面的程序,list 打印结果仍然是 {1, 2}(只不过 argList 的生命周期没有结束。)



    public static void main(String[] args) {
        int[] list = {1, 2};
         
        int[] argList = list;
        argList = new int[]{2, 3};
         
        System.out.println(Arrays.toString(list));
    }


==========



那么为什么如果我们在 mystery 中,不是改变 argList 的指向,而是改变 argList 的某一项,list 也会变?因为 argList 和 list 指向的是同一个内存空间,所以,改变 argList 指向的内存空间的值,通过 list 也能看到。


请仔细体会他们的区别。


这等效于下面的程序,list 将变成 {1, 3}


    public static void main(String[] args) {
        int[] list = {1, 2};
         
        int[] argList = list;
        argList[1] = 3;
         
        System.out.println(Arrays.toString(list));
    }



==========


请仔细理解这二者的区别。我建议你再回顾一下这一小节,体会一下这一小节我所讲解的问题,和你提出的问题的共性在哪里?https://class.imooc.com/lesson/1580#mid=36148 


(另外,为什么我们在 BST 的各种操作中,改变 node 的值,比如改变 node->val,node->left, node->right,就没有这个问题?本质和你提出的问题是一致的。)


==========


这个问题非常非常重要。在任何语言中都存在,只不过不同的语言,可能表现出的形式不一样。甚至在一些语言中会表现的更复杂,比如 C++ 中。这是因为,Java 的任何非基本类型,都是引用。但是 C++ 中任意一个变量,都有可能是值,或者是指针,或者是引用(C++ 中对应用的定义和 Java 不一样,Java 中的引用相当于 C++ 中的指针),所以情况可能更复杂,但是也更灵活。


继续加油!:)

  • 提问者 小坤2021 #1

    谢谢老师,这么理解对吗

    1.函数运行的时候,先创建一个形参变量,指向实参,对形参的指向的修改,不影响实参的指向
    2.所以可以放心地把引用类型传到函数中,因为函数永远无法修改它原来的指向

    2022-01-17 17:09:35
  • liuyubobobo 回复 提问者 小坤2021 #2

    正确!:)

    2022-01-17 18:35:42
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
计算机基础课
  • 参与学习       233    人
  • 解答问题       159    个

1000位程序员+大厂HR联袂推荐,面向所有程序员的计算机核心知识体系,优惠中~

了解课程
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师