Порядок виконання конструкторів

У процесі створення об'єкта розширеного класу віртуальна машина виділяє пам'ять для зберігання всіх його полів, включаючи і ті, які успадковані від базового класу, і останні отримують вихідні значення за замовчуванням, що відповідають їх типам (О (нуль) - для всіх числових типів, false - для boolean, '\ uOOOO' - для char і null - для посилань на об'єкти). Далі процес можна розділити на три наступні стадії:

1) виклик конструктора базового класу;

2) присвоєння вихідних значень полів об'єкту за допомогою виконання відповідних виразів і блоків ініціалізації;

3) виконання інструкцій, передбачених в тілі конструктора.

В першу чергу виконується явне або непряме звернення до конструктору базового класу. Якщо здійснюється явний виклик конструктора того ж похідного класу за допомогою посилання this, подібний ланцюжок викликів Конструкторів цього класу виконується до тих пір, поки не буде знайдена Інструкція явного або непрямого звернення до конструктору базового класу, після чого такий і викликається. Конструктор базового класу Виконується відповідно до тієї ж послідовністю стадій - процес триває рекурсивно, завершуючи після досягнення конструктора класу Object, оскільки надалі не Існує конструкторів класів вищого рівня ієрархії. У виразах, які виконуються в процесі виклику Конструктора базового класу, що не Можуть бути присутніми посилань на будь-які члени поточного об'єкта.

Нарешті, виконуються вирази тіла Конструктора. Якщо поточний Конструктор був викликаний Явно, по його завершенні управління передається в тіло конструктора-ініціатора, де виконуються залишилися Інструкції. Процес повторюється до тих пір, поки управління не буде передано назад у тіло конструктора "вихідного" похідного класу, назва якого було зазначено в вираженні new.

Якщо під час процесу Конструювання викидається виключення, віртуальна машина завершує виконання вираження new, генеруючи той же виняток, і не повертає очікувану посилання на об'єкт. Оскільки вираз явного виклику конструктора поточного або базового класу має бути першим в тілі конструктора-ініціатора, відловити виняток, викинуте викликаним Конструктором, неможливо. (Якби мова допускав подібне, існувала б і ймовірність створення об'єктів з невірним початковим станом.)

Нижче наведено приклад, який ілюструє різні стадії процесу конструювання об'єкта похідного класу.

protected int xMask = 0x00ff;

protected int fullMask;

Якщо в ході конструювання об'єкта викликаються методи, важливо уявляти собі порядок операцій і розуміти, що відбувається на кожному етапі процесу. У подібній ситуації при виклику методу ми завжди маємо справу з версією цього методу для фактичного типу об'єкта; наявність в полях об'єкта передбачених вихідних даних в цей момент не гарантовано. Так, наприклад, якщо на кроці 5 конструктор Х викликав би метод mask, використовувалося б поточне значення поля fullMask, рівне 0x00ff, але ніяк не 0xffff. І це абсолютно справедливо, хоча той же метод mask, будучи викликаним пізніше, після завершення процес а конструювання, отримав би значення fullMask, рівне 0xffff.

Давайте, крім того, уявімо, що в класі У метод mask піддався перевизначення - тепер він, скажімо, реалізований таким чином, що в ньому для обчислень безпосередньо використовується значення поля yMask. Якщо конструктор Х викликає метод mask, він може насправді звернутися і до версії mask, оголошеної в У, і в цей момент, зрозуміло, поле yMask буде містити значення 0 замість очікуваного 0xff00.

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

Вправа 3.2. Наберіть наведений вище текст класів Х і У і додайте в нього вираження, які допоможуть про стежити стадії зміни значень полів. Оголосіть метод main і виконайте його, щоб подивитися на отримані результати /

Вправа 3.3. Якби в процесі конструювання об'єкта виявилося складно задавати значення числових масок, використовуючи поля розширеного класу, як ви подолали б подібні проблеми?

Популярне

Хмара тегів

інформація

Схожі статті