Передача объектов в методы
Объекты допускается передавать методам в качестве параметров точно так же, как и примитивные значения. При этом объекты передаются по ссылке на них. Метод может изменять полученные значения. Самый важный вопрос здесь — а будут ли изменяться эти значения в вызвавшем методе? Если в метод передан объект — то будут, если же это примитивное значение — то нет. Почему так? Давайте рассмотрим на следующем примере.
Пример 1. Изменение переданных значений в методе
При запуске программы в стеке выделяется область памяти для метода main(). Там хранятся локальные переменные x и y. При вызове метода changePrimitives() в стеке выделяется еще одна область памяти, в которой создаются локальные переменные, а и b. Их значения хранятся там же. Если, а и b изменяются, это никак не влияет на переменные x и y — они хранятся в разных областях памяти.
Локальная объектная переменная box создается в стековой области памяти метода main(), но сам объект создается в куче. При вызове метода changeObject() передается не сам объект, а ссылка на него. Поэтому при изменении переменной о, в методе changeObject(), меняется объект находящийся куче. И эти изменения, естественно, видны в вызвавшем методе 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 < /** * Изменение объекта * * @param box */ static void changeObject(Box box) < box.width *= 2; box.height /= 2; box.depth += 2; >/** * Изменение примитивных типов * * @param a * @param b */ 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
Объекты можно создавать в методе и возвращать их. Например, как показано в следующем примере:
Пример 2. Возврат объекта из метода
public class ReturnObject < int a; ReturnObject(int i) < a = i; >ReturnObject incrementByTen() < ReturnObject temp = new ReturnObject(a + 10); return temp; >public static void main(String[] args) < ReturnObject ob1 = new ReturnObject(2); ReturnObject ob2 = ob1.incrementByTen(); System.out.println("ob1.a: " + ob1.a); System.out.println("ob2.a: " + ob2.a); >>
- Процедурное и объектно-ориентированное программирование
- Принципы ООП
- Классы и объекты
- Конструктор
- Ключевое слово this
- Перегрузка
- Стек и куча
- Java varargs
- Рекурсия
- Сборщик мусора и метод finalize
- Наследование
- Ключевое слово super
- Модификаторы доступа
- Геттеры и сеттеры
- Переопределение методов
- Абстрактные классы и методы
- Ключевое слово final
- Задания
Параметры в Java передаются по ссылке или по значению?
Этот вопрос уходит корнями в C++ прошлое (скорее всего интервьюера), это терминология из C++. Для начала нужно разобраться в этой терминологии.
В C++ ссылка – это переменная-псевдоним для другой переменной. Меняя значение ссылки, поменяется и оригинал. В Java такого нет, легко понять это подумав например о ссылке на int.
Ссылка в Java – это нечто похожее на адрес объекта в памяти. Похожая сущность в C++ называется указатель.
Передача параметра по значению – это копирование значения в переменную-параметр метода. По ссылке – передача ссылки, то есть по сути использование той же самой переменной. Разобраться подробнее (осторожно, много C++).
В Java параметр метода – всегда копия. Значит параметры передаются всегда по значению, просто это значение может быть ссылкой на объект. Код ниже это демонстрирует.
Передача параметров в Java: по значению или по ссылке?
Для начала простой пример. Пусть у нас есть метод m(), который меняет имя кошки на Vasya:
void m(Cat cat)
Создадим кошку Petya, запомним ее в переменную oldCat и прогоним через метод:
Cat cat = new Cat("Petya"); Cat oldCat=cat; this.pass.m(cat); assertEquals("Vasya", cat.getName()); assertEquals(oldCat, cat);
Убедились, что теперь кошка стала Васей, при этом значение ссылки cat осталось прежним (впрочем, мы и не пытались его поменять внутри метода). Но имя то поменялось, то есть действия в методе мы проделали со внешней кошкой (созданной снаружи метода), а не с копией объекта кошки внутри метода. И это правда.
Но это не значит, что передача параметра произошла по ссылке. Она произошла по значению. И этим значением является ссылка.
То есть, oldCat и cat как до вызова метода располагалась по адресу 10 (к примеру), так и после вызова будут располагаться по адресу 10. Даже если бы внутри метода этот адрес попробовали поменять. А давайте «попробуем».
На следующем примере поясню, что такое настоящая передача по ссылке, и как это происходило бы к примеру в в языке C++ (в котором передача параметров и вправду может происходить по ссылке).
Пример № 2
Теперь у нас метод m1(), в котором мы переменной cat присвоим новую кошку Fifa:
void m1(Cat cat) < cat.setName("Vasya"); //(1) cat=new Cat("Fifa"); //(2) здесь в С++ можно поменять адрес внешней cat, а у нас меняется адрес внутренней cat cat.setName("Kitty"); //(3) System.out.println(cat.getName()); //Kitty >
Повторим вышеприведенный тест, и результат будет таким же. Внешняя кошка снова Vasya, и ссылка осталась прежней:
Cat cat = new Cat("Petya"); Cat oldCat=cat; this.pass.m1(cat); assertEquals("Vasya", cat.getName()); assertEquals(oldCat, cat);
Рассмотрим по шагам, что происходит.
- На строке (1) мы поменяли имя внешней кошки (поскольку cat здесь — все еще ссылка на внешнюю кошку) на имя Vasya, как и в первом примере.
- Но на строке (2) cat становится уже ссылкой на новую кошку, и ее адрес уже не 10, а какой-нибудь 42. Но этот адрес относится к внутренней ссылке cat, а никак не ко внешней. Внешняя ссылка cat не поменялась.
- То есть на строке (2) и далее все, что мы делаем с кошкой уже касается новой кошки Fifi, расположенной по новому адресу. Внешнюю кошку это уже не касается, имя меняется не у нее. Она остается по старому адресу, который хранится во внешних переменных cat и oldCat.
Вот в C++ мы могли бы передать ссылку — так, что на строке (2) не внутренняя cat встала на новый адрес, а адрес внешней cat встал бы на новую кошку. Вот в чем разница.
У нас же после прогона метода m1() ссылка cat по прежнему равна oldCat, значение ее не поменялось. В Java такого синтаксиса, как в С++ для передачи по ссылке нет.
Автор sysout Опубликовано 25.12.2019 01.05.2021 Рубрики Core Java
Передача параметров в Java: по значению или по ссылке?: 2 комментария
Алексей :
Уже который раз натыкаюсь на это утверждение. «в Java все передается по значению!»
Почему такая путаница? Зачем людям вообще сранивать с С++?
Ссылка в с++ может хранить адрес объекта в куче. Можно передать объект по ссылке в качестве параметра в метод и это будет аналогично тому что происходит в Java — две ссылки на один и тот же объект. Просто разница лишь в том как вы это называете, а работа будет аналогичная!
В Java просто нету возможности создать объект на стэке или разыменовать указатель чтобы получить копию, зачем это придумали вообще?) Людей путать)
чуть проще. схематично
Obj obj1 = new Obj(«Вася»); m(obj1); // передается значение ссылки, т.е. адрес на объект assertEquals(«Вася», obj1.name); // оригинал ссылки obj1, объект не изменился void m(Obj obj2) < // копия ссылки obj1
obj2 = new Obj(«Петя»); // переписали копию ссылки, но не объект по ней
>
Добавить комментарий Отменить ответ
Прошу прощения: на комментарии временно не отвечаю.
Передача параметров в метод Java
Возможно я что — то не так понимаю. Но по моей логике переменная переданная в метод и дальнейшие модификации с ней должны остаться в это методе, и исчезнуть вместе с методом. Но в итоге я вижу в main измененный массив mas. Почему так происходит. Потому что в таком случае пропадает надобность в return. В Java давно, а тут такое. Видать что — то серьезное я забыл.
testA 100 testA 100 testA 100 testA 4 testA 5 test 100 test 100 test 100 test 4 test 5 Main 100 Main 100 Main 100 Main 4 Main 5
Отслеживать
51.6k 200 200 золотых знаков 61 61 серебряный знак 242 242 бронзовых знака
задан 12 янв 2018 в 13:07
user239760 user239760
В Java параметры в методы передаются по значениям и не меняются, но вот содержимое меняться может. Таким образом ссылка на сам массив (или даже на объект) измениться не может, а элементы массива (или значения полей объекта) — пожалуйста 🙂
12 янв 2018 в 13:09
Посмотрите главу «Подробное рассмотрение особенностей передачи аргументов» в полном руководстве по Java авторства Герберта Шилдта.
12 янв 2018 в 13:41
@post_zeew, У вас в доме кран протекает. Что делать? Идти в библиотеку, брать книгу и сидеть читать, как стать слесарем или позвонить сантехникам и ждать минут 30. Пока всех соседей не затопит. Было бы правильнее вам в начале помочь решить проблему, а после уже давать советы по книгам. Но в общем спасибо за книгу. А то ведь так любой может зайти в SO и писать в комментах. Чувак иди в Google там все есть
– user239760
12 янв 2018 в 13:58
Я ссылаюсь на конкретную главу книги, там даже не глава, а несколько абзацев. Книга в свободном доступе в электронном виде. В данном случае я указал именно на главу книги, так как в интернете на эту тему очень много принципиально неверной информации. В книге же все подробно расписано. И к слову: проблема большинства начинающих программистов заключается в том, что они не читают книги, в которых описываются фундаментальные вещи и не зная самых азов начинают писать что-то относительно сложное.
12 янв 2018 в 14:14
@post_zeew, или же они просто забывают что — то.
– user239760
12 янв 2018 в 15:30
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
@AlexChermenin замудрено объяснили. Объекты передаются по ссылке, примитивы по значению. Массивы примитивов — это объекты, поэтому передаются также по ссылке.
Обновлено: под передачей по ссылке я имею ввиду, что объект таки передается по значению. Но в значении содержится ссылка на объект, а не копия объекта.
int number = 0; void update(int num) < num++; System.out.println(num); // 1 >update(number); System.out.println(number); // 0
int[] numbers = new int[]; void update(int[] nums) < for(int i=0; i < nums.length; i++) < nums[i] = 5-i; >> update(numbers); /** * Ага, а тут кстати вызывается toString(), чего нет у примитивов */ System.out.println(numbers); //
Отслеживать
ответ дан 12 янв 2018 в 13:23
18.1k 24 24 серебряных знака 49 49 бронзовых знаков
Всё передаётся по значению, только для объектов ссылка и есть значение 🙂
12 янв 2018 в 13:25
У массивов тоже есть указатели, так как они выделают место в памяти. Так получается? А не имеет значение что это метод static или обычный ?
– user239760
12 янв 2018 в 13:34
@AlexChermenin под ссылкой вы имете ввиду hashCode?
– user239760
12 янв 2018 в 13:36
Массив — это объект. Изменяя элемент объекта, вы изменяете его по ссылке. Если же вы сделаете так: for(int num : nums) < num = 5; >то ничего не прокатит, потому что вы получите примитивный элемент массива, который получили уже как значение.
12 янв 2018 в 13:37
Не путайте, пожалуйста, людей. В Java все параметры передается по значению.
12 янв 2018 в 13:39
Примитивные типы передаются по значению. Объекты передаются в метод по ссылке. Поэтому все изменения ссылочного объекта влияют на переданный объект. Если переменная примитивный тип, то передается только значение переменной, то есть копия этого значения. И изменения копии никак не влияет на начальное значение.
Представьте себе ссылку, как «пульт управления переменной». Вы передаете в метод только «пульт управления» при передаче по ссылке. При выходе из метода уничтожается только «пульт управления», то есть ссылка. При передаче примитивного типа, происходит передача значения. То есть копируется значение переменной. А при выходе из метода исчезает эта копия. Исчезает значение переменной — копии.
Отслеживать
ответ дан 13 янв 2018 в 6:35
Vyacheslav Mishchenko Vyacheslav Mishchenko
537 1 1 золотой знак 5 5 серебряных знаков 13 13 бронзовых знаков
А как тогда создать клон объекта, чтобы у него был свой указатель, но значения все передались другому объекту. Короче у объекта есть 10 переменных. Чтобы через равно не передавать их все. Можно как — то одной строкой передать их все другому новому объекту? И как сделать это
– user239760
13 янв 2018 в 6:57
1. Переопределение метода clone() и реализация интерфейса Cloneable(); 2. Использование конструктора копирования; 3. Использовать для клонирования механизм сериализации Ссылка >>> habrahabr.ru/post/246993
