Вы когда-нибудь запутывались, почему изменения внутри метода иногда влияют на исходные данные, а иногда — нет? Давайте разберемся раз и навсегда.Главное правило (запомните его): Java передает ВСЕ параметры по значению. Всегда. Без исключений.Передача параметров в Java - 1Да, я знаю, что многие говорят: "Java передает примитивы по значению, а объекты по ссылке". Это не совсем так. Давайте разберемся, почему.

Начнем с простого: примитивы

Представьте, что переменная — это коробка с этикеткой. Для примитивов в этой коробке лежит само значение:
int x = 5;
int y = x;
Что происходит здесь?
  1. Создали коробку x и положили туда число 5
  2. Создали коробку y и скопировали в нее значение из x
Теперь у нас две независимые коробки. Если мы изменим содержимое одной, вторая не пострадает:
int x = 5;
int y = x;

x = 10;

System.out.println(x); // 10
System.out.println(y); // 5 (не изменилось!)
Простое правило: Для примитивов копируется само значение.

Передача примитивов в методы

То же самое происходит при вызове метода:
int x = 5;
doStuff(x);
System.out.println(x); // все еще 5

void doStuff(int y) {
    y = 27; // изменили локальную копию
    // это не влияет на x
}
Метод doStuff получил копию значения x. У метода своя коробка y с копией числа 5. Что бы мы ни делали с y внутри метода, на x это не повлияет.

А как работают объекты?

С объектами все немного интереснее, но принцип тот же. Давайте разберемся.

Что находится в переменной объектного типа?

Когда вы пишете:
Cat murzik = new Cat();
Происходит две вещи:
  1. В памяти (куче) создается объект Cat
  2. Переменная murzik хранит ссылку на этот объект (адрес в памяти)
Важно: В переменной murzik находится не сам кот, а только адрес, где этот кот живет в памяти.

Аналогия с пультом управления

Представьте, что объект — это телевизор, а переменная — пульт от него. Пульт сам по себе не телевизор, но с его помощью вы можете управлять телевизором.
Cat murzik = new Cat(); // создали кота и получили пульт управления им
Что находится в переменной murzik? Пульт (ссылка), а не сам кот (объект).

Копирование ссылок

Теперь посмотрим, что происходит при присваивании:
Cat murzik = new Cat();
Cat barsik = murzik;
Мы создали два пульта для одного и того же телевизора (кота). Теперь у нас:
  • Один объект Cat в памяти
  • Две ссылки (murzik и barsik), которые указывают на этот объект
Если мы что-то изменим через murzik, это повлияет на кота. И когда мы посмотрим через barsik, мы увидим те же изменения:
Cat murzik = new Cat();
murzik.setName("Мурзик");

Cat barsik = murzik; // скопировали ссылку

System.out.println(barsik.getName()); // Мурзик

barsik.setName("Барсик"); // изменили кота через вторую ссылку
System.out.println(murzik.getName()); // Барсик (изменения видны!)
Важно понять: Мы изменили объект, на который указывают ОБЕ ссылки. Сам объект один, просто пультов два.

Передача объектов в методы

Теперь самое интересное. Когда мы передаем объект в метод:
Cat murzik = new Cat();
murzik.setName("Мурзик");

doStuff(murzik);

void doStuff(Cat cat) {
    cat.setName("Барсик"); // ИЗМЕНИТ имя кота!
}
Что произошло?
  1. У нас был пульт murzik
  2. Метод получил копию этого пульта (параметр cat)
  3. Теперь оба пульта управляют одним котом
  4. Изменения через любой пульт влияют на одного и того же кота
Правило: Java копирует ссылку (пульт), а не объект (кот).

Критический момент: изменение самой ссылки

А вот теперь внимание. Что произойдет, если мы внутри метода создадим НОВОГО кота?
Cat murzik = new Cat();
murzik.setName("Мурзик");

doStuff(murzik);

System.out.println(murzik.getName()); // все еще "Мурзик"!

void doStuff(Cat cat) {
    cat = new Cat(); // создали нового кота
    cat.setName("Барсик");
    // это не повлияет на murzik!
}
Что случилось?
  1. Метод получил копию ссылки murzik в параметр cat
  2. Строка cat = new Cat() создала нового кота и переключила пульт cat на него
  3. Теперь cat управляет новым котом, а murzik — старым
  4. Изменения в новом коте никак не влияют на старого
Ключевая идея: Вы можете изменить объект через ссылку, но не можете изменить саму исходную ссылку.

Схема для понимания

Давайте визуализируем:
Cat murzik = new Cat(); // murzik → [Кот в памяти]
doStuff(murzik);

void doStuff(Cat cat) {
    // Сначала: murzik → [Кот в памяти] ← cat
    
    cat.setName("Барсик");
    // Изменили кота: murzik → [Кот: "Барсик"] ← cat
    // Изменения видны через обе ссылки!
    
    cat = new Cat();
    // Переключили cat на нового кота:
    // murzik → [Кот: "Барсик"]
    // cat → [Новый кот]
    // Теперь cat и murzik указывают на разных котов
}

Особый случай: immutable объекты

Некоторые объекты в Java нельзя изменить — они неизменяемые (immutable). Самые известные примеры: String, Integer, Double и другие обертки примитивов.

String — почему он ведет себя как примитив?

String name = "Мурзик";
changeName(name);
System.out.println(name); // все еще "Мурзик"

void changeName(String str) {
    str = "Барсик"; // не изменит исходную строку!
}
Что происходит? String — это объект, но у него нет методов для изменения содержимого. Когда мы пишем str = "Барсик", создается новый объект String, а параметр str просто переключается на него. Исходная переменная name продолжает указывать на старую строку.

Операции со String всегда создают новые объекты

String a = "Hello";
String b = a;

a = a + " World"; // создали НОВУЮ строку "Hello World"

System.out.println(a); // Hello World
System.out.println(b); // Hello (не изменилось!)
Строка a = a + " World" работает так:
  1. Создается новый объект String с содержимым "Hello World"
  2. Переменная a переключается на новый объект
  3. Переменная b продолжает указывать на старый объект "Hello"

То же самое с Integer и другими обертками

Integer x = 5;
changeValue(x);
System.out.println(x); // все еще 5

void changeValue(Integer num) {
    num = 10; // создали новый Integer(10), не изменив исходный
}
Integer неизменяемый. У него нет метода вроде setValue(). Единственный способ "изменить" Integer — создать новый объект.

Почему так сделано?

Неизменяемые объекты дают важные преимущества:
  • Безопасность в многопоточных приложениях
  • Можно использовать как ключи в HashMap
  • Оптимизация памяти (например, String pool)

Если нужно изменять строки

Используйте StringBuilder:
StringBuilder sb = new StringBuilder("Мурзик");
changeName(sb);
System.out.println(sb.toString()); // Барсик (изменилось!)

void changeName(StringBuilder builder) {
    builder.replace(0, builder.length(), "Барсик");
    // StringBuilder изменяемый, поэтому изменения видны
}

Таблица для запоминания

Тип данных Что копируется Изменения в методе влияют на оригинал?
Примитивы (int, double, boolean...) Само значение ❌ Нет
Объекты (Cat, ArrayList...) Ссылка на объект ✅ Да, если изменяем сам объект
Объекты (Cat, ArrayList...) Ссылка на объект ❌ Нет, если переназначаем ссылку
String, Integer, Double... Ссылка на объект ❌ Нет (неизменяемые!)

Практический пример: два разных сценария

Сценарий 1: Изменяем объект — изменения видны

public class Example {
    public static void main(String[] args) {
        Cat murzik = new Cat("Мурзик", 3);
        
        makeOlder(murzik);
        
        System.out.println(murzik.getName()); // Мурзик
        System.out.println(murzik.getAge());  // 4 (изменилось!)
    }
    
    static void makeOlder(Cat cat) {
        cat.setAge(cat.getAge() + 1); // изменили объект
    }
}

Сценарий 2: Переназначаем ссылку — изменения НЕ видны

public class Example {
    public static void main(String[] args) {
        Cat murzik = new Cat("Мурзик", 3);
        
        replaceWithNewCat(murzik);
        
        System.out.println(murzik.getName()); // все еще Мурзик
        System.out.println(murzik.getAge());  // все еще 3
    }
    
    static void replaceWithNewCat(Cat cat) {
        cat = new Cat("Барсик", 5); // создали НОВОГО кота
        // локальная переменная cat теперь указывает на нового кота
        // но это не влияет на murzik в main
    }
}

Частые вопросы (FAQ)

В: Почему String не меняется в методе?

О: String — неизменяемый класс. Любая операция, которая "изменяет" строку, на самом деле создает новый объект. Когда внутри метода вы пишете str = "новое значение", создается новая строка, и локальная переменная переключается на нее. Исходная переменная вне метода продолжает указывать на старую строку.

В: Как передать int в метод так, чтобы изменения были видны?

О: Никак напрямую. Примитивы всегда передаются по значению. Но есть три решения:
  1. Возвращайте новое значение из метода
  2. Используйте массив: int[] arr = {5}; changeValue(arr);
  3. Оберните в изменяемый объект (но это излишне)

В: Можно ли в Java передавать параметры по ссылке?

О: Нет. В Java нет передачи по ссылке в том смысле, как это есть в C++ (с оператором &). Java всегда передает по значению. Для объектов передается значение ссылки (адрес), но не сама ссылка по ссылке.

В: Что произойдет с исходным объектом, если переназначить ссылку внутри метода?

О: Абсолютно ничего. Исходный объект продолжит существовать, и исходная переменная продолжит на него указывать. Переназначение затрагивает только локальную копию ссылки внутри метода.

В: Почему Integer ведет себя как примитив?

О: Integer — это неизменяемый объект-обертка. У него нет методов для изменения значения. Операции вроде num = num + 1 создают новый объект Integer, а не изменяют существующий.

Подведем итоги

Повторите за мной: Java передает все по значению. Всегда. Что это означает на практике:
  • Для примитивов копируется значение
  • Для объектов копируется ссылка (адрес в памяти)
  • Вы можете изменить объект через ссылку
  • Вы НЕ можете изменить саму исходную переменную
  • Неизменяемые объекты (String, Integer) всегда ведут себя как примитивы
Запомните эти правила, и вы никогда не запутаетесь в передаче параметров.