Конструктор - це особливий спосіб, який призначається для первинної установки значень полів об'єкта. На перший погляд, конструктори об'єктів не сильно відрізняються від звичайних методів об'єкта. І справді всередині конструктора ми можемо робити все те ж, що й у звичайних методах об'єкта: виводити текст у консоль, звертатися до всіх полів та методів нового об'єкта, викидати винятки тощо. Як і звичайні методи, конструктори можуть мати аргументи. Як і перевантажених методів, конструкторів може бути кілька з різними сигнатурами. Так само як і дженерик-методи, конструктори можуть бути параметризовані змінними типами. Навіть якщо ми заглянемо в байт-код, що генерується компілятором, у місці де має бути виклик конструктора, ми виявимо звернення до деякого методу з ім'ям
<init>
виклик якого не відрізняється від виклику інших методів об'єкта. А знайшовши байт-код цього методу, ми виявимо, що він містить результат компіляції нашого конструктора. Здається, що відмінностей від звичайних методів небагато, але вони є, і досить суттєві. Спочатку давайте розберемося, а навіщо нам власне потрібні конструктори? Для зберігання та обробки будь-яких даних, чи то примітивні типи, масиви, чи об'єкти нам необхідний деякий обсяг пам'яті. Це можуть бути регістри процесора, місце на стеку, або шматочок простору, виділений в секції даних процесу, або динамічно розміщується частини пам'яті (купі). У багатьох мовах програмування, з метою прискорення, при запиті програмою нового шматочка пам'яті, пам'ять віддавалася програмі не відчищеною, і могла містити довільні дані, які були збережені в цьому осередку пам'яті раніше. Підготовка та запис у такий шматок пам'яті необхідних значень, щоб у результаті там виявилася якась осмислена структура даних, лягала цілком на плечі програміста. Цілком звичайно програмісти хотіли полегшити собі життя і писали підпрограми для ініціалізації (тобто установки початкових значень) для структур даних, що часто використовуються. Такі підпрограми застосовувалися майже завжди, тому творці мови Java, вирішабо зробити подібні підпрограми ініціалізації обов'язковими для виклику під час створення об'єктів, і назвали їх конструкторами . Коли Java створюється новий об'єкт відбувається таке: Спочатку менеджер пам'яті Java виділяє обсяг пам'яті необхідний розміщення об'єкта. У цьому враховуються як поля оголошені у класі створюваного об'єкта, але як і поля оголошені переважають у всіх предках цього. Додатково в цей обсяг включається простір для розміщення структур, які використовуються Java-машиною для внутрішніх потреб. Всі поля такої "заготівлі" автоматично встановлюються в дефолтні значення - null
для типів посилань, 0
для чисел і false
дляboolean
. Після цього автоматично викликається конструктор класу, завдання якого встановити початкові значення полів об'єкта. Якщо у звичайному методі перший оператор може бути будь-яким, то конструктор має набагато менше свободи. Першим оператором конструктора може бути або явний виклик іншого конструктора тієї самої класу, або явний чи неявний виклик конструктора батьківського класу. Явний виклик конструкторів того ж класу здійснюється за допомогою ключового слова, this
за яким слідує набір аргументів, укладений у дужки. Явний виклик конструктора батьківського класу здійснюється так само, але при цьому використовується ключове слово super
. У аргументах явного виклику конструктора тієї самої, чи батьківського класу не можна звертатися до полів і методів об'єкта, як і використовувати ключові слова this
і super
, оскільки явний виклик конструктора вводить статичний контекст. Для неявного виклику конструктора батьківського класу писати нічого не треба, але при цьому неявно викликається конструктор за замовчуванням, який повинен існувати і бути видимим для поточного класу. При цьому слід мати на увазі, що якщо ланцюжок виклику батьківських конструкторів перерветься до того, як конструктор класу, що Object
знаходиться на вершині ланцюжка, успішно завершить свою роботу, то об'єкт не буде фіналізованим, тобто метод finalize()
такого об'єкта ніколи викликаний не буде. Після завершення роботи конструктора батьківського класу управління неявно передається на блоки ініціалізаторів екземпляра та ініціалізатори полів екземпляра поточного класу. Ініціалізатори виконуються в порядку, в якому вони зустрічаються в тексті програми. Лише після завершення роботи ініціалізаторів управління передається частині конструктора, що залишилася. Інші особливості конструкторів стосуються моделі пам'яті Java. Якщо клас, чи з його предків, перевизначає метод finalize()
, завершення роботи конструктора відбудеться до ( happens-before ) запуску методу finalize()
. Якщо який-небудь потік побачив посилання на об'єкт після завершення роботи конструктора, то гарантується, що цей потік побачить коректно ініціалізовані final
поля об'єкта, ініціалізація яких відбулася до завершення роботи конструктора.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ