Объявления полей, однако, не являются частью какого-либо метода, поэтому они не могут выполняться как инструкции. Вместо этого компилятор Java автоматически генерирует код инициализации поля экземпляра и помещает его в конструктор или конструкторы для класса. Код инициализации вставляется в конструктор в том порядке, в каком он отображается в исходном коде, что означает, что инициализатор поля может использовать начальные значения полей, объявленных перед ним.
Кроме того, вы можете лениво инициализировать свое поле. В случаях, когда инициализация поля является дорогостоящей операцией, вы можете инициализировать его, как только это потребуется:
ExpensiveObject o;
public ExpensiveObject getExpensiveObject() { if (o == null) { o = newExpensiveObject(); } return o; }
И, в конечном счете (как указал Билл), ради управления зависимостями лучше избегать использования new оператора в любом месте вашего класса. Вместо этого предпочтительнее использовать внедрение зависимостей, т. Е. Позволить кому-то другому (другому классу / фреймворку) создавать экземпляры и внедрять зависимости в ваш класс.
Это снимает ответственность за создание B объекта из конструктора A. Это сделает ваш код более тестируемым и более простым в обслуживании в долгосрочной перспективе. Идея состоит в том, чтобы уменьшить связь между двумя классами A и B. Преимущество, которое это дает вам, заключается в том, что теперь вы можете передать любой объект, который расширяет B (или реализует B, если это интерфейс), в Aконструктор, и он будет работать. Одним из недостатков является то, что вы отказываетесь от инкапсуляции B объекта, поэтому он предоставляется вызывающей стороне A конструктора. Вам придется подумать, стоят ли преимущества такого компромисса, но во многих случаях так оно и есть.
@Override protectedvoidinit() { super.init(); if (something) a = getStringYadaYada(); } }
Видите ошибку? Оказывается, что a = null инициализатор вызывается после вызова конструктора суперкласса. Поскольку конструктор суперкласса вызывает init(), за инициализацией aследуетa = null инициализация.
Ответ 4
мое личное "правило" (которое почти никогда не нарушается) заключается в том, чтобы:
объявляйте все переменные в начале блока
сделайте все переменные окончательными, если они не могут быть
объявляйте по одной переменной в строке
никогда не инициализируйте переменную там, где она объявлена
инициализируйте что-либо в конструкторе только тогда, когда для выполнения инициализации требуются данные из конструктора
Итак, у меня был бы код типа:
publicclassX { publicstaticfinalintUSED_AS_A_CASE_LABEL=1; // only exception - the compiler makes me privatestaticfinalint A; privatefinalint b; privateint c;
// I will eat my own eyes before using ?: - personal taste. if(f) { e = 1; } else { e = 2; } } }
Таким образом, я всегда на 100% уверен, где искать объявления переменных (в начале блока) и их назначения (как только это имеет смысл после объявления). Это также потенциально оказывается более эффективным, поскольку вы никогда не инициализируете переменную значением, которое не используется (например, объявляете и инициализируете переменные, а затем создаете исключение до того, как половина этих переменных должна иметь значение). Вы также не выполняете бессмысленную инициализацию (например, int i = 0; а затем позже, перед использованием "i", делайте i = 5;.
Я очень ценю согласованность, поэтому следование этому "правилу" - это то, что я делаю постоянно, и это намного упрощает работу с кодом, поскольку вам не нужно искать что-то вокруг, чтобы найти что-то.