← Назад к вопросам

Как работает передача аргументов по ссылке в методы?

1.2 Junior🔥 141 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Передача аргументов по ссылке в методы Java

Важное уточнение

В Java НЕТ передачи по ссылке в классическом понимании! Java использует Call-by-Value для всех типов данных, но для объектов передаётся значение ссылки (адрес в памяти).

Многие ошибочно называют это "передачей по ссылке", но это неправильно. Правильнее сказать: передача по значению ссылки.

Примитивные типы: передача по значению

public static void main(String[] args) {
    int x = 10;
    changeValue(x);
    System.out.println(x); // Печатает 10 (не изменилось!)
}

public static void changeValue(int num) {
    num = 20; // Изменяем копию
}

Что происходит в памяти:

main():             changeValue():
┌─────────┐       ┌─────────┐
│ x = 10  │────→  │ num = 10│ (копия значения)
└─────────┘       └─────────┘
                   num = 20 (меняем копию)

Оригинальная переменная x остаётся 10.

Объекты: передача по значению ссылки

public static void main(String[] args) {
    User user = new User("John");
    changeUser(user);
    System.out.println(user.name); // Печатает "Jane" (ИЗМЕНИЛОСЬ!)
}

public static void changeUser(User u) {
    u.name = "Jane"; // Меняем данные объекта
}

class User {
    String name;
    User(String name) { this.name = name; }
}

Что происходит в памяти:

main():                    changeUser():
┌──────────────┐          ┌──────────────┐
│ user ────────┼─────────→ │ u (копия) ──┼─────────→ [Объект User]
│              │          │              │         name="Jane"
└──────────────┘          └──────────────┘

(ссылка скопирована)      (указывает на ОТ ЖЕ объект)

Обе ссылки указывают на ОДИН объект, поэтому изменение видно везде.

Различие: попытка изменить саму ссылку

public static void main(String[] args) {
    User user = new User("John");
    reassignUser(user);
    System.out.println(user.name); // Печатает "John" (не изменилось!)
}

public static void reassignUser(User u) {
    u = new User("Jane"); // Меняем копию ссылки, не оригинал
}

Почему не работает:

main():                    reassignUser():
┌──────────────┐          ┌──────────────┐
│ user ────────┼─────────→ │ u (копия) ──┼─────────→ [Новый объект]
│              │          │              │         name="Jane"
└──────────────┘          └──────────────┘
                          (копия ссылки переназначена)

Оригинальная ссылка в main остаётся как была!

Таблица: что меняется, что нет

ДействиеПримитивыОбъектыРезультат
Изменить значение переменнойНетНетНе видно в caller
Изменить поле объекта-ДаВидно в caller
Переназначить переменнуюНетНетНе видно в caller

Практические примеры

Пример 1: Попытка поменять два объекта (не работает)

public static void main(String[] args) {
    User user1 = new User("John");
    User user2 = new User("Jane");
    
    swap(user1, user2);
    
    System.out.println(user1.name); // John (не поменялось)
    System.out.println(user2.name); // Jane (не поменялось)
}

public static void swap(User a, User b) {
    User temp = a;
    a = b;      // Меняем копию ссылки a
    b = temp;   // Меняем копию ссылки b
    // Оригинальные ссылки в main не изменились
}

Пример 2: Изменение списка внутри метода

public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("Alice");
    
    addName(names);
    System.out.println(names); // [Alice, Bob] (изменилось!)
}

public static void addName(List<String> list) {
    list.add("Bob"); // Меняем содержимое объекта
}

Исходная ссылка указывает на ТОТ ЖЕ список, поэтому изменения видны.

Пример 3: Переассign списка (не работает)

public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("Alice");
    
    reassignList(names);
    System.out.println(names); // [Alice] (не изменилось)
}

public static void reassignList(List<String> list) {
    list = new ArrayList<>();
    list.add("Bob"); // Добавляем в новый список
    // Оригинальная ссылка остаётся на старый список
}

Массивы: передача по значению ссылки

public static void main(String[] args) {
    int[] arr = {1, 2, 3};
    
    modifyArray(arr);
    System.out.println(Arrays.toString(arr)); // [10, 2, 3]
}

public static void modifyArray(int[] a) {
    a[0] = 10; // Меняем элемент массива
}

Immutable объекты: особенности

public static void main(String[] args) {
    String str = "Hello";
    changeString(str);
    System.out.println(str); // "Hello" (не изменилось)
}

public static void changeString(String s) {
    s = "World"; // String — immutable, создаётся новый объект
}
String immutable, поэтому операции над ним создают новый объект, а не меняют оригинал.

Почему Java не имеет истинной передачи по ссылке

В C++:

void increment(int& x) { // Истинная передача по ссылке (&)
    x++;
}

int main() {
    int a = 5;
    increment(a);
    cout << a; // 6 (изменилось)
}

В Java нет оператора & для передачи по ссылке. Java всегда копирует значение (для примитивов) или копирует ссылку (для объектов).

Итоговые правила

  1. Примитивы: Всегда передаются по значению. Изменения внутри метода не видны снаружи.
  2. Объекты: Ссылка передаётся по значению. Изменения полей объекта видны снаружи. Переассигнмент ссылки не видна снаружи.
  3. Массивы: Как объекты — передаётся копия ссылки, но содержимое изменяется.
  4. Обёртки (Integer, String): Immutable, поэтому похожи на примитивы в использовании.

Желаемый запомнить принцип: В Java всё передаётся по значению. Для объектов это значение — адрес (ссылка) в памяти.