1. Введение
В разных языках программирования существуют два основных способа передачи параметров в функции (или методы):
- По значению: функция получает копию значения переменной. Если функция изменяет параметр, оригинал не меняется.
- По ссылке: функция получает "ссылку" (указатель) на оригинальный объект. Изменения внутри функции отражаются на оригинале.
В Java ВСЕ параметры передаются по значению!
Но тут есть подвох: когда мы передаём примитивные типы (int, double, boolean и т. д.), копируется само значение. А когда передаём ссылочные типы (например, массивы или объекты), копируется значение ссылки (то есть "адрес" объекта в памяти), но не сам объект.
Если вы чувствуете, что мозг начинает плавиться, не переживайте — сейчас всё разложим по полочкам и покажем на примерах!
2. Примитивные типы: копируется значение
Начнём с простого: что произойдёт, если мы попытаемся изменить значение переменной типа int внутри метода?
public class Demo
{
public static void main(String[] args)
{
int number = 5;
changeValue(number);
System.out.println(number); // Что выведет?
}
public static void changeValue(int n)
{
n = 42;
}
}
Результат: на экран выведется 5.
Почему?
В метод changeValue передаётся КОПИЯ значения переменной number. Внутри метода мы меняем только копию (n), оригинальная переменная number в main остаётся нетронутой.
Аналогия
Представьте, что вы дали другу ксерокопию паспорта, а не оригинал. Друг может рисовать усы на копии сколько угодно — ваш настоящий паспорт останется прежним!
3. Ссылочные типы: копируется ссылка
Теперь попробуем то же самое с массивом:
public class Demo
{
public static void main(String[] args)
{
int[] numbers = {1, 2, 3};
changeFirst(numbers);
System.out.println(numbers[0]); // Что выведет?
}
public static void changeFirst(int[] arr)
{
arr[0] = 99;
}
}
Результат: на экран выведется 99.
Почему?
В метод changeFirst передаётся копия ССЫЛКИ на массив numbers. Оба имени (numbers в main и arr в методе) указывают на один и тот же массив в памяти. Поэтому, если мы меняем элемент массива внутри метода, изменение "видно" снаружи.
Визуализация
main: numbers ──► [1, 2, 3]
▲
│
changeFirst: arr ──┘
Изменив arr[0], мы на самом деле меняем numbers[0], потому что оба указывают на один и тот же объект.
4. Но если поменять саму ссылку — исходный объект не изменится!
Попробуем теперь сделать так:
public class Demo
{
public static void main(String[] args)
{
int[] numbers = {1, 2, 3};
replaceArray(numbers);
System.out.println(numbers[0]); // Что выведет?
}
public static void replaceArray(int[] arr)
{
arr = new int[] {10, 20, 30};
}
}
Результат: выведет 1.
Почему?
В методе replaceArray переменная arr начинает указывать на новый массив, но это никак не влияет на переменную numbers в main.
Они были связаны одной ссылкой, но когда внутри метода мы присваиваем arr = ..., это только локальное изменение.
Аналогия
Это как если бы у вас и у друга был один и тот же ключ от квартиры. Друг сделал себе новый ключ, но ваш старый ключ остался прежним, и дверь осталась та же.
5. Примеры для закрепления: нельзя "поменять местами" два int через метод
Многие новички пытаются написать метод для обмена двух чисел местами, но у них ничего не выходит:
public class Demo
{
public static void main(String[] args)
{
int a = 5, b = 10;
swap(a, b);
System.out.println(a + " " + b); // Ожидание: 10 5, а на самом деле?
}
public static void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
}
Результат: на экран выведется 5 10.
Почему?
Потому что в swap попадают копии переменных a и b. Всё, что мы делаем внутри swap, влияет только на эти копии, а не на оригиналы.
6. Но с массивами можно менять содержимое!
Посмотрим на правильный способ поменять местами значения в массиве:
public class Demo
{
public static void main(String[] args)
{
int[] arr = {5, 10};
swap(arr);
System.out.println(arr[0] + " " + arr[1]); // Ожидание: 10 5
}
public static void swap(int[] arr)
{
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
}
Результат: выведет 10 5.
Почему?
Потому что мы меняем содержимое объекта (массива), на который указывает и оригинальная переменная, и параметр метода.
7. Демонстрация: объекты и их поля
С массивами разобрались. А как насчёт объектов своих классов? Всё точно так же!
class Box
{
int value;
}
public class Demo
{
public static void main(String[] args)
{
Box box = new Box();
box.value = 7;
changeBox(box);
System.out.println(box.value); // Что выведет?
}
public static void changeBox(Box b)
{
b.value = 42;
}
}
Результат: выведет 42.
Почему?
Потому что мы меняем поле объекта, на который указывает ссылка.
Что происходит при передаче параметров в методы
| Что передаём | Как копируется | Можно изменить внутри метода? | Изменения видны снаружи? |
|---|---|---|---|
| int, double и др. | Копия значения | Да, но только копию | Нет |
| Массив | Копия ссылки | Да, если менять элементы | Да |
| Объект (класс) | Копия ссылки | Да, если менять поля | Да |
| Массив/объект | Копия ссылки | Нет, если присвоить новую ссылку | Нет |
8. Типичные ошибки
Ошибка № 1: ожидание, что int поменяется после вызова метода. Многие новички думают, что если передать переменную типа int в метод, то можно поменять её значение внутри метода. Это неверно: меняется только копия!
Ошибка № 2: попытка поменять местами два int через метод. Как мы уже видели, это невозможно — только копии переменных меняются местами, оригиналы не затрагиваются.
Ошибка № 3: попытка "заменить" массив через метод. Если вы внутри метода присвоите параметру-массиву новый массив (arr = new int[]{...}), это не повлияет на оригинальный массив. Чтобы "заменить" массив — нужно вернуть новый массив из метода и присвоить его переменной в вызывающем коде.
Ошибка № 4: забыли, что можно менять содержимое объекта. Если передаёте объект (или массив) — не забывайте, что любые изменения его полей/элементов будут видны снаружи!
Ошибка № 5: путаница с null. Если передать в метод ссылку, равную null, и попытаться обратиться к её полям — получите NullPointerException. Будьте внимательны!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ