Неинициализированные переменные и члены в Java - PullRequest
27 голосов
/ 06 ноября 2008

Учтите это:

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», что является ошибкой времени компиляции. Почему разница между локальными переменными и членами?

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

Ответы [ 7 ]

44 голосов
/ 06 ноября 2008

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

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

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

22 голосов
/ 06 ноября 2008

Вот сделка. Когда вы звоните

TestClass tc = new TestClass();

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

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

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

НТН

PS: для тяжелой бессонницы, прочитайте это .

13 голосов
/ 06 ноября 2008

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

3 голосов
/ 06 ноября 2008

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

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

3 голосов
/ 06 ноября 2008

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

2 голосов
/ 14 апреля 2012

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

Локальные переменные НЕ УКАЗАНЫ и не инициализированы, и вы несете ответственность за установку начального значения. Компилятор не позволяет вам их использовать.

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

Примечание: ноль отличается от неопределенного.

1 голос
/ 26 января 2017

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...