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

В чем разница между передачей параметров по ссылке и по значению?

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

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

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

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

# Передача параметров по значению и по ссылке в Java

Это один из самых важных вопросов для понимания работы Java. Многие разработчики путаются, думая, что Java поддерживает передачу по ссылке, но это не совсем верно. В Java есть только передача по значению, но нюанс в том, что значением может быть ссылка на объект.

Основной принцип: Java передаёт только по значению

В Java ВСЕ параметры передаются по значению. Это означает, что значение копируется в новую переменную. Ключевая разница:

  • Для примитивных типов (int, double, boolean) — копируется сам числовой результат
  • Для объектов — копируется ссылка (адрес в памяти), но не сам объект

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

public class PrimitiveParameterExample {
  
  public static void modifyValue(int value) {
    value = 100;  // Изменяем локальную копию
  }
  
  public static void main(String[] args) {
    int original = 5;
    System.out.println("Before: " + original);  // 5
    
    modifyValue(original);  // Передаём копию значения
    
    System.out.println("After: " + original);   // 5 (не изменилось!)
  }
}

Что происходит:

  1. Создаётся переменная original = 5
  2. При вызове modifyValue(original) значение 5 копируется в параметр value
  3. Внутри метода меняем value = 100
  4. Исходная переменная original остаётся 5, потому что была изменена только копия

2. Передача объектов (передача ссылки по значению)

public class User {
  private String name;
  private int age;
  
  public User(String name, int age) {
    this.name = name;
    this.age = age;
  }
  
  public void setName(String name) {
    this.name = name;
  }
  
  public String getName() {
    return name;
  }
}

public class ReferenceParameterExample {
  
  // Меняем содержимое объекта
  public static void modifyObject(User user) {
    user.setName("John");  // Изменяем существующий объект
  }
  
  // Пытаемся переназначить ссылку
  public static void reassignReference(User user) {
    user = new User("Jane", 30);  // Создаём новый объект локально
  }
  
  public static void main(String[] args) {
    User user = new User("Alice", 25);
    
    System.out.println("Before modifyObject: " + user.getName());  // Alice
    modifyObject(user);
    System.out.println("After modifyObject: " + user.getName());   // John
    
    System.out.println("Before reassignReference: " + user.getName());  // John
    reassignReference(user);
    System.out.println("After reassignReference: " + user.getName());   // John (не Jane!)
  }
}

Важно: метод reassignReference() не может изменить то, на что ссылается переменная user в методе main(). Ссылка в параметре и ссылка в вызывающем коде — это разные переменные.

3. Диаграмма памяти

--- Для примитивного типа ---

int original = 5;
modifyValue(original);

Стек памяти:
┌─────────────┐
│ original: 5 │  <- в main()
└─────────────┘
┌─────────────┐
│ value: 5    │  <- копия в modifyValue()
└─────────────┘
После value = 100:
┌─────────────┐
│ original: 5 │  <- не изменилось
└─────────────┘
┌──────────────┐
│ value: 100   │  <- изменилась только копия
└──────────────┘


--- Для объектного типа ---

User user = new User("Alice", 25);
modifyObject(user);

Стек:                       Куча:
┌──────────────────┐        ┌─────────────────┐
│ user: 0x1234 ────┼───────→│ User {          │
│                  │        │   name: "John" │  <- содержимое изменилось
│ (в main)         │        │   age: 25       │
└──────────────────┘        └─────────────────┘

┌──────────────────┐
│ user: 0x1234 ────┼──────→ тот же объект
│                  │
│ (параметр)       │
└──────────────────┘

Обе ссылки указывают на тот же объект в куче!


--- Для переназначения ссылки ---

User user = new User("Alice", 25);
reassignReference(user);

Стек:                       Куча:
┌──────────────────┐        ┌─────────────────┐
│ user: 0x1234 ────┼───────→│ User {          │
│                  │        │   name: "Alice"│  <- оригинальный объект
│ (в main)         │        │   age: 25       │
└──────────────────┘        └─────────────────┘

┌──────────────────┐        ┌─────────────────┐
│ user: 0x5678 ────┼───────→│ User {          │
│                  │        │   name: "Jane" │  <- новый объект
│ (параметр)       │        │   age: 30       │
└──────────────────┘        └─────────────────┘

Разные ссылки, разные объекты!
Оригинальная ссылка в main() не изменилась.

4. Практический пример: почему это важно

public class CollectionExample {
  
  // Меняем содержимое List
  public static void addToList(List<String> list) {
    list.add("New Item");  // Работает! Меняем исходный список
  }
  
  // Пытаемся создать новый List
  public static void createNewList(List<String> list) {
    list = new ArrayList<>();  // Локальное переназначение
    list.add("New List Item");  // Не повлияет на исходный список
  }
  
  public static void main(String[] args) {
    List<String> items = new ArrayList<>();
    items.add("Item 1");
    
    System.out.println("Before addToList: " + items);  // [Item 1]
    addToList(items);
    System.out.println("After addToList: " + items);   // [Item 1, New Item]
    
    System.out.println("Before createNewList: " + items);  // [Item 1, New Item]
    createNewList(items);
    System.out.println("After createNewList: " + items);   // [Item 1, New Item] (не изменилось!)
  }
}

5. Сравнение языков

Языковая особенностьJavaC/C++Python
ПримитивыПо значениюПо значению (если не &)По значению (immutable)
ОбъектыПо ссылке (по значению)По указателю или по ссылкеПо ссылке
Поддержка refНетДа (& и *)Нет
Можно менять ссылку?Нет в Java (внутри метода)Да (с &)Нет напрямую

6. Частые ошибки и недопонимания

Ошибка 1: Думать, что Java поддерживает pass-by-reference

// НЕВЕРНО: это не работает как pass-by-reference
public static void swap(User a, User b) {
  User temp = a;
  a = b;
  b = temp;
  // a и b внутри метода поменялись, но переменные в main() остались без изменений
}

Ошибка 2: Не различать изменение объекта и переназначение ссылки

// Работает: меняем содержимое объекта
public static void updateUser(User user) {
  user.setName("New Name");  // OK
}

// НЕ работает: переназначение ссылки
public static void replaceUser(User user) {
  user = new User("Another", 30);  // Не повлияет на исходную ссылку
}

7. Когда это важно

Immutable объекты

// String — immutable
public static void modifyString(String str) {
  str = str + " modified";  // Создаёт новую String, не изменяет исходную
}

String text = "Hello";
modifyString(text);
System.out.println(text);  // Hello (не Hello modified)

Mutable объекты

// StringBuilder — mutable
public static void modifyStringBuilder(StringBuilder sb) {
  sb.append(" modified");  // Изменяет исходный объект
}

StringBuilder text = new StringBuilder("Hello");
modifyStringBuilder(text);
System.out.println(text);  // Hello modified

Итоговая таблица

СценарийРезультатПочему
Меняем примитив в методеОригинал не изменяетсяКопируется значение
Меняем поле объекта в методеОригинальный объект изменяетсяСсылка указывает на тот же объект
Переназначаем ссылку в методеОригинальная ссылка не изменяетсяКопируется только значение ссылки, не сама ссылка
Передаём nullПараметр равен nullСсылка может быть null

Заключение

Java использует только передачу по значению:

  • Для примитивов значением является сам примитив
  • Для объектов значением является ссылка (адрес в памяти)

Это означает:

  • ✅ Можешь менять содержимое объекта
  • ✅ Можешь вызывать методы объекта
  • ❌ Не можешь переназначить исходную ссылку из метода
  • ❌ Не можешь заменить исходный примитив

Понимание этого различия критично для правильной работы с Java и избежания неожиданного поведения программ.