Java,按值传递,引用变量

Posted

技术标签:

【中文标题】Java,按值传递,引用变量【英文标题】:Java, pass-by-value, reference variables 【发布时间】: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:你能修改一下你的帖子吗? 【参考方案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。

【讨论】:

以上是关于Java,按值传递,引用变量的主要内容,如果未能解决你的问题,请参考以下文章

Java 是“按引用传递”还是“按值传递”?

Java 是“按引用传递”还是“按值传递”?

Java 是“按引用传递”还是“按值传递”?

Java 是“按引用传递”还是“按值传递”?

java按值传递?

Python按值传递参数和按引用传递参数