Справа в тому що:

1. Якщо Ви створюєте клас і в ньому визначаєте конструктор з аргументами (клас AClass, у якого тільки один конструктор, який приймає int i), то компілятор вже не створить конструктор за замовчуванням. Тому що це порушило б контракт класу AClass, який не може бути инициализирован без аргументів. Якщо Ви хочете ще мати і конструктор за замовчуванням, задавайте тепер його явно.

Інакше не можна було б заборонити створення конструктора за замовчуванням, що було б погано.

2. При створенні конструкторів класу BClass, який успадковується від іншого класу, компілятор вимагає, щоб першим рядком конструктора був виклик іншого конструктора (успадкованого чи в цьому класі).

Чому? Тому що раз Ви наслідуєте від якогось класу, Ви хочете повторно використовувати його логіку. Конструктор призводить екземпляр класу в якесь початкове цілісне стан. У Вашому випадку для ініціалізації AClass вимагає аргумент, без якого JVM не знає, як форматувати екземпляр класу.

Якщо у класу не визначені конструктори, то він намагається створити конструктор за замовчуванням, тобто без аргументів:

Public class AClass1 ()

Оскільки тут явно конструктори не визначені І клас не успадковується від інших класів, компілятор створює конструктор за замовчуванням.

Це еквівалентно такому визначенню:

Public class AClass1 (public AClass1 () ())

Тепер подивимося на BClass1:

Public class BClass1 extends AClass1 ()

Тут теж явно конструктори не визначені, і компілятор намагається створити конструктор за замовчуванням. Оскільки в класі AClass1 є конструктор за замовчуванням, він створить конструктор за замовчуванням, який буде викликати конструктор AClass1. Цей код еквівалентний такому:

Public class BClass1 extends AClass1 (public BClass1 () (super ();))

У Вашому випадку створюється клас БЕЗ конструктора за замовчуванням:

Public AClass (public AClass (int i) ())

Оскільки тут описаний (хоча б один) конструктор, конструктор за замовчуванням вже не створюється. Т. е. Такий код вже не компілюватиметься:

AClass a = new AClass (); // не працює

потрібно щось на кшталт

AClass a = new AClass (1);

Відповідно, будь-який конструктор BClass вимагатиме виклику будь-якого конструктора AClass або BClass. При такому описі компілятор буде лаятися:

Public BClass extends AClass ()

Тому що буде спроба виклику конструкора за замовчуванням класу AClass, який не визначений:

Public BClass extends AClass (public BClass () (super (); // помилка; в класі AClass немає такого конструктора))

Проте, можна створити клас BClass з конструктором за замовчуванням, задавши якесь значення для конструктора AClass:

Public class BClass extends AClass (public BClass () (super (1);))

Це компілюватиметься.

Як відомо, об'єкт є екземпляром певного класу. Для його створення використовується ключове слово new, наприклад:
Person student = new Person ( "Mike")
Цим кодом створюється новий об'єкт Personі вказується його ім'я - Mike. рядок « Mike»Передається аргументом відповідному конструктору Person:

Person (String name) (this.name = name;)

Цей конструктор дозволяє вказати ім'я людини при створенні об'єкта.

Конструктор завжди викликається, коли створюється новий екземпляр класу. Іншими словами, конструктори використовуються для ініціалізації стану об'єкта при створенні об'єкта. Конструктори виглядають як звичайні методи, але це далеко не так:

  • Конструктори мають те ж ім'я, що і ім'я класу.
  • Конструктори, як і методи, мають список прийнятих параметрів, але не мають повертається тип (навіть void).

Нижче представлений набір з 7 основних правил роботи з конструкторами , Що дозволяють повністю розібратися в їх роботі.

  1. Конструктори можна перевантажувати:

Це означає, що клас може мати безліч різних конструкторів, якщо їх списки параметрів різні. наприклад:

Class Rectangle (int width; int height; Rectangle () (width = 1; height = 1;) Rectangle (int width) (this. Width = width; this. Height = width;) Rectangle (int width, int height) ( this. width = width; this. height = height;))

Маючи три різних конструктора Rectangleможна створювати новий об'єкт трьома різними способами:

Rectangle rect1 = new Rectangle (); Rectangle rect2 = new Rectangle (10); Rectangle rect3 = new Rectangle (10,20);

  1. Конструктор за замовчуванням:

Оголошення конструкторів зовсім не обов'язково. У разі якщо не визначено жодного з конструкторів, то компілятор Javaавтоматично генерує конструктор за замовчуванням, який порожній і не має параметрів.

Наприклад, якщо ми напишемо клас Rectangle наступним чином:

Class Rectangle (int widh, height; int area () () int perimeter () ())

Те компілятор автоматично вставляє конструктор за замовчуванням: ectangle () ()

  1. Компілятор буде генерувати конструктор за замовчуванням, якщо в класі вже є конструктор:

Розглянемо наступний приклад:

Class Rectangle (int width; int height; Rectangle (int width) (this. Width = width; this. Height = width;) Rectangle (int width, int height) (this. Width = width; this. Height = height;) )

При спробі створити новий об'єкт: Rectangle rect1 = new Rectangle (); Компілятор видасть помилку, тому що не може знайти конструктор без аргументів.

  1. Конструктори не успадковуються:

На відміну від методів, конструктори не успадковуються. приклад:

Class Rectangle (Rectangle (int width, int height) ()) class Square extends Rectangle ()

Не можна зробити щось на зразок цього: Square box = new Square (10, 10);

  1. Конструктори можуть бути приватними!

Можна зробити конструктор приватним, щоб не дати зовнішньому коду створити новий екземпляр класу. У чому ж може бути перевага приватного конструктора?

У шаблоні проектування, званому Singleton, Приватний конструктор використовується, щоб гарантувати, що завжди існує тільки один екземпляр класу. клас Singletonнадає статичний метод для отримання цього унікального екземпляра.

  1. Конструктор за замовчуванням має той же модифікатор доступу, що і клас:

При написанні наступного класу: public class Person ()

Компілятор при вставці конструктора за замовчуванням, вкаже і необхідний модифікатор доступу: public Preson ();

  1. Першим рядком будь-якого конструктора повинен викликатися або перевантажений конструктор того ж класу або конструктор його суперкласу:

Якщо це не так, то компілятор автоматично допише виклик конструктора суперкласу без аргументів super (); Це може привести до помилки, оскільки такого конструктора може не бути в суперкласі. приклад:

Class Parent (Parent (int number) ()) class Child extends Parent (Child () ())

Призведе до помилки, тому що компілятор вставляє виклик super () в конструкторі класу Child:

Child () (super (); // дописав компілятор)

Однак компілятор буде вставляти конструктор за замовчуванням для класу Parent, Тому що вже є інші конструктори.

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

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

public long idNUm;

public String name= "<Без имени>";

public Body orbits = null;

private static long nextID = 0;

idNUm = nextID ++;

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

Конструктор класу Body оголошений без параметрів. Його призначення досить важливо - він забезпечує унікальність значення поля idNum новостворюваного об'єкта класу. У вихідному варіанті класу невелика помилка програміста-користувача, пов'язана, наприклад, з неакуратно виконаною операцією присвоювання значення полю idNum або з відсутністю інструкцій збільшення вмісту поля nextID, могла б привести до того, що кілька об'єктів Body отримали б один і той самий порядковий номер. Подібний результат явно Помилковий, оскільки порушує ту частину контракту класу, в якій мовиться:

"Значення idNum різних об'єктів класу повинні бути унікальними".

Передавши відповідальність за вибір вірних значень idNum самому класу, ми раз і назавжди позбудемося подібних помилок. Тепер конструктор класу Body - це єдиний суб'єкт, який змінює вміст поля nextID і потребує доступу до нього. Тому ми можемо і повинні позначити змінну nextID модифікатором private, щоб запобігти можливості звернення до неї за межами класу. Зробивши це, ми виключимо один з потенційних джерел помилок, які загрожують майбутнім користувачам нашого класу.

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

Вирази ініціалізації змінних name і orbits в тілі оголошення класу присвоюють останнім деякі допустимі і доцільні значення. Тепер при створенні об'єкт автоматично набуває початковий набір задовольняють контрактом властивостей, що описують його стан. Далі ми вільні змінити деякі з них на свій розсуд:

Body sun = new Body (); // idNUm = 0

sun. name = "Сонце";

Body earth = new Body (); // idNum = 1

earth.name = "земля";

earth.orbits = sun;

У Процесі створення об'єкта за допомогою оператора new конструктор класу Body Викликається післяприсвоювання полях name і огbits передбачених нами Виразів ініціалізації.

Розглянутий вище випадок - коли ми заздалегідь, в момент написання програми інформовані про ім'я астрономічного об'єкта і про те, навколо якого небесного тіла проходить орбіта його руху, - відносно рідкісний. З цієї точки зору цілком резонним буде створити ще один конструктор, який приймає значення імені об'єкта і посилання на центральний об'єкт в якості аргументів:

Body (String bodyName, Body orbitsAround) (

name = bodyName; orbits = orbitsAround;

Як неважко помітити, один конструктор класу звертається до іншої за допомогою виразу this - першої виконуваної інструкції в тілі конструктора-ініціатора. Подібна пропозиція називають явним викликом конструктора.Якщо конструктор, до якого ви маєте намір звернутися явно, передбачає завдання аргументів, при виклику вони повинні бути передані. Який з конструкторів буде викликаний - це обумовлюється кількістю аргументів і набором їх типів. В даному випадку вираз this означає виклик конструктора без параметрів, що дозволяє встановити значення idNum об'єкта і збільшити поточний зміст статичного поля nextID на одиницю. Звернення до this дає можливість уникнути повторення коду ініціалізації idNum і зміни nextID. Тепер код, який передбачає створення об'єктів, стає істотно більш простим:

Body sun = new Bоdу ( "сонце", null);

Body earth = new Воdу ( "земля", sun);

Версія конструктора, що викликається в процесі виконання оператора new, визначається структурою списку переданих аргументів.

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

Керуючись міркуваннями логічної повноти, ми могли б ввести до складу класу Body ще один конструктор, який передбачає завдання єдиний аргумент і використовується для створення об'єктів, які представляють небесні тіла, які не є супутниками більших світил. До цього конструктору зручно звертатися, коли заздалегідь відомо, що значення другого аргументу одно null:

Body (String bodyName) (

this (bodyName, null);

Тут знову використовується прийом явного виклику конструктора.

Нерідкі ситуації, коли в контракті класу міститься вимога про те, щоб код, відповідальний за створення об'єктів цього класу, надавав конструкторам класу додаткову інформацію. Наприклад, ви як автор класу Body можете обумовити таку умову: "Всі об'єкти класу Body повинні володіти ім'ям". Щоб гарантувати його виконання, ви маєте право включити в список параметрів кожного з конструкторів класу параметр імені - тепер вам не доведеться піклуватися про ініціалізації поля name.

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

· Без допомоги конструкторів з параметрами деякі класи не в змозі забезпечити свої об'єкти прийнятними вихідними значеннями.

· При використанні додаткових конструкторів завдання визначення початкових властивостей об'єктів спрощується (наочний приклад - конструктор класу Body з двома параметрами).

· Створення об'єкта часто пов'язано з великими витратами через некоректне вибору ініціалізаторів. Наприклад, якщо об'єкт класу містить таблицю і обчислювальні витрати на її конструювання великі, було б зовсім нерозумно передбачати ініціалізатор за замовчуванням, свідомо знаючи, що пізніше так чи інакше доведеться відкинути результат його роботи і повторити ініціалізацію, щоб надати таблиці властивості, потрібні в конкретній ситуації . Доцільніше відразу застосувати відповідний конструктор, який здатний створити таблицю з необхідними властивостями.

· Якщо конструктор НЕ позначений ознакою publiс, коло суб'єктів, які можуть ним скористатися для створення екземплярів класу, обмежується. Ви маєте право, наприклад, заборонити програмістам, які використовують пакет, створювати об'єкти класу, передбачивши для всіх конструкторів класу ознака доступу на рівні пакета.

Дуже широке застосування знаходять і конструктори без параметрів. Якщо, оголошуючи клас, ви не створили жодного конструктора якої б то не було різновиди, компілятор автоматично включить до складу класу порожній конструктор без параметрів. Подібний конструктор (його називають конструктором за замовчуванням)створюється тільки в тому випадку, якщо інших конструкторів не існує, оскільки можна навести приклади класів (таких як, скажімо, Attr, розглянутий в наступному розділі), в яких застосування конструктора без параметрів недоцільно або зовсім неможливо. Якщо необхідні і конструктор без параметрів, і один або кілька додаткових конструкторів, перший повинен бути створений явно. Конструктор за замовчуванням отримує той же ознака доступу, що і клас, для якого він створюється, якщо оголошення класу забезпечено модифікатором publiс, то і конструктор

буде позначений як publiс.

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

Body (Body other) (

idNum = other.idNum;

name= Other.name;

orbits = other.orbits;

Підхід до вирішення завдання копіювання об'єктів, пов'язаний із застосуванням спеціальних конструкторів, в класах стандартних пакетів Java широкого поширення не знайшов, оскільки для досягнення такої мети кращим вважається використання методу Сlone (зверніться до розділу 3.9 на сторінці 111). Втім, простий конструктор копії передбачений, наприклад, у складі класу String, а в класах колекцій (вони розглядаються в главі 16)

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

У тексті конструкторів допускається згадка оголошуються винятків.

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

1. Поняття конструктора за замовчуванням

Конструктор за замовчуванням (default constructor) - це конструктор, який не має параметрів. Конструктор за замовчуванням може оголошуватися в класі явно або генеруватися автоматично.

У найбільш загальному випадку, для класу ClassName, конструктор за замовчуванням має наступний вигляд:

class ClassName (... // оголошення конструктора ClassName () ( // тіло конструктора // ... } ... }
2. У яких випадках конструктор за замовчуванням генерується в класі автоматично а в яких ні? приклад

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

Наприклад.У наступному оголошенні класу конструктор за замовчуванням генерується автоматично

class CMyClass ( int d; int GetD () ( return d; ) void SetD ( int nd) (d = nd;))

Вищенаведений код означає, що можна оголошувати об'єкт класу з використанням конструктора за замовчуванням:

// працює, так як в класі більше не реалізовано жодного конструктора CMyClass mc = new CMyClass ();

Якщо в тіло класу CMyClass додати хоча б один інший конструктор (наприклад, конструктор з одним параметром), то конструктор за замовчуванням автоматично генеруватися НЕ буде

class CMyClass ( int d; // конструктор за замовчуванням вже не генерується автоматично CMyClass ( int nd) (d = nd;) int GetD () ( return d; ) void Set ( int nd) (d = nd;))

Після вищенаведеної реалізації оголосити об'єкт з використанням конструктора за замовчуванням не вдасться. Однак, можна оголосити об'єкт з використанням конструктора з одним параметром

// помилка компіляції, так як в класі вже оголошений інший конструктор // CMyClass mc = new CMyClass (); CMyClass mc2 = new CMyClass (7); // а цей код працює

В результаті виконання вищенаведеної рядки буде видана помилка компіляції:

The constructor CMyClass () is undefined

Для того, щоб мати реалізацію конструктора за замовчуванням і оголошувати об'єкт класу з використанням конструктора за замовчуванням, його потрібно задавати явно. Це може бути, наприклад, таким чином

class CMyClass ( int d; // явне оголошення конструктора за замовчуванням CMyClass () (d = 0;) // оголошення конструктора з 1 параметром, CMyClass ( int nd) (d = nd;) int GetD () ( return d; ) void Set ( int nd) (d = nd;))

Після такої реалізації можна створювати екземпляр класу з використанням двох конструкторів, наприклад

CMyClass mc = new CMyClass (); // викликається конструктор за замовчуванням mc.d = 25; CMyClass mc2 = new CMyClass (5); // викликається конструктор з 1 параметром

3. Виклик конструкторів з інших конструкторів. приклад

Мова програмування Javaдозволяє здійснювати виклик конструкторів класу з іншого конструктора цього ж класу. Для цього використовується ключове слово this, яке є посиланням на поточний клас.

Приклад.У прикладі демонструється використання класу CPixel, який реалізує піксель на екрані монітора.

// Клас, який реалізує піксель на екрані монітора public class CPixel ( // внутрішні змінні класу private int x, y; // координати пікселя private int color; // колір пікселя // конструктор без параметрів (конструктор за замовчуванням) CPixel () (x = y = color = 0;) // конструктор з 2 параметрами, які ініціалізують тільки координати CPixel ( int _x, int _y) (x = _x; y = _y; color = 0;) // конструктор з 1 параметром, який ініціалізує тільки колір CPixel ( int _color) (color = _color; x = y = 0;) // конструктор з 3 параметрами, який викликає конструктор з 2 параметрами CPixel ( int _x, int _y, int _color) ( // виклик конструктора з 2 параметрами: обов'язково перша операція і тільки один раз this(_X, _y); // this (_color); // повторний виклик конструктора заборонений this.color = _color; // так можна ) // методи доступу int GetX () ( return x; ) int GetY () ( return y; ) int GetColor () ( return color; ))

Використання класу CPixel в іншому програмному коді (методі)

CPixel cp1 = new CPixel (2,8); // виклик конструктора з 2 параметрами CPixel cp2 = new CPixel (3,5,8); // виклик конструктора, який викличе інший конструктор int d; d = cp1.GetX (); // d = 2 d = cp2.GetColor (); // d = 8 d = cp2.GetY (); // d = 5 ...

4. Які обмеження (вимоги) накладаються на виклик інших конструкторів з конструктора класу?

Щоб коректно викликати інші конструктори з конструктора класу, потрібно дотримуватися наступних вимог (обмежень):

  • зателефонувати можна лише за один інший конструктор класу. Викликати два і більше інших конструктора цього класу заборонено. Це випливає з логіки, що конструктор класу призначений для створення об'єкта класу тільки один раз (а не два і більше рази);
  • виклик іншого конструктора повинен бути першою операцією в зухвалій конструкторі. Якщо в зухвалій конструкторі виклик іншого конструктора реалізувати другий (третій і т.д.) операцією, то компілятор видасть помилку.

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

Ти, можливо, чуєш цей термін вперше, але насправді напевно користувався конструкторами, тільки сам не помічав цього :) Ми переконаємося в цьому пізніше.

Що таке конструктори і навіщо вони потрібні?

Розглянемо два приклади. public class Car (String model; int maxSpeed; public static void main (String args) (Car bugatti = new Car (); bugatti. model = "Bugatti Veyron"; bugatti. maxSpeed ​​= 407;)) Ми створили наш автомобіль і встановили для нього модель і максимальну швидкість. Однак в реальному проекті у об'єкта Car явно буде не 2 поля. А, наприклад, 16 полів! public class Car (String model; // модель int maxSpeed; //максимальна швидкість//об'єм двигуна// прізвище власника// число місць в салоні String salonMaterial; // матеріал салону boolean insurance; // застрахована//країна виробник int trunkVolume; // обсяг багажника int accelerationTo100km; public static void main (String args) (Car bugatti = new Car (); bugatti. color = "blue"; bugatti. accelerationTo100km = 3; bugatti. engineVolume = 6.3; bugatti. manufacturerCountry = "Italy"; bugatti. ownerFirstName = " Amigo "; bugatti. yearOfIssue = 2016 року; bugatti. insurance = true; bugatti. price = 2000000; bugatti. isNew = false; bugatti. placesInTheSalon = 2; bugatti. maxSpeed ​​= 407; bugatti. model =" Bugatti Veyron ";)) ми створили новий об'єкт Car. Одна проблема: полів-то у нас 16, а проініціалізувати ми тільки 12! Спробуй зараз за кодом знайти ті, які ми забули! Не так-то просто, так? У такій ситуації програміст може легко помилитися і пропустити ініціалізацію якогось поля. В результаті поведінка програми стане помилковим: public class Car (String model; // модель int maxSpeed; //максимальна швидкість int wheels; // ширина дисків double engineVolume; //об'єм двигуна String color; // колір int yearOfIssue; // рік випуску String ownerFirstName; // ім'я власника String ownerLastName; // прізвище власника long price; // ціна boolean isNew; // нова чи ні int placesInTheSalon; // число місць в салоні String salonMaterial; // матеріал салону boolean insurance; // застрахована String manufacturerCountry; //країна виробник int trunkVolume; // обсяг багажника int accelerationTo100km; // розгін до 100 км / год в секундах public static void main (String args) (Car bugatti = new Car (); bugatti. color = "blue"; bugatti. accelerationTo100km = 3; bugatti. engineVolume = 6.3; bugatti. manufacturerCountry = "Italy"; bugatti. ownerFirstName = " Amigo "; bugatti. yearOfIssue = 2016 року; bugatti. insurance = true; bugatti. price = 2000000; bugatti. isNew = false; bugatti. placesInTheSalon = 2; bugatti. maxSpeed ​​= 407; bugatti. model =" Bugatti Veyron "; System. out. println ( "Модель Bugatti Veyron. Об'єм двигуна -"+ Bugatti. engineVolume + ", багажника -" + bugatti. trunkVolume + ", Салон зроблений з"+ Bugatti. salonMaterial + ", Ширина дисків -"+ Bugatti. wheels + ". Була пріоберетена в 2018 році паном"+ Bugatti. ownerLastName); )) Висновок в консоль: Модель Bugatti Veyron. Об'єм двигуна - 6.3, багажника - 0, салон зроблений з null, ширина дисків - 0. Була придбана в 2018 році паном nullВашій покупцеві, який віддав 2 мільйони доларів за машину, явно не сподобається, що його назвали " паном null"! А якщо серйозно, в результаті в нашій програмі виявився некоректно створений об'єкт - машина з шириною дисків 0 (тобто взагалі без дисків), відсутнім багажником, салоном, зробленим з невідомого матеріалу, та ще й належить незрозуміло кому. Можна тільки уявити, як така помилка може "вистрілити" при роботі програми! Нам потрібно якось уникнути подібних ситуацій. Треба, щоб в нашій програмі було обмеження: при створенні нового об'єкта машини для нього завждиповинні бути вказані, наприклад, модель і максимальна швидкість. Інакше - не дозволяти створення об'єкта. З цим завданням легко справляються функції-конструктори. Вони отримали свою назву не просто так. Конструктор створює своєрідний "каркас" класу, якому кожен новий об'єкт класу повинен відповідати. Давай для зручності повернемося до більш простому варіанту класу Car з двома полями. З урахуванням наших вимог, конструктор для класу Car буде виглядати так: public Car (String model, int maxSpeed) (this. Model = model; this. MaxSpeed ​​= maxSpeed;) А створення об'єкта тепер виглядає так: public static void main (String args ) (Car bugatti = new Car ( "Bugatti Veyron", 407);) Зверни увагу, як створюється конструктор. Він схожий на звичайний метод, але у нього немає типу значення, що повертається. При цьому в конструкторі вказується назва класу, теж з великої літери. У нашому випадку - Car. Крім того, в конструкторі використовується нове для тебе ключове слово this. "This" по-англійськи - "цей, цього". Це слово вказує на конкретний предмет. Код в конструкторі: public Car (String model, int maxSpeed) (this. Model = model; this. MaxSpeed ​​= maxSpeed;) можна перевести майже дослівно: "model для цієї машини (яку ми зараз створюємо) = аргументу model, який вказаний в конструкторі. maxSpeed ​​для цієї машини (яку ми створюємо) = аргументу maxSpeed, який вказаний в конструкторі. " Так і сталося: public class Car (String model; int maxSpeed; public Car (String model, int maxSpeed) (this. Model = model; this. MaxSpeed ​​= maxSpeed;) public static void main (String args) (Car bugatti = new Car ( "Bugatti Veyron", 407); System. out. println (bugatti. model); System. out. println (bugatti. maxSpeed);)) Висновок в консоль: Bugatti Veyron 407Конструктор успішно присвоїв потрібні значення. Ти, можливо, зауважив, що конструктор дуже схожий на звичайний метод! Так воно і є: конструктор - це метод, тільки трохи специфічний :) Так само як в метод, в наш конструктор ми передали параметри. І так само як виклик методу, виклик конструктора не спрацює, якщо їх не вказати: public class Car (String model; int maxSpeed; public Car (String model, int maxSpeed) (this. Model = model; this. MaxSpeed ​​= maxSpeed;) public static void main (String args) (Car bugatti = new Car (); // помилка!)) Бачиш, конструктор зробив те, чого ми намагалися добитися. Тепер можна створити машину без швидкості або без моделі! На цьому схожість конструкторів і методів не закінчується. Так само, як і методи, конструктори можна перевантажувати. Уяви, що у тебе вдома живуть 2 кота. Одного з них ти взяв ще кошеням, а другого ти приніс додому з вулиці вже дорослим і не знаєш точно, скільки йому років. Значить, наша програма повинна вміти створювати котів двох видів - з ім'ям і віком для першого кота, і тільки з ім'ям - для другого кота. Для цього ми перевантажимо конструктор: public class Cat (String name; int age; // для першого кота // для другого кота public Cat (String name) (this. name = name;) public static void main (String args) (Cat barsik = new Cat ( "Barsik", 5); Cat streetCatNamedBob = new Cat ( "Bob");)) До споконвічного конструктору з параметрами "ім'я" і "вік" ми додали ще один, тільки з ім'ям. Точно так само ми перевантажували методи в минулих уроках. Тепер ми успішно можемо створити обидва варіанти котів :)

Пам'ятаєш, на початку лекції ми говорили, що ти вже користувався конструкторами, тільки сам не помічав цього? Так і є. Справа в тому, що у кожного класу в Java є так званий конструктор за замовчуванням. У нього немає ніяких аргументів, але він спрацьовує кожного разу при створенні будь-якого об'єкта будь-якого класу. public class Cat (public static void main (String args) (Cat barsik = new Cat ();)) На перший погляд це непомітно. Ну створили об'єкт і створили, де тут робота конструктора? Щоб це побачити, давай прямо руками напишемо для класу Cat порожній конструктор, а всередині нього виведемо якусь фразу в консоль. Якщо вона виведеться, значить конструктор відпрацював. public class Cat (public Cat () (System. out. println ( "Створили кота!");) public static void main (String args) (Cat barsik = new Cat (); // ось тут спрацював конструктор за замовчуванням } } Висновок в консоль: Створили кота! Ось і підтвердження! Конструктор за замовчуванням завжди незримо присутній в твоїх класах. Але тобі потрібно знати ще одну його особливість. Дефолтний конструктор зникає з класу, коли ти створюєш якийсь конструктор з аргументами.Доказ цього, насправді, ми вже бачили вище. Ось в цьому коді: public class Cat (String name; int age; public Cat (String name, int age) (this. Name = name; this. Age = age;) public static void main (String args) (Cat barsik = new Cat (); // помилка!)) Ми не змогли створити кота без імені і віку, тому що визначили конструктор для Cat: рядок + число. Дефолтний конструктор відразу після цього зникз класу. Тому обов'язково запам'ятай: якщо тобі в твоєму класі потрібно кілька конструкторів, включаючи порожній, його потрібно створити окремо. Наприклад, ми створюємо програму для ветеринарної клініки. Наша клініка хоче робити добрі справи і допомагати бездомним котиків, про яких ми не знаємо ні імені, ні віку. Тоді наш код повинен виглядати так: public class Cat (String name; int age; // для домашніх котів public Cat (String name, int age) (this. name = name; this. age = age;) // для вуличних котів public Cat () () public static void main (String args) (Cat barsik = new Cat ( "Barsik", 5); Cat streetCat = new Cat ();)) Тепер, коли ми явно прописали конструктор за замовчуванням, ми можемо створювати котів обох типів :) Для конструктора (як і для будь-якого методу) дуже важливий порядок проходження аргументів.Поміняємо в нашому конструкторі аргументи імені і віку місцями. public class Cat (String name; int age; public Cat (int age, String name) (this. name = name; this. age = age;) public static void main (String args) (Cat barsik = new Cat ( "Барсик ", 10); // помилка!)) Помилка! Конструктор чітко описує: при створенні об'єкта Cat йому повинні бути передані число і рядок, саме в такому порядку. Тому наш код не спрацьовує. Обов'язково запам'ятай це і зважай при створенні своїх власних класів: public Cat (String name, int age) (this. Name = name; this. Age = age;) public Cat (int age, String name) (this. Age = age; this. name = name;) Це два абсолютно різних конструктора! Якщо виразити в одному реченні відповідь на питання "Навіщо потрібен конструктор?", можна сказати: для того, щоб об'єкти завжди знаходилися в правильному стані. Коли ти використовуєш конструктори, всі твої змінні будуть коректно проініціалізовані, і в програмі не буде машин зі швидкістю 0 і інших "неправильних" об'єктів. Їх використання дуже вигідно перш за все для самого програміста. Якщо ти будеш форматувати значення будуть працювати, великий ризик що-небудь пропустити і помилитися. А з конструктором такого не буде: якщо ти передав в нього не всі необхідні аргументи або переплутав їх типи, компілятор відразу ж видасть помилку. Окремо варто сказати про те, що всередину конструктора не варто поміщати логіку твоєї програми. Для цього в твоєму розпорядженні є методи, В яких ти можеш описати весь потрібний тобі функціонал. Давай подивимося, чому логіка в конструкторі - це погана ідея: public class CarFactory (String name; int age; int carsCount; public CarFactory (String name, int age, int carsCount) (this. Name = name; this. Age = age; this. carsCount = carsCount; System. out. println ( "Вона була заснована" "В середньому вона виробляє"+ (This. CarsCount / this. Age) + "машин в рік"); ) Public static void main (String args) (CarFactory ford = new CarFactory ( "Ford", 115, 50000000);)) У нас є клас CarFactory, Що описує фабрику з виробництва автомобілів. Усередині конструктора ми инициализируем все поля, і сюди ж поміщаємо логіку: виводимо в консоль деяку інформацію про фабрику. Здавалося б - нічого поганого в цьому немає, програма прекрасно відпрацювала. Висновок в консоль: Наша автомобільна фабрика називається Ford Вона була заснована 115 років тому За цей час на ній було вироблено 50000000 автомобілів В середньому вона виробляє 434782 машин на рікАле насправді ми заклали міну уповільненої дії. І подібний код може дуже легко призвести до помилок. Уявімо собі, що тепер ми говоримо що ні про Ford, а про нову фабриці "Amigo Motors", яка існує менше року і справила 1000 автомобілів: public class CarFactory (String name; int age; int carsCount; public CarFactory (String name, int age, int carsCount) (this. name = name; this. age = age; this. carsCount = carsCount; System. out. println ( "Наша автомобільна фабрика називається"+ This. name); System. out. println ( "Вона була заснована"+ This. age + "років тому"); System. out. println ( "За цей час на ній було вироблено"+ This. carsCount + "автомобілів"); System. out. println ( "В середньому вона виробляє"+ (This. CarsCount / this. Age) + "машин в рік"); ) Public static void main (String args) (CarFactory ford = new CarFactory ( "Amigo Motors", 0 1000);)) Висновок в консоль: Наша автомобільна фабрика називається Amigo Motors Exception in thread "main" java.lang.ArithmeticException: / by zero Вона була заснована 0 років тому За цей час на ній було вироблено 1000 автомобілів at CarFactory. (CarFactory.java:15) at CarFactory.main (CarFactory.java:23) Process finished with exit code 1Приїхали! Програма завершилася з якоїсь незрозумілої помилкою. Спробуєш здогадатися, в чому причина? Причина - в логіці, яку ми помістили в конструктор. А конкретно - ось в цьому рядку: System. out. println ( "В середньому вона виробляє"+ (This. CarsCount / this. Age) + "машин в рік"); Тут ми виконуємо обчислення і ділимо кількість вироблених машин на вік фабрики. А оскільки наша фабрика нова (тобто їй 0 років) - в результаті виходить ділення на 0, яке в математиці заборонено. В результаті програма завершується з помилкою. Як же нам коштувало вчинити?Винести всю логіку в окремий метод і назвати його, наприклад, printFactoryInfo (). Як параметр йому можна передати об'єкт CarFactory. Туди ж можна помістити всю логіку, і заодно - обробку можливих помилок, На зразок нашої з нулем років. Кожному своє. Конструктори потрібні для коректного завдання стану об'єкта.Для бізнес-логіки у нас є методи. Не варто змішувати одне з іншим. Ось кілька корисних посилань, де ти можеш додатково почитати про конструкторів: