JavaRush /Курси /JAVA 25 SELF /Передавання параметрів за значенням і за посиланням

Передавання параметрів за значенням і за посиланням

JAVA 25 SELF
Рівень 8 , Лекція 4
Відкрита

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. Будьте уважні!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ