Чим ініціалізатор екземпляра відрізняється від конструктора?


82

Іншими словами, навіщо вам потрібен ініціалізатор примірника? Яка різниця чи перевага у вас при написанні ініціалізатора екземпляра над конструктором?


2
Ініціатори екземплярів зустрічаються досить рідко (якщо ви не врізаєтеся в код від когось, хто цікавиться ідіомою подвійних дужок).
Том Хоутін - таклін

Відповіді:


109

Здається, це добре пояснює це:

Ініціалізатори екземплярів є корисною альтернативою ініціалізаторам змінних екземплярів, коли:

  • код ініціалізатора повинен ловити винятки, або

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

Але в класі, який мав кілька конструкторів, вам доведеться повторити код у кожному конструкторі. За допомогою ініціалізатора екземпляра ви можете просто написати код один раз, і він буде виконаний незалежно від того, який конструктор використовується для створення об’єкта. Ініціатори екземплярів також корисні в анонімних внутрішніх класах, які взагалі не можуть оголошувати жодні конструктори.

З: JavaWorld Об’єкт ініціалізації в Java .


17
З іншого боку, ви можете написати код один раз в одному конструкторі і просто викликати його з усіх інших конструкторів. Але анонімні внутрішні класи мають добру думку.
Тадеуш Копець

11
Ви можете зателефонувати йому з інших конструкторів - але тоді ви знову повторюєте виклик. Якщо ви додаєте новий конструктор, вам слід пам’ятати, що потрібно додати до нього виклик. З ініціалізатором екземпляра не так.
talonx

5
@talonx, я погоджуюся з вашим аргументом про забуття, але використання поведінки за замовчуванням однаково небезпечно. Коли прихильник читає конструктор у застарілому коді, не завжди пам’ятатиме про перевірку можливих ініціалізаторів екземплярів. Тоді як явно використаний init () буде виділятися.
Ассамбар

1
@ javamonkey79: Ви хочете сказати, що якщо я вибираю конструктор над ініціалізатором екземпляра для свого класу, єдиним місцем, де ініціалізатор екземпляра є корисним, є робота з анонімними класами?
realPK

4
@Assambar Ви не можете призначити остаточні поля у своєму методі init (), але можете в блоці ініціалізації.
Тіммос,

22

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

Семантично ініціалізатор - це приємний інструмент з кількох причин:

ініціалізатор може покращити читабельність коду, зберігаючи логіку ініціалізації поруч із ініціалізованою змінною:

   public class Universe {
       public int theAnswer;
       {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;
       }

       // a bunch of other vars
   }

проти

   public class Universe {
       public int theAnswer;

       // a bunch of other vars

       public Universe() {
         int SIX = 6;
         int NINE = 7;
         theAnswer = SIX * NINE;

         // other constructor logic
       }
   }

Ініціалізатори викликаються незалежно від того, який конструктор використовується.

Ініціалізатори можна використовувати в анонімних внутрішніх класах, де конструктори не можуть.


3
Те, що у вас є, - це технічно "ініціалізатор змінної екземпляра", а не "ініціалізатор екземпляра" (блок, безпосередньо вкладений у клас). Див. 3-й розділ JLS 8.6.
Том Хоутін - таклін

Блок ініціалізації екземпляра не має імені, як показано тут . Чи не так? Ви позначили ім'я коду ініціалізатора екземпляра theAnswer. Це правильно? або мені чогось не вистачає.
RBT

1
@RBT theAnswer- це оголошена змінна екземпляра. Він ініціалізується в анонімному блоці ініціалізації. Зверніть увагу на крапку з комою після оголошення змінної.
ykaganovich

10

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


4

Я б уникнув ідіоми ініціалізатора екземпляра загалом - єдина реальна перевага, яку він дає перед ініціалізаторами змінних, це обробка винятків.

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


1
Вам доведеться вручну викликати метод init у ВСІХ ваших конструкторах.
Stephan

2
@Alex Хороша думка, але оскільки більшість класів з декількома конструкторами мають спільну логіку, вони в будь-якому випадку скликають один одного. Принаймні на більшості моїх занять.
CPerkins

3

Реальна перевага примірника ініціалізаторів над конструкторами видно , коли ми використовуємо анонімний внутрішній клас .

Анонімні внутрішні класи не можуть мати конструктор (оскільки вони анонімні) , тому вони цілком природно підходять для ініціалізаторів, наприклад .


1

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

Ми не можемо замінити конструктор блоком екземпляра, оскільки конструктор може приймати аргументи, але блок екземпляра не може приймати аргументи.

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

Приклад:

class MyClass{

    static int object_count = 0;

    MyClass(){
        object_count++;
    }

    MyClass(int i){

        object_count++;
    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Вихід: 2

class MyClass{

    static int object_count = 0;

    {
        object_count++;
    }

    MyClass(){

    }

    MyClass(int i){     

    }

    void getCount() {

        System.out.println(object_count);
    }

    public static void main(String... args) {
        MyClass one = new MyClass();
        MyClass two = new MyClass(2);
        two.getCount();
    }
}

Вихід: 2


0

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

Компілятор Java копіює блоки ініціалізатора в кожен конструктор. Отже, цей підхід можна використовувати для спільного використання коду між кількома конструкторами. Документація Oracle

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.