ООП ·
‹ Предыдущий Следующий ›
⏱ 5 минут чтения Обновлено: 2026-07-28

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

Объекты в Java передаются методам как параметры точно так же, как и примитивные значения. Разница проявляется в результате: изменения полей объекта, сделанные внутри метода, видны в вызвавшем методе, а изменения примитивного параметра — нет. Разберём, почему так происходит, где эта логика перестаёт работать и как объект возвращают из метода.

По ссылке или по значению

Java всегда передаёт аргументы по значению (pass-by-value). Для примитивного параметра копируется само значение (10, 3.14, true). Для объектного параметра копируется ссылка — адрес объекта в куче, а не сам объект.

Именно поэтому копия ссылки указывает на тот же самый объект: через неё метод может изменить состояние объекта, и это изменение увидят все, кто держит ссылку на него. На практике эту модель часто описывают фразой «объекты передаются по ссылке» — она удобна, но неточна, и на собеседовании её лучше уточнять.

Точная формулировка

В Java нет передачи по ссылке (pass-by-reference), как в C++ или C#. Передаётся копия ссылки на объект. Отсюда два следствия: изменить состояние объекта внутри метода можно, а вот заставить переменную вызывающего кода указывать на другой объект — нельзя.

Пример 1. Изменение примитивов и объекта в методе

При запуске программы в стеке выделяется область памяти для метода main(). Там хранятся локальные переменные x и y. При вызове метода changePrimitives() в стеке выделяется ещё один блок памяти, в котором создаются локальные переменные a и b — копии переданных значений. Изменение a и b никак не влияет на x и y: это разные ячейки памяти.

Локальная переменная box тоже создаётся в стеке метода main(), но сам объект Box живёт в куче. В метод changeObject() попадает копия ссылки, указывающая на тот же объект. Поэтому изменение полей box.width, box.height, box.depth внутри метода меняет объект в куче, и результат виден в main():

public class Box {
    double width;
    double height;
    double depth;

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }
}
public class TestDemo {

    // Меняет состояние объекта: изменения видны в вызывающем методе
    static void changeObject(Box box) {
        box.width *= 2;
        box.height /= 2;
        box.depth += 2;
    }

    // Меняет копии значений: изменения НЕ видны в вызывающем методе
    static void changePrimitives(int a, int b) {
        a *= 2;
        b /= 2;
    }

    public static void main(String[] args) {
        Box box = new Box(5, 6, 7);

        int x = 10;
        int y = 10;
        System.out.println("x и y перед вызовом: " + x + " " + y);
        changePrimitives(x, y);
        System.out.println("x и y после вызова: " + x + " " + y);

        System.out.println("box перед вызовом: " + box.width + " " + box.height + " " + box.depth);
        changeObject(box);
        System.out.println("box после вызова: " + box.width + " " + box.height + " " + box.depth);
    }
}

Результат выполнения:

x и y перед вызовом: 10 10
x и y после вызова: 10 10
box перед вызовом: 5.0 6.0 7.0
box после вызова: 10.0 3.0 9.0

Где интуиция подводит: переприсваивание параметра

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

public class ReassignDemo {

    // Параметру присваивается новый объект — снаружи ничего не меняется
    static void reassign(Box box) {
        box = new Box(1, 1, 1);
        box.width = 100;
    }

    // Меняется состояние переданного объекта — снаружи изменение видно
    static void mutate(Box box) {
        box.width = 100;
    }

    public static void main(String[] args) {
        Box first = new Box(5, 6, 7);
        reassign(first);
        System.out.println("после reassign: " + first.width);

        Box second = new Box(5, 6, 7);
        mutate(second);
        System.out.println("после mutate: " + second.width);
    }
}

Результат выполнения:

после reassign: 5.0
после mutate: 100.0

Сведём поведение в одну таблицу — она отвечает на вопрос «увидит ли вызывающий метод изменение?»:

Что передали Что сделали внутри метода Видно снаружи? Почему
int, double, boolean и другие примитивы Изменили значение параметра Нет В метод скопировано само значение
Ссылка на изменяемый объект (Box, int[], StringBuilder, ArrayList) Изменили поле или элемент объекта Да Копия ссылки указывает на тот же объект в куче
Ссылка на объект Присвоили параметру новый объект Нет Переприсвоена только локальная копия ссылки
String, Integer и другие неизменяемые типы «Изменили» через + или переприсваивание Нет Объект неизменяем — создаётся новый

Массивы, String и StringBuilder

Массив в Java — тоже объект, поэтому метод, получивший массив, может менять его элементы, и вызывающий код увидит новые значения. А вот String неизменяем (immutable): операция text += " Java" внутри метода создаёт новую строку и присваивает её локальному параметру, оригинал остаётся прежним. Если строку нужно «нарастить» в методе — используйте StringBuilder либо возвращайте результат:

public class ArrayStringDemo {

    static void fill(int[] numbers) {
        numbers[0] = 42;            // массив — объект, изменение видно снаружи
    }

    static void addSuffix(String text) {
        text += " Java";            // создаётся новая строка, оригинал не меняется
    }

    static void addSuffix(StringBuilder text) {
        text.append(" Java");       // изменяется тот же объект
    }

    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        fill(numbers);
        System.out.println("numbers[0]: " + numbers[0]);

        String text = "Hello";
        addSuffix(text);
        System.out.println("text: " + text);

        StringBuilder builder = new StringBuilder("Hello");
        addSuffix(builder);
        System.out.println("builder: " + builder);
    }
}

Результат выполнения:

numbers[0]: 42
text: Hello
builder: Hello Java

Кстати, два метода addSuffix() с разными типами параметров — это перегрузка методов: компилятор выбирает нужный вариант по типу аргумента.

На заметку

Модификатор final у параметра (void changeObject(final Box box)) запрещает переприсваивать параметр, но не защищает объект: box.width = 100; по-прежнему сработает. Если метод не должен менять чужие данные — работайте с защитной копией объекта или используйте неизменяемые типы.

Пример 2. Возврат объекта из метода

Объекты можно не только принимать, но и создавать внутри метода и возвращать наружу. Возвращается при этом ссылка на объект в куче — объект не копируется. Метод incrementByTen() не меняет текущий объект, а создаёт новый с увеличенным значением поля:

public class ReturnObjectExample {
    int a;

    ReturnObjectExample(int i) {
        a = i;
    }

    ReturnObjectExample incrementByTen() {
        ReturnObjectExample temp = new ReturnObjectExample(a + 10);
        return temp;
    }

    public static void main(String[] args) {
        ReturnObjectExample ob1 = new ReturnObjectExample(2);
        ReturnObjectExample ob2 = ob1.incrementByTen();
        System.out.println("ob1.a: " + ob1.a);
        System.out.println("ob2.a: " + ob2.a);
    }
}

Результат выполнения:

ob1.a: 2
ob2.a: 12

Промежуточная переменная temp здесь не обязательна — тело метода можно записать короче: return new ReturnObjectExample(a + 10);. Объект, созданный внутри метода, не исчезает после выхода из него: он живёт в куче ровно до тех пор, пока на него есть хотя бы одна ссылка, после чего его освободит сборщик мусора.

Коротко о главном

  • Java передаёт аргументы только по значению: для примитива копируется значение, для объекта — ссылка.
  • Изменение состояния объекта внутри метода видно вызывающему коду.
  • Присваивание параметру нового объекта вызывающему коду не видно.
  • Массивы и коллекции ведут себя как изменяемые объекты; String, Integer и другие неизменяемые типы «изменить» через параметр нельзя.
  • Метод может создать объект и вернуть ссылку на него — объект при этом не копируется.

Часто задаваемые вопросы

Java передаёт объекты по ссылке или по значению?

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

Почему метод swap() не меняет местами две переменные?

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

Можно ли изменить строку, переданную в метод?

Нет. Класс String неизменяемый: конкатенация внутри метода создаёт новую строку и присваивает её локальному параметру. Используйте StringBuilder, если строку нужно изменять внутри метода, или возвращайте новую строку как результат.

Как вернуть из метода несколько значений?

В Java нет ключевых слов out и ref, поэтому используют объект-контейнер: собственный класс, record начиная с Java 16, массив или коллекцию. Также можно передать в метод изменяемый объект и заполнить его поля, но возврат результата обычно читается лучше.

Видео объяснение

Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.

Комментарии

Зарегистрируйтесь или войдите, чтобы иметь возможность оставить комментарий.