JavaRush /Курси /Java Syntax Zero /Змінні-посилання в Java

Змінні-посилання в Java

Java Syntax Zero
Рівень 8 , Лекція 3
Відкрита

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 біти).

Посилання при цьому можна тільки присвоювати один одному. Ви не можете міняти посилання або присвоювати їм довільні значення:

Код Опис
String hello = "Привіт";
String s = hello;
Так можна
String hello = "Привіт";
hello++;
А так — не можна
String hello = 0x1234;
І так — не можна

4. Порожнє посилання — null

А що зберігає змінна-посилання, якщо їй ще нічого не присвоїли?

А зберігає вона порожнє посилання — null. null — це спеціальне ключове слово в Java, яке означає відсутність посилання (порожнє посилання). Значення null можна присвоїти будь-якій посилальній змінній.

Усі змінні-посилання, якщо їм не присвоєно якесь посилання, мають значення null.

Приклади:

Код Опис
class Person
{
   public static String name;
   public static int age;
}


Змінна String name має значення за замовчуванням: null.
Змінна int age має значення за замовчуванням: 0.

Локальні змінні без значення вважаються неініціалізованими як для примітивних типів, так і для типів-посилань.

Якщо змінна зберігає посилання на якийсь об'єкт, а ви хочете стерти значення змінної, просто присвойте їй посилання null.

Код Опис
String s = null;
s = "Привіт";
s = null;
s зберігає посилання null.
s зберігає посилання на об'єкт-рядок
s зберігає посилання null

5. Передача посилань у методи

Якщо у якогось методу є параметри посилальні змінні, передача значень у них відбувається так само, як і при роботі зі звичайними змінними. Змінній-параметру просто присвоюється значення іншої змінної.

Приклад:

Код Опис
class Solution
{
   public static void fill(String[] array, String value)
   {
      for (int i = 0; i < array.length; i++)
        array[i] = value;
   }

   public static void main(String[] args)
   {
     String[] data = new String[10];
     fill(data, "Hello");
   }
}


Метод fill заповнює переданий масив array переданим значенням value.

При виклику методу fill змінній array присвоюється посилання на масив data. Змінній value присвоюється посилання на об'єкт-рядок «Hello».

От як виглядатиме ситуація в пам'яті перед викликом методу fill:

Передача посилань у методи

От як виглядатиме ситуація під час роботи методу fill:

Передача посилань у методи 2

Змінні data і array посилаються (зберігають посилання) на один і той самий масив-контейнер у пам'яті.

Змінна value зберігає посилання об'єкта рядки Hello.

Клітинки масиву теж зберігають просто посилання на об'єкт Hello.

Фактично ніякого дублювання об'єктів не відбувається — копіюються тільки посилання.



6. Порівняння з мовою C/C++

Іноді Java-програмістів запитують на співбесіді: як передаються дані в методи у Java? Іноді ще уточнюють: по посиланню чи за значенням?

Це питання йде з мови C++ — у мові Java він не має сенсу. У Java змінним-параметрам завжди просто присвоюються значення змінних-аргументів. Тому правильна відповідь буде — за значенням.

Але будьте готові до того, щоб пояснити свою позицію, адже вам можуть тут же заперечити, що «примітивні типи передаються за значенням, а посилальні – по посиланню».

Коріння цієї проблеми пов'язані з тим, що багато Java-програмістів були в минулому програмістами C++. А там питання «як передаються параметри в методи» грало дуже важливу роль.

У Java все однозначно: примітивні типи зберігають значення, посилальні теж зберігають значення — посилання. Все залежить від того, що вважати значенням змінної.

У мові C++ у змінній можна було зберігати як посилання на об'єкт, так і сам об'єкт. Те ж стосувалося примітивних типів: у змінній можна було зберігати значення або оголосити змінну посиланням на тип int. Тому, щоб не плутатися, C++ програмісти посилання на об'єкт завжди називають посиланням, а сам об'єкт — значенням.

У C++ легко могла скластися ситуація, коли одна змінна містить об'єкт, а інша — посилання на цей самий об'єкт. Тому питання, що зберігає в собі змінна — сам об'єкт або тільки посилання на нього — було дуже важливим. При передачі в метод об'єкта він копіювався (якщо передавався за значенням), і не копіювався (якщо передавався по посиланню).

У Java цієї подвійності немає, і правильна відповідь звучить так: параметри в методи Java передаються за значенням. Просто у випадку з посилальними змінними це значення — посилання.

Коментарі (9)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Василь Рівень 4
24 липня 2023
до порівняння з мовою С та С++ було все нормально
Kostiantyn Bogatyrchuk Рівень 14 Expert
22 січня 2023
", і є вказівні змінні (reference variables)'', - скоріше "змінні типу посилання"
Taras Woytowitch Рівень 16
4 січня 2023
Уявіть собі що ви даруєте автомобіль людині😂 Ага, уявляю_)
Neridnyi Taras Рівень 23
23 вересня 2022
Чому останнє завдання не можна розвязати ось так :


    public static void fillArray(Integer[] array, int value, int begin, int end) {
        // напишіть тут ваш код
        Arrays.fill(array, value, begin, end);
    }
Тільки циклом for?
Andriy Рівень 16
4 жовтня 2022
Тому що перепутані місцями параметри. Має бути:

Arrays.fill(array, begin, end, value);
Олег Рівень 111 Expert
12 квітня 2023
Можна, тільки якщо в методі Arrays.fill() правильно поставити параметри ) Думаю, що так і заплановано, для чого ж тоді імпортовано java.util.Arrays.
Anonymous #2997874 Рівень 10
29 серпня 2022
Розвязання 2ої задачі у підказках не зовсім коректне, оскільки якщо значення end буде більше ніж довжина масиву, то впаде java.lang.ArrayIndexOutOfBoundsException.
Анет Рівень 13
18 серпня 2021
Після С++, вказівники у джаві як бальзам на душу)
Roma Chernesh Рівень 16
19 грудня 2022
У плані?