2

在以下示例中,我无法理解 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 指向它的旧对象。为什么会这样?改变不应该也保留吗?毕竟它是一个参考变量。

问候

4

5 回答 5

7

这里有一个简单的方法来理解它。

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

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

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

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

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

局部变量b和分别是全局变量和c副本s_ccct_ccc

仍然在calculcateb[0] = b[0] + 9完成后:

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

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

c = b内的赋值calculate产生了这种情况:

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

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

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

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

既没有c_ccc也没有t_ccc改变;each 仍然引用与以前相同的数组calculate。调用calculate更改了由 引用的数组的内容s_ccc,使用对该数组的复制引用(在 中b)。

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

于 2009-01-31T14:22:49.293 回答
7

在较早的问题“ Java 是否通过引用传递? ”中对 Java 如何传​​递变量进行了扩展讨论。Java 确实按值传递对象引用。

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

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

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

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

于 2009-01-31T12:41:05.333 回答
6

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

因此,当您更改数组内部的值 b[0] 时,可以在方法外部看到更改。然而线

c = b;

将在方法内部更改 c 的值,但在方法外部不会看到该更改,因为 c 的值是按值传递的。

于 2009-01-31T12:42:06.953 回答
5

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

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

于 2009-01-31T12:43:33.130 回答
1

在进入 时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 岁。

于 2009-01-31T12:52:43.063 回答