【问题标题】:Java, pass-by-value, reference variablesJava,按值传递,引用变量
【发布时间】:2010-10-04 16:00:18
【问题描述】:

在以下示例中,我在理解 Java 的“按值传递”操作时遇到了问题:

public class Numbers {

    static int[] s_ccc = {7};
    static int[] t_ccc = {7};

    public static void calculate(int[] b, int[] c) {

        System.out.println("s_ccc[0] = " + s_ccc[0]); // 7
        System.out.println("t_ccc[0] = " + t_ccc[0]); // 7

        b[0] = b[0] + 9;  
        System.out.println("\nb[0] = " + b[0]); // 16

        c = b;
        System.out.println("c[0] = " + c[0] + "\n"); // 16
    }

    public static void main(String[] args) {

        calculate(s_ccc, t_ccc);

        System.out.println("s_ccc[0] = " + s_ccc[0]);  // 16
        System.out.println("t_ccc[0] = " + t_ccc[0]);  // 7 
    }
}

我知道因为 s_ccc 是一个引用变量,所以当我将它提供给方法 calculate() 并在方法中对其元素进行一些更改时,即使在我离开该方法后更改仍然存在。我认为 t_ccc 也应该如此。又是 一个引用变量,我把它交给方法calculate(),在方法中我把t_ccc的引用改为s_ccc的引用。现在 t_ccc 应该是一个指向数组的引用变量,该数组有一个 int 类型的元素等于 16。但是当方法 calculate() 离开时,似乎 t_ccc 指向它的旧对象。为什么会这样?改变不应该也保留吗?毕竟是引用变量。

问候

【问题讨论】:

  • 您的意思是calculate(s_ccc, t_ccc) 而不是calculate(s_ccc, s_ccc)?
  • 是的,完全正确!我的意思是计算(s_ccc,t_ccc)。对不起。
  • @gemm:你能修改一下你的帖子吗?

标签: java reference pass-by-value


【解决方案1】:

在较早的问题“Is Java pass by reference?”中有关于 Java 如何传​​递变量的扩展讨论。 Java 确实按值传递对象引用。

在您的代码中,数组(即对象)的引用被传递到calculate()。这些引用是按值传递的,这意味着对bc 值的任何更改仅在方法内可见(它们实际上只是s_ccct_ccc 的副本)。这就是为什么main() 中的t_ccc 从未受到影响。

为了强化这个概念,一些程序员将方法参数声明为final 变量:

public static void calculate(final int[] b, final int[] c) 

现在,编译器甚至不允许您更改bc 的值。当然,这样做的缺点是您不能再在您的方法中方便地操作它们。

【讨论】:

    【解决方案2】:

    这里有一个简单的理解方式。

    Java 总是传递参数的副本。如果参数是原始类型(例如整数),则被调用的方法会获取原始值的副本。如果参数是引用类型,则被调用的方法会获取引用的副本(不是所引用事物的副本)。

    当您的main 方法开始时,s_ccct_ccc 中的每一个都引用一个不同的数组。 就是这种情况,括号表示变量,方括号表示实际的数组结构:

    (s_ccc) ---> [7]
    (t_ccc) ---> [7]
    

    假设你的意思是calculate(s_ccc, t_ccc),那么在calculate方法的开头:

    (s_ccc) ---> [7]  <---(b)
    (t_ccc) ---> [7]  <---(c)
    

    局部变量bc 分别是全局变量s_ccct_ccc副本

    仍在calculcate 内,在b[0] = b[0] + 9 完成后:

    (s_ccc) ---> [16] <---(b)
    (t_ccc) ---> [7]  <---(c)
    

    b引用的数组中的零(唯一)位置已被修改。

    calculate 中的赋值 c = b 会产生这种情况:

    (s_ccc) ---> [16] <---(b)
                   ^------(c)
    (t_ccc) ---> [7]
    

    局部引用变量c,现在包含与b 相同的引用。这对全局引用变量t_ccc 没有影响,它仍然引用与以前相同的数组。

    calculate 退出时,它的局部变量(在图表的右侧)消失了。 calculate 中没有使用全局变量(左侧),因此它们不受影响。最后的情况是:

    (s_ccc) ---> [16]
    (t_ccc) ---> [7]
    

    c_ccct_ccc 均未更改;每个仍然引用与之前相同的数组calculate。调用calculate 更改了s_ccc 引用的数组的内容,使用了对该数组的复制引用(在b 中)。

    局部变量c 开始是作为t_ccc副本,并在calculate 中进行了操作,但这既没有改变t_ccc 本身,也没有改变它的引用数据。

    【讨论】:

      【解决方案3】:

      该方法按值接收变量。这些值无法更改(就方法的调用者而言),但其中包含的值可以(如果它是一个对象,或者在本例中是一个数组)。

      所以当你改变数组内部的值 b[0] 时,可以在方法外部看到变化。然而这条线

      c = b;
      

      会在方法内部改变 c 的值,但是这种改变不会在方法外部看到,因为 c 的值是按值传递的。

      【讨论】:

        【解决方案4】:

        calculate(s_ccc, s_ccc); 行表明您实际上并没有对 t_ccc 做任何事情。 但是,如果此行已读取calculate(s_ccc, t_ccc);,则效果保持不变。

        这是因为您在此处为 c 分配了一个新值:c = b;
        为引用参数分配新值时,引用会丢失。
        这就是通过引用更改变量和分配新值之间的区别。

        【讨论】:

          【解决方案5】:

          进入calculate()b 指向s_cccc 指向t_ccc。如您所见,更改b[0] 将因此更改s_ccc

          但是,任务

          c = b;
          

          只有c指向b指向的同一个对象,即s_ccc。它不会将b 指向的内容复制到c 指向的内容。因此,t_ccc 保持不变。如果您在calculate(): 末尾添加了以下行

          c[0] = c[0] + 5;
          

          s_ccc[0] 将是 21。

          【讨论】:

            猜你喜欢
            • 2010-09-05
            • 1970-01-01
            • 2014-04-12
            • 2013-07-22
            • 2016-11-28
            相关资源
            最近更新 更多