1. Переменные-ссылки
В языке Java переменные могут быть двух видов: переменные примитивного типа и все остальные. Вот про «все остальные» мы сейчас и поговорим.
На самом деле правильнее будет сказать, что есть переменные примитивного типа (primitive type variables) и ссылочные переменные (reference variables). Так что же это такое, эти ссылочные переменные?
В отличие от примитивных типов, которые позволяют хранить значения прямо внутри переменных, переменные ссылочных типов хранят ссылки на объекты. Т.е. где-то в памяти существует некий объект, а в переменной-ссылке просто хранится адрес этого объекта в памяти (ссылка).
Значения прямо внутри переменных хранят только примитивные типы, все остальные типы хранят только ссылку на объект. Ранее вы уже, кстати, сталкивались с двумя такими типами переменных — это переменные типа String
и переменные типа массив.
И массив, и строка являются объектами, которые хранятся где-то в памяти. Переменные типа String
и переменные типа массив хранят только ссылки на объекты.
Переменные int a, int b и double d
— примитивные и хранят значение внутри себя.
Переменная String str
— ссылочная и хранит адрес (ссылку) объекта типа String
в памяти.
При присваивании примитивного значения переменной примитивного типа, его значение копируется (дублируется). При присваивании же ссылочной переменной копируется только адрес объекта, сам же объект при этом не копируется.
2. Суть ссылок
В чем принципиальное отличие переменных-ссылок от примитивных переменных?
Примитивная переменная — как коробка: в ней можно хранить какое-нибудь значение. Переменная-ссылка больше похожа на листок бумаги с телефонным номером на нем.
Машина vs ключи от машины
Представьте, что вы решили подарить другу на день рождения машину. Вы же не будете заворачивать ее в коробку и нести с собой: машина для этого слишком большая.
Гораздо удобнее взять с собой только ключи от машины и коробку, достаточно вместительную для них. А ваш друг и так все поймет, когда достанет из коробки ключи. Нет нужды нести с собой всю машину, когда можно просто передать ключи.
Человек vs его телефонный номер
Или еще один вариант: человек и его телефонный номер. Телефонный номер — не сам человек, но номер телефона можно использовать, чтобы звонить ему, спрашивать у него какую-то информацию, давать команды.
Ссылка тоже используется для взаимодействия с объектом. Все объекты взаимодействуют друг с другом при помощи ссылок. Вместо того, чтобы «обмениваться людьми», достаточно обменяться телефонными номерами.
При присваивании значения в примитивную переменную, его значение копируется (дублируется). При присваивании же значения ссылочной переменной копируется только адрес объекта («телефонный номер»), сам же объект при этом не копируется.
Ссылка даёт ещё одно преимущество: можно передать ссылку на объект в какой-нибудь метод, и этот метод будет в состоянии модифицировать (изменять) наш объект, используя ссылку на него, вызывая его методы и обращаясь к данным внутри объекта.
3. Присваивание ссылок
При присваивании ссылочных переменных происходит просто присваивание адреса объекта в памяти. Сами объекты при этом не появляются и не исчезают.
Такой подход позволяет избежать копирования больших объемов памяти. Если куда-то нужно передать очень большой объект, мы просто передадим в этот метод ссылку на объект и все. Ссылка занимает гораздо меньше места.
Размер всех переменных-ссылок (независимо от типа) одинаков и составляет 4 байта (как тип int). Но! Если ваше приложение запущено на 64-х разрядной Java-машине, размер всех ссылок будет 8 байт (64 бита).
Ссылки при этом можно только присваивать друг другу. Вы не можете менять ссылки или присваивать им произвольные значения:
Код | Описание |
---|---|
|
Так можно |
|
А так — нельзя |
|
И так — нельзя |
4. Пустая ссылка — null
А что хранит переменная-ссылка, если ей еще ничего не присвоили?
А хранит она пустую ссылку — null. null
— это специальное ключевое слово в Java, обозначающее отсутствие ссылки (пустую ссылку). Значение null
можно присвоить любой ссылочной переменной.
Все переменные-ссылки, если им не присвоена какая-нибудь ссылка, имеют значение null
.
Примеры:
Код | Описание |
---|---|
|
Переменная String name имеет значение по умолчанию: null .Переменная int age имеет значение по умолчанию: 0 .
|
Локальные переменные без значения считаются неинициализированными как для примитивных типов, так и для типов-ссылок.
Если переменная хранит ссылку на какой-то объект, а вы хотите стереть значение переменной, просто присвойте ей ссылку null.
Код | Описание |
---|---|
|
s хранит ссылку null .s хранит ссылку на объект-строкуs хранит ссылку null |
5. Передача ссылок в методы
Если у какого-нибудь метода есть параметры ссылочные переменные, передача значений в них происходит точно так же, как и при работе с обычными переменными. Переменной-параметру просто присваивается значение другой переменной.
Пример:
Код | Описание |
---|---|
|
Метод fill заполняет переданный массив array переданным значением value . |
При вызове метода fill
переменной array
присваивается ссылка на массив data
. Переменной value
присваивается ссылка на объект-строку «Hello».
Вот как будет выглядеть ситуация в памяти перед вызовом метода fill
:
Вот как будет выглядеть ситуация во время работы метода fill
:
Переменные data
и array
ссылаются (хранят ссылки) на один и тот же массив-контейнер в памяти.
Переменная value
хранит ссылку объекта строки Hello
.
Ячейки массива тоже хранят просто ссылки на объект Hello
.
Фактически никакого дублирования объектов не происходит — копируются только ссылки.
6. Сравнение с языком С/С++
Иногда Java-программистов спрашивают на собеседовании: как передаются данные в методы в Java? Иногда еще уточняют: по ссылке или по значению?
Этот вопрос идет из языка С++ — в языке Java он не имеет смысла. В Java переменным-параметрам всегда просто присваиваются значения переменных-аргументов. Так что правильным ответом будет — по значению.
Но будьте готовы к тому, чтобы объяснить свою позицию, т.к. вам могут тут же возразить, что «примитивные типы передаются по значению, а ссылочные – по ссылке».
Истоки этой проблемы связаны с тем, что многие Java-программисты были в прошлом С++ программистами. А там вопрос «как передаются параметры в методы» играл очень важную роль.
В Java все однозначно: примитивные типы хранят значения, ссылочные тоже хранят значение — ссылку. Все дело в том, что считать значением переменной.
В языке C++ в переменной можно было хранить как ссылку на объект, так и сам объект. То же касалось примитивных типов: в переменной можно было хранить значение или объявить переменную ссылкой на тип int
. Поэтому чтобы не путаться, С++ программисты ссылку на объект всегда называют ссылкой, а сам объект — значением.
В С++ легко могла сложиться ситуация, что одна переменная содержит объект, а другая — ссылку на этот же объект. Поэтому вопрос, что хранит в себе переменная — сам объект или только ссылку на него — был очень важен. При передаче в метод объекта он копировался (если передавался по значению), и не копировался (если передавался по ссылке).
В Java этой двойственности нет, и правильный ответ звучит так: параметры в методы Java передаются по значению. Просто в случае с ссылочными переменными это значение — ссылка.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ