Должен ли я инстанцировать переменные экземпляра при объявлении или в конструкторе?

Есть ли преимущества у того или иного подхода?

Пример 1:

class A {
    B b = new B();
}

Пример 2:

class A {
    B b;

    A() {
         b = new B();
    }
}
Решение
  • Нет никакой разницы - переменная экземпляра инициализации фактически поставлен в конструктор(ы) с помощью компилятора.
  • Первый вариант более читабельный.
  • Вы можете'Т есть обработка исключений с первого варианта.
  • Есть также инициализацию блока, который также положил в конструктор(ы) с помощью компилятора:

{ а = новый(); }

Проверьте в <а href="и http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html">солнце&#39;объяснение и советы</а>

От в <а href="и http://docstore.mik.ua/orelly/java-ent/jnut/ch03_02.htm" и GT;учебник</а>:

В поле декларации, однако, не являются частью какого-либо метода, поэтому они не могут быть выполнены, как заявления. Вместо этого компилятор Java генерирует экземпляр-поле код инициализации автоматически и помещает его в конструктор или конструкторы для класса. Кода инициализации вставляется в конструктор в порядке, он появляется в исходном коде, это означает, что поле инициализатор можно использовать начальные значения полей, объявленных до этого.

Кроме того, вы, возможно, захотите, чтобы lazily initialize своей области. В случаях, когда инициализация поля является дорогостоящей операцией, вы можете инициализировать его, как только это необходимо:

ExpensiveObject o;

public ExpensiveObject getExpensiveObject() {
    if (o == null) {
        o = new ExpensiveObject();
    }
    return o;
}

И в конечном итоге (как указал Билл), ради управления зависимостями, лучше avoid с помощью оператора new в любом месте в пределах своего класса. Вместо этого, с использованием <а href="и http://en.wikipedia.org/wiki/Dependency_injection">внедрения зависимостей</а> предпочтительнее - т. е. позволить кому-нибудь другому (другой класс/Основы) создания и внедрения зависимостей в вашем классе.

Комментарии (6)

Другим вариантом было бы использовать инъекция зависимостей.

class A{
   B b;

   A(B b) {
      this.b = b;
   }
}

Это снимает ответственность за создание объекта Б из конструктора "а". Это сделает ваш код более проверяемым и легче поддерживать в долгосрочной перспективе. Идея в том, чтобы уменьшить сцепление между двумя классами " а " и "Б". Благо, что это дает вам, что теперь вы можете передать любой объект, который расширяет Б (или реализует "Б", если он является интерфейсом) в A's в конструктор и он будет работать. Одним недостатком является то, что вы даете инкапсуляцию объекта Б, поэтому он подвержен абонентом а конструктор. Вы'll должны рассмотреть, если выгоды стоит это компромисс, но во многих случаях они.

Комментарии (11)

Я погорел в интересной форме сегодня:

class MyClass extends FooClass {
    String a = null;

    public MyClass() {
        super();     // Superclass calls init();
    }

    @Override
    protected void init() {
        super.init();
        if (something)
            a = getStringYadaYada();
    }
}

Видите ошибку? Получается, что инициализаторе А = нуль вызывается после конструктор суперкласса вызывается. Поскольку конструктор суперкласса вызывает init(), инициализация а затем к А = нуль инициализации.

Комментарии (3)

мое личное и"правило" и (редко сломанный) - это:

  • объявлять все переменные в начале блок
  • сделать все переменные окончательным, если они не может быть
  • объявить одну переменную в строке
  • никогда не инициализировать переменную, в которой объявлен
  • только инициализировать что-то в конструктор, когда ей нужны данные из конструктор делать инициализация

Так что я бы код, как:

public class X
{
    public static final int USED_AS_A_CASE_LABEL = 1; // only exception - the compiler makes me
    private static final int A;
    private final int b;
    private int c;

    static 
    { 
        A = 42; 
    }

    {
        b = 7;
    }

    public X(final int val)
    {
        c = val;
    }

    public void foo(final boolean f)
    {
        final int d;
        final int e;

        d = 7;

        // I will eat my own eyes before using ?: - personal taste.
        if(f)
        {
            e = 1;
        }
        else
        {
            e = 2;
        }
    }
}

Этот путь я всегда 100% уверен, где искать переменные (в начале блока), и их назначения (как только это имеет смысл после провозглашения). Это ветры потенциально является более эффективным, поскольку вы никогда не инициализировать переменную со значением, которое не используется (например объявить и init vars и затем бросать исключение, прежде чем половина этих Варс необходимо иметь значение). Вы также не ветер делает бессмысленным инициализации (вроде int я = 0; и потом, прежде чем"Я &quot и; используется, Я = 5;.

Я ценю постоянство очень много, так что после этого на "правило" это то, чем я занимаюсь все время, и это делает его гораздо легче работать с кодом, так как вы не'т придется охотиться вокруг, чтобы найти вещи.

Ваш пробег может варьироваться.

Комментарии (9)

Пример 2 является менее гибким. Если вы добавить еще один конструктор, вы должны помнить, чтобы инстанцировать области, а также в том, что конструктор. Просто инстанцировать поля непосредственно, или ввести где-то ленивая загрузка в геттер.

Если инстанцирование требует больше, чем просто "новое", использовать инициализатор блок. Это будет работать вне зависимости от используемого конструктора. Е. Г.

public class A {
    private Properties properties;

    {
        try {
            properties = new Properties();
            properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("file.properties"));
        } catch (IOException e) {
            throw new ConfigurationException("Failed to load properties file.", e); // It's a subclass of RuntimeException.
        }
    }

    // ...

}
Комментарии (0)

Я воспринимаю это почти просто дело вкуса, покуда инициализация простой и не'т необходимости каких-либо логике.

Конструктор подход немного более хрупким, если вы не'т использовать инициализатор блок, потому что если вы позже добавить второй конструктор и забудьте инициализировать б здесь, вы'будете получать нуль только при использовании, что в прошлом конструктор.

См http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html для получения более подробной информации об инициализации в Java (и за разъяснения по initalizer блоки и другие не известные функции инициализации).

Комментарии (3)

Используя либо инъекции зависимостей или отложенная инициализация всегда предпочтительнее, так как уже подробно об этом в другой ответы.

Когда вы Don'т хотите или можете'т использовать эти образцы, а для примитивных типов данных, есть три веские причины, что я могу думать, почему это'ов предпочтительнее, чтобы инициализировать атрибуты класса вне конструктора:

  1. избежать повторения = если у вас есть более чем один конструктор, или когда вам нужно, чтобы добавить больше, вы выиграли'т придется повторить инициализацию снова и снова во всех конструкторах органов;
  2. улучшена читаемость = вы можете легко сказать, с первого взгляда, какие переменные должны быть инициализированы вне класса;
  3. сокращение строк кода = для каждого инициализация выполнена в декларации строка меньше в конструкторе.
Комментарии (0)

Оба метода допустимы. Обратите внимание, что в последнем случае b=new B() может не инициализироваться, если присутствует другой конструктор. Считайте, что код инициализатора вне конструктора является общим конструктором и код выполняется.

Комментарии (0)

Я думаю, что пример 2 предпочтительнее. Я думаю, что лучшей практикой является объявление вне конструктора и инициализация в конструкторе.

Комментарии (0)

Я'вэ не видел следующие ответы:

Возможное преимущество инициализация в момент объявления может быть с в настоящее время язь's, где вы можете очень легко перейти к объявлению переменной (в основном Сочетание клавиш Ctrl-<hover_over_the_variable и GT;-<left_mouse_click>) из любого места в коде. Вы тогда сразу увидите значение этой переменной. В противном случае, у вас есть, чтобы "Поиск" и на том месте, где инициализация делается (в основном:) конструктор.

Это преимущество является, конечно, общее для всех других логических рассуждений, но для некоторых людей, что "функция" и может быть более важным.

Комментарии (0)

Есть еще один тонкий повод, чтобы инициализировать вне конструктора, который никто не упомянул раньше (очень специфические, надо сказать). Если вы используете для создания инструментов UML диаграммы классов из кода (обратный инжиниринг), большинство инструментов я считаю отмечу инициализации примере 1, и перенести его на схему (если вы предпочитаете, чтобы она показала начальные значения, как я делаю). Они не будут принимать эти начальные значения из примера 2. Опять же, это очень специфическая причина - если вы работаете с UML инструменты, но как только я узнал, что, я пытаюсь принять все по умолчанию значения вне конструктора, если, как было сказано раньше, возникает вопрос о возможных исключений или сложная логика.

Комментарии (0)

Второй вариант предпочтительней, так как позволяет использовать различную логику в конструкторы для создания экземпляра класса и использовать конструкторы сцепления. Е. Г.

class A {
    int b;

    // secondary ctor
    A(String b) {
         this(Integer.valueOf(b));
    }

    // primary ctor
    A(int b) {
         this.b = b;
    }
}

Так что второго варианта является более гибким.

Комментарии (0)
    class MyClass extends FooClass {
    String a = null;

    public MyClass() {
        super();     // Superclass calls init();
    }

    @Override
    protected void init() {
        super.init();
        if (something)
            a = getStringYadaYada();
    }
}

В связи с вышесказанным

String a = null;

нуль инит можно было бы избежать, т. к. все равно это'ы по умолчанию. Однако, если нужно другое значение по умолчанию, затем, из-за неконтролируемого порядок инициализации, Я бы исправить следующим образом:

class MyClass extends FooClass 
{
    String a;
    {
        if( a==null ) a="my custom default value";
    }
    ...
Комментарии (0)

Это'ы довольно разные на самом деле:

Декларация происходит до начала строительства. Так сказать, если кто инициализировал переменную (B в данном случае) как на места, конструктор'ы инициализации заменит на уровне класса.

Так объявлять переменные на уровне класса, инициализировать их в конструкторе.

Комментарии (0)

Второй - пример ленивой инициализации. Первый - более простая инициализация, но по сути они одинаковы.

Комментарии (0)