Вопрос-ответ

Why are local variables not initialized in Java?

Почему локальные переменные не инициализированы в Java?

Была ли какая-либо причина, по которой разработчики Java считали, что локальным переменным не следует присваивать значение по умолчанию? Серьезно, если переменным экземпляра можно присвоить значение по умолчанию, то почему мы не можем сделать то же самое для локальных переменных?

И это также приводит к проблемам, как описано в этом комментарии к сообщению в блоге:


Что ж, это правило больше всего расстраивает при попытке закрыть ресурс в блоке finally. Если я создаю экземпляр ресурса внутри try, но пытаюсь закрыть его внутри finally, я получаю эту ошибку. Если я перемещаю создание экземпляра за пределы try , я получаю другую ошибку, указывающую, что a это должно быть в пределах try.


Очень неприятно.


Переведено автоматически
Ответ 1

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

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

Ответ 2

"Проблема", на которую вы ссылаетесь, кажется, описывает эту ситуацию:

SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}

Жалоба комментатора заключается в том, что компилятор отказывается от строки в finally разделе, утверждая, что so она может быть неинициализированной. Затем в комментарии упоминается другой способ написания кода, вероятно, что-то вроде этого:

// Do some work here ...
SomeObject so = new SomeObject();
try {
so.DoUsefulThings();
} finally {
so.CleanUp();
}

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

В любом случае, эта вторая версия кода является правильным способом его написания. В первой версии сообщение об ошибке компилятора было правильным. so Переменная может быть неинициализированной. В частности, если SomeObject конструктор выходит из строя, so не будет инициализирован, и поэтому попытка вызова будет ошибкой so.CleanUp. Всегда вводите try раздел после того, как вы приобрели ресурс, который этот finally раздел завершает.

Блок try-finally после so инициализации существует только для защиты SomeObject экземпляра, чтобы убедиться, что он будет очищен независимо от того, что еще произойдет. Если есть другие вещи, которые необходимо запустить, но они не связаны с тем, был ли SomeObject экземпляру выделено свойство, тогда они должны быть помещены в другой try-finally блок, вероятно, тот, который оборачивает тот, который я показал.

Требование назначать переменные вручную перед использованием не приводит к реальным проблемам. Это приводит лишь к незначительным неприятностям, но ваш код от этого станет лучше. У вас будут переменные с более ограниченной областью действия и try-finally блоки, которые не пытаются защитить слишком сильно.

Если бы локальные переменные имели значения по умолчанию, то so в первом примере они были бы null. На самом деле это ничего бы не решило. Вместо того, чтобы получать ошибку времени компиляции в finally блоке, у вас будет NullPointerException скрывающийся там, который может скрывать любое другое исключение, которое может возникнуть в разделе кода "Поработайте здесь". (Или исключения в finally разделах автоматически привязываются к предыдущему исключению? Я не помню. Даже в этом случае у вас будет дополнительное исключение вместо реального.)

Ответ 3

Более того, в приведенном ниже примере внутри конструкции SomeObject могло возникнуть исключение, и в этом случае переменная 'so' была бы равна null, а вызов CleanUp вызовет исключение NullPointerException

SomeObject so;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
so.CleanUp(); // Compiler error here
}

Что я обычно делаю, так это:

SomeObject so = null;
try {
// Do some work here ...
so = new SomeObject();
so.DoUsefulThings();
} finally {
if (so != null) {
so.CleanUp(); // safe
}
}
Ответ 4

Фактический ответ на ваш вопрос заключается в том, что переменные метода создаются простым добавлением числа к указателю стека. Их обнуление было бы дополнительным шагом. Что касается переменных класса, то они помещаются в инициализированную память в куче.

Почему бы не сделать дополнительный шаг? Сделаем шаг назад - Никто не упомянул, что "предупреждение" в этом случае - очень хорошая вещь.

Вы никогда не должны инициализировать свою переменную нулем на первом проходе (когда вы впервые ее кодируете). Либо присвоите им фактическое значение, либо не присваивайте его вообще, потому что в противном случае Java может сказать вам, когда вы действительно облажаетесь. Возьмите ответ Electric Monk в качестве отличного примера. В первом случае на самом деле удивительно полезно, что он сообщает вам, что если try() завершится неудачей из-за того, что конструктор SomeObject выдал исключение, то в конечном итоге вы получите NPE в finally . Если конструктор не может выдать исключение, его не должно быть в try.

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

Кроме того, не лучше ли явно указать "int size = 0", а не "int size", и заставить следующего программиста выяснить, что вы предполагаете, что оно равно нулю?

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

java