Без розуміння синтаксису Java неможливо стати серйозним розробником, тому ми продовжуємо вивчати синтаксис. В одній із минулих статей ми говорабо про примітивні змінні, але так як видів змінних два, сьогодні ми поговоримо про другий вид — типи посилань у Java. То що це? Навіщо потрібні типи даних у Java? Давайте уявимо, що у нас є об'єкт телевізор з деякими характеристиками, такими як номер каналу, гучність звуку та прапор увімкненості:
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;
це означатиме, що ми першою змінною як значення привласнабо копію адресаи (значення бітів адресаи) на другий об'єкт, і тепер обидві змінні посилаються на другий об'єкт (інакше кажучи, два пульти від одного й того самого телевізора). У той же час перший об'єкт залишився без змінної, яка на нього посилається. У результаті у нас є об'єкт, до якого неможливо звернутися, адже змінна була такою умовною ниточкою до нього, без якої він перетворюється на сміття, просто лежить у пам'яті та посідає місце. Згодом цей об'єкт буде знищений із пам'яті збирачем сміття . Перервати сполучну ниточку з об'єктом можна і без іншого посилання:
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();
Можливо, ви подумаєте, що це зробить об'єкт незмінним. Але ж ні, це не так. Посилальна змінна з модифікатором 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";
Як наслідок ми отримаємо константу, аналог констант примітивного типу, адже тут ми не можемо ні перевизначити посилання, ні змінити внутрішній стан об'єкта (внутрішні дані).
Підведемо підсумки
- Якщо прості змінні зберігають біти значень, посилальні змінні зберігають біти, що представляють спосіб отримання об'єкта.
- Посилання на об'єкти оголошуються лише одного виду об'єктів.
- Будь-який клас в Java - це тип посилання.
- За замовчуванням Java значення будь-якого змінного посилання —
null
. String
- Стандартний приклад посилального типу. Також цей клас є незмінним (immutable).- Посилальні змінні з модифікатором
final
прив'язані лише одного об'єкту без можливості перевизначення.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ