【问题标题】:Call by reference or Call by value按引用调用或按值调用
【发布时间】:2013-08-15 07:37:43
【问题描述】:

谁能解释一下这个程序是如何执行的?

这是我似乎无法理解其输出的代码:

    class Box {
        int size;
        Box (int s) {
           size = s;
        }
    }
    public class Laser {
        public static void main(String[] args) {
            Box b1 = new Box(5);
            Box[] ba = go(b1, new Box(6));
            ba[0] = b1;
            for(Box b : ba)
                System.out.println(b.size + " ");
        }

        static Box[] go (Box b1, Box b2) {
            b1.size = 4;
            Box[] ma = {b2, b1};
            return ma;
        }
    }

我运行时的实际输出是 4, 4。但根据我的理解应该是 5, 4。 任何人都可以帮助了解这是如何执行的吗?

【问题讨论】:

  • Is Java "pass-by-reference"? 的可能重复项
  • b1.size 引用的对象与使用 Box b1 = new Box(5); 创建的对象相同
  • 理解这一点的最好方法是拿起铅笔和纸,开始画出发生了什么......
  • 详细解释请查看我的回答。

标签: java static-methods pass-by-reference call-by-value


【解决方案1】:

在 Java 中,对象通过引用传递,基元通过值传递。

public class Laser {
    public static void main(String[] args) {
        //Create a Box of size 5.
        Box b1 = new Box(5);
        //Create an array of Box objects, which are the results of the go method
        Box[] ba = go(b1, new Box(6));
        // ba now looks like {new Box(6), b1 /*b1 is size 4 now*/}
        // assign reference to b1 at index 0 in ba.
        ba[0] = b1;
        // array now looks like {b1, b1}
        for(Box b : ba)
            System.out.println(b.size + " ");
    }

    static Box[] go (Box b1, Box b2) {
        //set the size of the first box to 4
        b1.size = 4;
        //create an array of boxes, with b2 in index 0 and b1 in index 1
        Box[] ma = {b2, b1};
        return ma;
    }
}

【讨论】:

  • 对象也是按值传递的。
  • 一个对象是一个引用。在 Java 中它们是按值传递的。
  • 对象不是对象引用。对象就是对象,引用就是引用。方法参数是按值传递的对象引用。
  • @chrylis 这是对我评论的回答吗?您能否向我展示一个示例,说明您可以在哪里访问“对象”(如您所说)而不仅仅是访问它的引用?在 Java 中,如果您愿意,所有对象都是引用或指针
  • @SotiriosDelimanolis 同意了。在特定的上下文中,尽管谈论 Call by ReferenceCall by Value 语义(将您对引用的定义与此答案的第一行进行比较:“对象通过引用传递” 以了解我在说什么),对象的行为类似于引用(我会使用术语指针更清楚),即您只能间接访问它的内存。无论如何,这一切都归结为“参考”这个词对每个人意味着什么:)
【解决方案2】:

当您将对象作为参数传递时,您实际上是在传递对它的一个引用的副本。也就是说,如果您在方法内部修改参数对象,则该对象将在该方法返回时保留这些修改,这就是为什么您看到 b1go 返回后保留了 size = 4 赋值。

【讨论】:

  • 您实际上是在传递引用值的副本。
  • 我以为Java传递的是内存中对象的地址,或者换句话说,是对对象位置的引用?
  • 因为Java是传值总是,它实际上是传递地址的值的副本。
【解决方案3】:

Java 总是在方法调用中传递对对象的引用。当您调用 go 时,第一个参数 (b1) 是对您在 main 中的相同 b1 Box 的引用。然后修改该对象,其size 现在是4

【讨论】:

  • 这不是同一个参考。它是引用值的副本。
  • 我没有说它是“相同的参考”,不管那是什么意思。它是对同一对象的(n 个相同的)引用。
  • 我认为你应该指定引用是一个副本(因为它是),即使它具有相同的值。如果您更改参考,原件保持不变。
  • 我们可以链接到这个问题的标准答案吗?虽然理解这一点很重要,但这个问题每天都会出现,并立即演变成一场迂腐的火焰战争。 ;-)
  • 这并不意味着 Java 规范没有明确说明它是按值传递的。
【解决方案4】:

我已经为你添加了 cmets;

The important TWIST that you missed here is;

Box[] ma = {b2, b1}; 不是{b1,b2}。返回时交换内存位置。

       public static void main(String[] args) {
            Box b1 = new Box(5);                 // b1 == location A
            Box[] ba = go(b1, new Box(6));       // ba == location B which stores Location A, D

           // PLEASE NOTE HERE
           // After the function go() is executed;
           // ba[] will have {D, A}
           // So ba[0] will have object b1 which is at location A. 

            ba[0] = b1;                          // location B will now store A and A
            for(Box b : ba)
                System.out.println(b.size + " ");  //  Output: 4 and 4
        } 

        static Box[] go (Box b1, Box b2) {        // go(location A, location D )
            b1.size = 4;                          // A's object.size = 4
            Box[] ma = {b2, b1};                  // position is interchanged here (D and A)
            return ma;                            // return the location of ma
        }

希望这会有所帮助。

如果您有任何问题,请告诉我。在java中,一切都是按值传递的。内存地址按值传递。

【讨论】:

  • 虽然您是对的,但我相信他在问为什么ba[0] 的大小不等于0,因为他使用的是b1 对象,该对象之前声明的大小为5 两行。关键是一堆其他答案都在探讨的整个参数值范式。
  • @Deactivator2 是的,我同意,这绝对是价值传递。
【解决方案5】:

传递给go 的值是对象的引用。如果您来自 C,您可以将参数视为具有指针类型,类似于

Box** go (Box *b1, Box *b2) {
     b1->size = 4;
     /* allocate an array of Box*, set it up, return it */
}

(对不起,如果我把 C 语法弄错了。)指针(引用)本身是按值传递的,这意味着如果你在“go”(在 C 程序中)说:

b1 = &some_other_box;

它不会影响调用端的任何变量,并且它在 Java 中的工作方式相同。这使它与 PHP 或 Pascal 中的 var 参数有点不同。

【讨论】:

    【解决方案6】:

    Java 在方法调用中将引用的副本传递给对象。

    你不能通过赋值来改变一个对象。

    public static void main(String[] args) {
        Box b1 = new Box(5);
        Change(b1);
        System.out.println(b1.size + " ");
    
    static void Change(Box b1) {
        b1 = new Box(6);
    }
    

    这将始终返回 5,因为函数 Change 只会将副本覆盖到引用的对象。

    但是,您可以像这样影响对象的属性:

    public static void main(String[] args) {
        Box b1 = new Box(5);
        Change(b1);
        System.out.println(b1.size + " ");
    
    static void Change(Box b1) {
        b1.size = 6;
    }
    

    这将返回 6(对对象的引用保持不变)。

    【讨论】:

      【解决方案7】:

      如果原帖中的函数 'go' 在 C 中(并且 b1 和 b2 是 C 指针),它可以做以下两件事(调用者会看到):

      1. 更改 b1 和 b2 指向的数据结构中的变量值。
      2. 让原来的 b1 和 b2 指向不同的数据结构。

      在 Java 中,我们可以做 1,但不能做 2,因为 go 无法访问原始的 b1 和 b2。 (它可以访问 b1 和 b2 的副本。在子程序开始时,副本 b1 指向与原始 b1 相同的对象,依此类推。)

      说Java是通过调用传递确实错过了一些东西,即函数做1的能力,即改变b1和b2引用的堆中对象的属性。 [这就是语句中发生的事情,b1.size = 4;,导致原发帖者的困惑。]

      'go' 中的 b1 与 'main' 中的 b1 位置不同。但是,当函数启动时,'go' 中的 b1 引用与 'main' 中的 b1 相同的对象,并且当使用 'main' 中的 b1 作为引用时,将看到使用 'go' 中的 b1 对对象所做的任何更改。

      但是,如果 'go' 中的 b1 设置为另一个 Box,则 'main' 中的 b1 不会看到这个;它仍然会看到以前的 Box。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-24
        • 2014-02-09
        • 2021-04-25
        • 2013-12-15
        • 2015-06-07
        相关资源
        最近更新 更多