Java, передача по значению, ссылочные переменные - PullRequest
2 голосов
/ 31 января 2009

У меня проблема с пониманием действия 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. Это опять ссылочная переменная, я даю ее методу calc (), а в методе я меняю ссылку на t_ccc, чтобы она была ссылкой s_ccc. Теперь t_ccc должен быть ссылочной переменной, указывающей на массив, который имеет один элемент типа int, равный 16. Но когда оставлен метод Calculate (), кажется, что t_ccc указывает на его старый объект. Почему это происходит? Разве изменения не должны остаться для него тоже? В конце концов, это ссылочная переменная.

Привет

Ответы [ 5 ]

7 голосов
/ 31 января 2009

Вот простой способ понять это.

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_ccc и t_ccc соответственно.

Все еще в пределах calculcate, после того как b[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 не были изменены; каждый из них по-прежнему ссылается на тот же массив, что и раньше calculate. Вызов calculate изменил содержимое массива, на который ссылается s_ccc, используя скопированную ссылку на этот массив (в b).

Локальная переменная c начиналась как копия t_ccc и манипулировалась в calculate, но это не изменило ни самого t_ccc, ни его ссылочных данных.

7 голосов
/ 31 января 2009

Существует расширенное обсуждение того, как Java передает переменные в предыдущем вопросе " Является ли Java передачей по ссылке? ". Java действительно передает объектные ссылки по значению .

В вашем коде ссылки на массивы (которые являются объектами) передаются в calculate(). Эти ссылки передаются по значению, что означает, что любые изменения значений b и c видны только внутри метода (на самом деле они являются просто копией s_ccc и t_ccc). Вот почему t_ccc в main() никогда не было затронуто.

Чтобы подкрепить эту концепцию, некоторые программисты объявляют параметры метода как final переменные:

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

Теперь компилятор даже не позволит вам изменить значения b или c. Конечно, недостатком этого является то, что вы больше не можете удобно ими манипулировать в своем методе.

6 голосов
/ 31 января 2009

Метод получает переменные по значению. Эти значения нельзя изменить (насколько видит вызывающий метод), но содержащиеся в них значения могут (если это объект или, как в данном случае, массив).

Таким образом, когда вы изменяете значение b [0] внутри массива, это изменение можно увидеть за пределами метода. Однако линия

c = b;

изменит значение c внутри метода, но это изменение не будет видно за пределами метода, так как значение c было передано по значению.

5 голосов
/ 31 января 2009

Строка calculate(s_ccc, s_ccc); указывает, что вы на самом деле ничего не делаете с t_ccc. Однако, если эта строка прочитала calculate(s_ccc, t_ccc);, эффект остается прежним.

Это связано с тем, что вы назначаете здесь новое значение c: c = b;
При присвоении нового значения аргументу ссылки ссылка теряется.
В этом разница между изменением ссылочной переменной и назначением нового значения.

1 голос
/ 31 января 2009

При входе в calculate(), b точек на s_ccc и c точек на 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.

...