Перейти к содержимому
Теория · Методы · урок 3 из 8

Параметры — это копии

Параметр — переменная метода, и внутри метода её можно менять. Но что тогда будет с переменной, которую передали при вызове? Изменится ли и она?

Вернёмся на кухню. У нашего прибора есть особенность: он никогда не берёт продукты с вашего стола. Перед работой он кладёт в чашу точную копию, а ваш продукт остаётся на месте. Что бы ни случилось в чаше, на столе всё лежит как лежало.

Посмотрим, как это выглядит в коде. Метод addBonus прибавляет к параметру 10, а переменная points в main остаётся прежней:

JAVA
public class Main {
    static void addBonus(int score) {
        score = score + 10;
        System.out.println(score); // 60
    }

    public static void main(String[] args) {
        int points = 50;
        addBonus(points);
        System.out.println(points); // 50
    }
}

Давайте разберём пример по строкам:

  1. int points = 50; заводит переменную points в main.
  2. Вызов addBonus(points) копирует значение 50 в параметр score.
  3. score = score + 10; меняет копию: в score теперь 60.
  4. addBonus печатает 60 и завершается, а вместе с ним исчезает и score.
  5. main печатает points, и там по-прежнему 50.

Здесь картинка с кухней неточна. Настоящий продукт не скопируешь, а Java копирует значение аргумента при каждом вызове. Для числа это быстрая операция, и вы её не замечаете.

Передача по значению

Такой способ передачи называют передачей по значению: метод получает копию значения аргумента, а не саму переменную.

Вы уже встречали это копирование в теме о переменных: после int b = a; переменная b живёт сама по себе. Вызов addBonus(points) делает то же, что строка int score = points;, только score — параметр метода.

Параметр — обычная локальная переменная метода:

  • её можно читать и менять через = и ++;
  • она появляется при каждом вызове и исчезает, когда метод завершается;
  • с переменной вызывающего кода она никак не связана.

Есть два случая, когда связь только кажется.

Одинаковые имена

Назовите параметр points, как переменную в main, и переменных всё равно будет две. Каждая видна только в своём методе, и запись в одну не трогает другую.

Выражение в аргументе

Аргументом может быть не переменная, а выражение. При вызове addBonus(points + 5) Java сначала вычисляет 55 и копирует в параметр уже готовое число. Метод не знает, из чего это число получилось.

Как вернуть новое значение

Метод связан с вызывающим кодом двумя путями: аргументы на входе и результат на выходе. Чтобы main получил новое число, метод возвращает его через return, а main сам кладёт результат в переменную:

JAVA
public class Main {
    static int withBonus(int score) {
        return score + 10;
    }

    public static void main(String[] args) {
        int points = 50;
        points = withBonus(points);
        System.out.println(points); // 60
    }
}

Разберём main по строкам:

  1. withBonus(points) получает копию числа 50 и возвращает 60.
  2. points = … кладёт этот результат в points.
  3. println печатает 60: переменная изменилась, но изменил её сам main, присваиванием.

Прибор отдал готовое блюдо, а поставили его на место старого вы сами.

Вопрос с собеседования

Этот вопрос часто задают на собеседовании: «Как Java передаёт аргументы — по значению или по ссылке?» Ответ состоит из двух частей:

  • всегда по значению: метод получает копию, а не саму переменную;
  • поэтому присваивание параметру никогда не меняет переменную вызывающего кода.

Правило действует и для объектов — того, что программа создаёт по описанию класса, только копируется у них не сам объект.

Частая ошибка

Написали метод, который должен поменять местами две переменные:

JAVA
public class Main {
    static void swap(int a, int b) {
        int t = a;
        a = b;
        b = t;
    }

    public static void main(String[] args) {
        int x = 1;
        int y = 2;
        swap(x, y); // ✗
        System.out.println(x + " " + y); // 1 2
    }
}

Программа печатает 1 2: переменные не поменялись. Метод swap поменял местами свои копии a и b, а до x и y в main он не дотянулся. В Java метод не может изменить переменные вызывающего кода. Меняйте значения там, где лежат сами переменные: прямо в main, через третью переменную. Почему не работает такой swap, тоже любят спрашивать на собеседовании.

Главное

  • При вызове значение аргумента копируется в параметр — это передача по значению.
  • Изменение параметра меняет только копию, а переменная вызывающего кода остаётся прежней.
  • Java всегда передаёт аргументы по значению, поэтому swap(x, y) не меняет x и y.
  • Чтобы получить новое значение, метод возвращает его, а вызывающий присваивает: points = withBonus(points);.