Параметры — это копии
Параметр — переменная метода, и внутри метода её можно менять. Но что тогда будет с переменной, которую передали при вызове? Изменится ли и она?
Вернёмся на кухню. У нашего прибора есть особенность: он никогда не берёт продукты с вашего стола. Перед работой он кладёт в чашу точную копию, а ваш продукт остаётся на месте. Что бы ни случилось в чаше, на столе всё лежит как лежало.
Посмотрим, как это выглядит в коде. Метод addBonus прибавляет к параметру 10, а переменная points в main остаётся прежней:
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
}
}Давайте разберём пример по строкам:
int points = 50;заводит переменнуюpointsвmain.- Вызов
addBonus(points)копирует значение50в параметрscore. score = score + 10;меняет копию: вscoreтеперь60.addBonusпечатает60и завершается, а вместе с ним исчезает иscore.mainпечатаетpoints, и там по-прежнему50.
Здесь картинка с кухней неточна. Настоящий продукт не скопируешь, а Java копирует значение аргумента при каждом вызове. Для числа это быстрая операция, и вы её не замечаете.
Передача по значению
Такой способ передачи называют передачей по значению: метод получает копию значения аргумента, а не саму переменную.
Вы уже встречали это копирование в теме о переменных: после int b = a; переменная b живёт сама по себе. Вызов addBonus(points) делает то же, что строка int score = points;, только score — параметр метода.
Параметр — обычная локальная переменная метода:
- её можно читать и менять через
=и++; - она появляется при каждом вызове и исчезает, когда метод завершается;
- с переменной вызывающего кода она никак не связана.
Есть два случая, когда связь только кажется.
Одинаковые имена
Назовите параметр points, как переменную в main, и переменных всё равно будет две. Каждая видна только в своём методе, и запись в одну не трогает другую.
Выражение в аргументе
Аргументом может быть не переменная, а выражение. При вызове addBonus(points + 5) Java сначала вычисляет 55 и копирует в параметр уже готовое число. Метод не знает, из чего это число получилось.
Как вернуть новое значение
Метод связан с вызывающим кодом двумя путями: аргументы на входе и результат на выходе. Чтобы main получил новое число, метод возвращает его через return, а main сам кладёт результат в переменную:
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 по строкам:
withBonus(points)получает копию числа50и возвращает60.points = …кладёт этот результат вpoints.printlnпечатает60: переменная изменилась, но изменил её самmain, присваиванием.
Прибор отдал готовое блюдо, а поставили его на место старого вы сами.
Вопрос с собеседования
Этот вопрос часто задают на собеседовании: «Как 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);.