JavaRush /Java блог /Java Developer /Ссылочные типы данных в Java
Автор
Александр Выпирайленко
Java-разработчик в Toshiba Global Commerce Solutions

Ссылочные типы данных в Java

Статья из группы Java Developer
Без понимания синтаксиса Java невозможно стать серьезным разработчиком, поэтому сегодня мы продолжаем изучать синтаксис. В одной из прошлых статей мы говорили о примитивных переменных, но так как видов переменных два, сегодня мы поговорим о втором виде — ссылочные типы в Java. Итак что это? Зачем нужны ссылочные типы данных в Java? Ссылочные типы данных в Java - 1Давайте представим, что у нас есть объект телевизор с некоторыми характеристиками, такими как номер канала, громкость звука и флаг включенности:

public class TV {
   int numberOfChannel;
   int soundVolume;
   boolean isOn;
}
Как простой тип, например, int, может хранить эти данные? Напомним: одна переменная int — это 4 байта. А ведь там внутри есть две переменные (4 байта + 4 байта) этого же типа, да ещё и boolean (+1 байт)... Итого — 4 к 9, а ведь как правило, в объекте хранится намного больше информации. Что делать? Нельзя же вложить объект в переменную. На этом моменте в нашей истории появляются ссылочные переменные. Ссылочные переменные хранят адрес ячейки памяти, в которой расположен определенный объект. То есть это “визитка” с адресом, имея которую мы можем найти наш объект в общей памяти и выполнять с ним некоторые манипуляции. Ссылка на любой объект в Java представляет собой ссылочную переменную. Как бы это выглядело с нашим объектом телевизора:

TV telly = new TV();
Переменной типа TV с именем telly мы задаем ссылку на создаваемый объект типа TV. То есть, JVM выделяет память в куче под объект TV, создает его и адрес на его местоположение в памяти, кладется в переменную telly, которая хранится в стеке. Подробнее о памяти, а именно — о стеке и еще массе полезного, можно почитать в этой лекции. Переменная типа TV и объект типа TV, заметили? Это неспроста: объектам определенного типа должны соответствовать переменные того же типа (не считая наследования и реализаций интерфейсов, но сейчас мы это не учитываем). В конце концов, не будем же мы в стаканы наливать суп? Получается, что у нас объект — это телевизор, а ссылочная переменная для него — как бы пульт управления. С помощью этого пульта мы можем взаимодействовать с нашим объектом и его данными. Например, задать характеристики для нашего телевизора:

telly.isOn = true;
telly.numberOfChannel = 53;
telly.soundVolume = 20;
Тут мы использовали оператор точки . — чтобы получить доступ и начать использование внутренних элементов объекта, на который ссылается переменная. Например, в первой строке мы сказали переменной telly: “Дай нам внутреннюю переменную isOn объекта, на который ты ссылаешься, и задай ей значение true” (включи нам телевизор).

Переопределение ссылочных переменных

Допустим, у нас есть две переменные ссылочного типа и объекты, на которые они ссылаются:

TV firstTV = new TV();
TV secondTV = new TV();
Если мы напишем:

firstTV = secondTV;
это будет означать, что мы первой переменной в качестве значения присвоили копию адреса (значение битов адреса) на второй объект, и теперь обе переменные ссылаются на второй объект (иначе говоря, два пульта от одного и того же телевизора). В тоже время, первый объект остался без переменной, которая на него ссылается. В итоге у нас есть объект, к которому невозможно обратиться, ведь переменная была такой условной ниточкой к нему, без которой он превращается в мусор, просто лежит в памяти и занимает место. Впоследствии этот объект будет уничтожен из памяти сборщиком мусора. Ссылочные типы данных в Java - 2Прервать связующую ниточку с объектом можно и без другой ссылки:

secondTV = null;
В итоге ссылка на объект останется одна — firstTV, а secondTV уже ни на кого указывать не будет (что не мешает нам в дальнейшем присвоить ей ссылку на какой-нибудь объект типа TV).

Класс String

Отдельно хотелось бы упомянуть класс String. Это базовый класс, предназначен для хранения и работы с данными, которые хранятся в виде строки. Пример:

String text = new String("This TV is very loud");
Здесь мы передали строку для хранения в конструкторе объекта. Но никто так не делает. Ведь строки можно создавать:

String text = "This TV is very loud";
Гораздо удобнее, правда? По популярности использования String не уступает примитивным типам, но всё же это класс, и переменная, которая ссылается на него — не примитивного, а ссылочного типа. У String есть вот такая замечательная возможность конкатенации строк:

String text = "This TV" + " is very loud";
В итоге мы снова получим текст: This TV is very loud, так как две строки соединятся в одно целое, и переменная будет ссылаться на этот полный текст. Важным нюансом является то, что String — это неизменяемый класс. Что это значит? Возьмем такой пример:

String text = "This TV";
text = text + " is very loud";
Вроде, бы всё просто: объявляем переменную, задаем ей значение. На следующей строке изменяем его. Но не совсем-то и изменяем. Так как это неизменяемый класс, на второй строке начальное значение не меняется, а создается новое, которое в свою очередь состоит из первого + " is very loud".

Ссылочные константы

В статье про примитивные типы мы затрагивали тему констант. Как же будет себя вести ссылочная переменная, когда мы объявим её final?

final TV telly = new TV();
Возможно, вы подумаете, что это сделает объект неизменяемым. Но нет, это не так. Ссылочные типы данных в Java - 3Ссылочная переменная с модификатором final будет привязана к определенному объекту без возможности её как-либо отвязать (переопределить или приравнять к null). То есть, после задания значения такой переменной, код вида:

telly = new TV();
или

telly = null;
будет вызывать ошибку компиляции. То есть final действует только на ссылку, а на сам объект влияния не оказывает. Если изначально он у нас изменяемый, мы без проблем можем менять его внутреннее состояние:

telly.soundVolume = 30;
Иногда, переменные обозначают как final даже в аргументах метода!

public void enableTV (final TV telly){
   telly.isOn = true;
}
Это делается для того, чтобы в процессе написания метода эти аргументы нельзя было переопределить и соответственно создать меньше путаницы. А что если обозначить final ссылочную переменную, которая ссылается на неизменяемый объект? К примеру String:

final String PASSWORD = "password";
Как следствие, мы получим константу, аналог констант примитивного типа, ведь тут мы не можем ни переопределить ссылку, ни изменить внутреннее состояние объекта (внутренние данные).

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

  1. Если простые переменные хранят биты значений, то ссылочные переменные хранят биты, представляющие способ получения объекта.
  2. Ссылки на объекты объявляются лишь для одного вида объектов.
  3. Любой класс в Java — это ссылочный тип.
  4. По умолчанию в Java значение любой переменной ссылки — null.
  5. String — стандартный пример ссылочного типа. Также этот класс является неизменяемым (immutable).
  6. Ссылочные переменные с модификатором final привязаны лишь к одному объекту без возможности переопределения.
Комментарии (16)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
MrFurion Уровень 32
3 апреля 2023
,Что за бред. Сначало вы говорите строка это ссылочный тип, потом говорите примитивный так как она состоит из символов, потом говорите это ссылочный потому что это класс. Так какой он тип может кто-то объяснить нормально?
YesOn Уровень 13
1 августа 2021
Не понял про неизменяемость класса String: String text = "This TV"; text = text + " is very loud"; Вторая строка написана ошибочно и компилятор её не пропустит? Потому что переменная text так и останется со значением = "This TV"; ? Или что имелось ввиду?
gunsroses Уровень 22
14 сентября 2020
TV telly = new TV(); Переменной типа TV с именем telly мы задаем ссылку на создаваемый объект типа TV. скажите пожалуйста, тип переменной и объекта TV должны быть одинаковыми это понятно, а где сам объект ??? )
Иван Серов Уровень 7
14 апреля 2020
Спасибо за статью, гораздо понятней чем лекция в курсе, но все же кой что до конца так и не понял а именно назначение вот этой строки: final TV telly = new TV(); Для чего она , и если написать вот так : public class TV { int numberOfChannel; int soundVolume; boolean isOn; public void enableTV (){ int x= TV.numberOfChannel; } }
Demitelix Уровень 11
11 апреля 2020
Спасибо
sergey Уровень 22
10 апреля 2020
статья по мотивам Head First Java?