Неинициализированные переменные и члены в Java

Рассмотрим это:

public class TestClass {

    private String a;
    private String b;

    public TestClass()
    {
    a = "initialized";
    }

    public void doSomething()
    {
    String c;

        a.notify(); // This is fine
    b.notify(); // This is fine - but will end in an exception
    c.notify(); // "Local variable c may not have been initialised"
    }

}

Я не понимаю. "b" никогда не инициализируется, но будет давать ту же ошибку времени выполнения, что и "c", что является ошибкой времени компиляции. Почему разница между локальными переменными и членами?

Изменить: включение личных членов было моим первоначальным намерением, и вопрос все еще стоит...

Ответ 1

Правила для определенного задания довольно сложны (см. главу 16 JLS 3rd Ed). Нецелесообразно применять определенное задание на полях. Как бы то ни было, даже до того, как они будут инициализированы, возможно даже наблюдать окончательные поля.

Ответ 2

Язык определяет его таким образом.

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

См. раздел 4.5.5 здесь http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

Ответ 3

Здесь сделка. Когда вы вызываете

TestClass tc = new TestClass();

команда new выполняет четыре важные задачи:

  • Выделяет память в куче для нового объекта.
  • Инициирует поля класса к их значениям по умолчанию (численные значения равны 0, boolean - false, объекты - null).
  • Вызывает конструктор (который может повторно инициировать поля, а может и не).
  • Возвращает указатель на новый объект.

Итак, ваши поля "a" и "b" инициируются как null, а "a" перезапускается в конструкторе. Этот процесс не имеет отношения к вызову метода, поэтому локальная переменная 'c' никогда не инициализируется.

НТН

PS: для тяжелого бессознательного читайте this.

Ответ 4

Компилятор может понять, что c никогда не будет установлен. Переменная b может быть установлена ​​кем-то еще после вызова конструктора, но перед doSomething(). Сделайте b частным, и компилятор может помочь.

Ответ 5

Компилятор может определить из кода doSomething(), что c объявлен там и никогда не инициализирован. Поскольку он является локальным, нет никакой возможности, что он инициализируется в другом месте.

Он не может определить, когда и где вы собираетесь называть doSomething(). b является публичным участником. Вполне возможно, что вы должны инициализировать его в другом коде перед вызовом метода.

Ответ 6

Члены-переменные инициализируются нулевыми или их примитивными значениями по умолчанию, если они являются примитивами.

Локальные переменные UNDEFINED и не инициализированы, и вы несете ответственность за установку начального значения. Компилятор не позволяет вам использовать их.

Следовательно, b инициализируется, когда экземпляр класса TestClass создается, а c - undefined.

Примечание: значение null отличается от undefined.

Ответ 7

Фактически вы определили одну из больших дыр в системе Java, которая обычно пытается найти ошибки во время редактирования/компиляции, а не время выполнения, потому что, как сказал принятый ответ, трудно определить, инициализирован ли b или нет.

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

Второй подход - строгая проверка нуля. Вы можете включить его в настройках eclipse либо по проекту, либо по умолчанию. Я верю, что это заставит вас нейтрализовать ваш b.notify(), прежде чем вы его вызовете. Это может быстро выйти из-под контроля, поэтому оно имеет тенденцию идти с набором аннотаций, чтобы сделать вещи проще:

Аннотации могут иметь разные имена, но в концепции, когда вы включаете строгую проверку нуля, а аннотации типов переменных "обнуляются" и "NotNull". Если вы попытаетесь поместить переменную Nullable в непустую переменную, вы должны сначала проверить ее на null. Параметры и типы возвращаемых данных также аннотируются, поэтому вам не нужно проверять значение null каждый раз, когда вы назначаете непустую переменную.

Существует также аннотация уровня пакета NotNullByDefault, которая заставит редактора предположить, что никакая переменная никогда не может иметь нулевое значение, если вы не помечаете ее Nullable.

Эти аннотации в основном применяются на уровне редактора - вы можете включить их в затмение и, возможно, другие редакторы - вот почему они не обязательно стандартизированы. (По крайней мере, в прошлый раз, когда я проверяю, Java 8 может иметь некоторые аннотации, которые я еще не нашел)