Была ли какая-то причина, по которой разработчики Java считали, что локальным переменным не следует давать значение по умолчанию? Серьезно, если переменным экземпляра можно присвоить значение по умолчанию, то почему мы не можем сделать то же самое для локальных переменных?
И это также приводит к проблемам, как описано в этом комментарии к сообщению в блоге :
Это правило больше всего разочаровывает при попытке закрыть ресурс в блоке finally. Если я создаю экземпляр ресурса внутри try, но пытаюсь закрыть его в finally, я получаю эту ошибку. Если я перемещаю экземпляр за пределы попытки, я получаю еще одну ошибку, в которой говорится, что он должен быть в пределах попытки.
Очень неприятно.
java
variables
initialization
Шивасубраманиан А
источник
источник
Ответы:
Локальные переменные объявляются в основном для некоторых вычислений. Таким образом, решение программиста установить значение переменной, и оно не должно принимать значение по умолчанию. Если программист по ошибке не инициализировал локальную переменную и принял значение по умолчанию, то на выходе может быть какое-то неожиданное значение. Таким образом, в случае локальных переменных компилятор попросит программиста выполнить инициализацию некоторым значением, прежде чем он получит доступ к переменной, чтобы избежать использования неопределенных значений.
источник
"Проблема", на которую вы ссылаетесь, похоже, описывает эту ситуацию:
Жалоба комментатора заключается в том, что компилятор блокирует строку в
finally
разделе, утверждая, что онаso
может быть неинициализированной. Затем в комментарии упоминается другой способ написания кода, возможно, примерно так:Комментатор недоволен этим решением, потому что компилятор затем говорит, что код «должен быть в пределах попытки». Я предполагаю, что это означает, что часть кода может вызвать исключение, которое больше не обрабатывается. Я не уверен. Ни одна из версий моего кода не обрабатывает исключения, поэтому все, что связано с исключениями в первой версии, должно работать так же во второй.
В любом случае, эта вторая версия кода - правильный способ ее написания. В первой версии сообщение об ошибке компилятора было правильным. Возможно,
so
переменная не инициализирована. В частности, еслиSomeObject
конструктор не работает,so
он не будет инициализирован, и поэтому попытка вызова будет ошибкойso.CleanUp
. Всегда входите вtry
раздел после того, как вы приобрели ресурс,finally
завершающий раздел.Блок
try
-finally
послеso
инициализации нужен только для защитыSomeObject
экземпляра, чтобы убедиться, что он будет очищен, что бы еще ни случилось. Если есть другие вещи, которые необходимо запустить, но они не связаны с тем, было лиSomeObject
свойство выделено для экземпляра, тогда они должны перейти в другойtry
-finally
блок, возможно, тот, который является оболочкой того, который я показал.Требование ручного присвоения переменных перед использованием не приводит к реальным проблемам. Это приведет лишь к незначительным неприятностям, но ваш код будет лучше для этого. У вас будут переменные с более ограниченной областью действия и
try
-finally
блоки, которые не пытаются слишком сильно защищать.Если бы локальные переменные имели значения по умолчанию, то
so
в первом примере было быnull
. На самом деле это ничего бы не решило. Вместо того, чтобы получать ошибку времени компиляции вfinally
блоке, у вас будетNullPointerException
скрываться там, что может скрыть любое другое исключение, которое могло произойти в разделе кода «Сделайте кое-что здесь». (Или исключения вfinally
разделах автоматически связываются с предыдущим исключением? Я не помню. Даже в этом случае у вас было бы дополнительное исключение на пути к реальному.)источник
Более того, в приведенном ниже примере внутри конструкции SomeObject могло возникнуть исключение, и в этом случае переменная so будет иметь значение null, а вызов CleanUp вызовет исключение NullPointerException.
Я обычно делаю вот что:
источник
Обратите внимание, что последние переменные экземпляра / члена не инициализируются по умолчанию. Потому что они окончательные и впоследствии не могут быть изменены в программе. Это причина того, что Java не дает им значения по умолчанию и заставляет программиста инициализировать его.
С другой стороны, переменные-члены, не являющиеся конечными, могут быть изменены позже. Следовательно, компилятор не позволяет им оставаться неинициализированными именно потому, что они могут быть изменены позже. Что касается локальных переменных, объем локальных переменных намного уже. Компилятор знает, когда к нему привыкаешь. Следовательно, принуждение программиста к инициализации переменной имеет смысл.
источник
Фактический ответ на ваш вопрос заключается в том, что переменные метода создаются путем простого добавления числа в указатель стека. Обнулить их было бы лишним шагом. Для переменных класса они помещаются в инициализированную память в куче.
Почему бы не сделать дополнительный шаг? Сделайте шаг назад - никто не упомянул, что «предупреждение» в этом случае - очень хорошая вещь.
Вы никогда не должны инициализировать свою переменную нулем или нулем на первом проходе (когда вы ее впервые кодируете). Либо присвойте ему фактическое значение, либо не присваивайте его вообще, потому что в противном случае Java может сказать вам, когда вы действительно облажаетесь. Возьмите ответ Electric Monk в качестве отличного примера. В первом случае на самом деле удивительно полезно, когда он сообщает вам, что если try () завершится неудачно из-за того, что конструктор SomeObject сгенерировал исключение, вы получите NPE в файле finally. Если конструктор не может создать исключение, его не должно быть в попытке.
Это предупреждение - потрясающая программа проверки многопутевого плохого программиста, которая спасла меня от глупых действий, поскольку проверяет каждый путь и гарантирует, что если вы использовали переменную в каком-либо пути, вам пришлось бы инициализировать ее на каждом пути, ведущем к ней. . Теперь я никогда не инициализирую переменные явно, пока не решу, что это правильно.
Вдобавок ко всему, не лучше ли явно сказать «int size = 0», а не «int size», и заставить следующего программиста понять, что вы хотите, чтобы он был равен нулю?
С другой стороны, я не могу придумать единственной веской причины, чтобы компилятор инициализировал все неинициализированные переменные до 0.
источник
Я думаю, что основная цель заключалась в сохранении сходства с C / C ++. Однако компилятор обнаруживает и предупреждает вас об использовании неинициализированных переменных, что сводит проблему к минимуму. С точки зрения производительности немного быстрее разрешить вам объявлять неинициализированные переменные, поскольку компилятору не нужно будет писать оператор присваивания, даже если вы перезапишете значение переменной в следующем операторе.
источник
(Может показаться странным публиковать новый ответ так долго после вопроса, но появился дубликат .)
Для меня причина сводится к следующему: назначение локальных переменных отличается от назначения переменных экземпляра. Локальные переменные используются как часть вычислений; переменные экземпляра должны содержать состояние. Если вы используете локальную переменную без присвоения ей значения, это почти наверняка логическая ошибка.
Тем не менее, я мог полностью отказаться от требования, чтобы переменные экземпляра всегда явно инициализировались; ошибка может возникнуть в любом конструкторе, в котором результат допускает неинициализированную переменную экземпляра (например, не инициализированную при объявлении и не в конструкторе). Но это не решение Gosling et. др., сняли в начале 90-х, так вот и мы. (И я не говорю, что они сделали неправильный звонок.)
Однако я не мог отказаться от использования локальных переменных по умолчанию. Да, мы не должны полагаться на компиляторы, чтобы перепроверить нашу логику, и никто этого не делает, но это все равно удобно, когда компилятор ее обнаруживает. :-)
источник
Более эффективно не инициализировать переменные, а в случае локальных переменных это безопасно, потому что инициализацию может отслеживать компилятор.
В случаях, когда вам нужно инициализировать переменную, вы всегда можете сделать это самостоятельно, так что это не проблема.
источник
Идея локальных переменных заключается в том, что они существуют только в той ограниченной области, для которой они необходимы. Таким образом, не должно быть причин для неуверенности в отношении значения или, по крайней мере, того, откуда оно взято. Я мог представить себе множество ошибок, возникающих из-за значения по умолчанию для локальных переменных.
Например, рассмотрим следующий простой код ... ( NB давайте предположим, в демонстрационных целях, что локальным переменным присваивается значение по умолчанию, как указано, если не инициализировано явно )
Когда все сказано и сделано, предполагая, что компилятор присвоил letterGrade значение по умолчанию '\ 0' , этот код в том виде, в каком он написан, будет работать правильно. Однако что, если мы забыли инструкцию else?
Тестовый запуск нашего кода может привести к следующему
Такой результат, хотя и ожидался, определенно не входил в намерения программиста. Действительно, вероятно, в подавляющем большинстве случаев (или, по крайней мере, в значительном их числе) значение по умолчанию не будет желаемым , поэтому в подавляющем большинстве случаев значение по умолчанию приведет к ошибке. Имеет смысл заставить кодировщик назначать начальное значение локальной переменной перед ее использованием, поскольку затруднения при отладке, вызванные забыванием
= 1
in,for(int i = 1; i < 10; i++)
намного перевешивают удобство отсутствия необходимости включать= 0
infor(int i; i < 10; i++)
.Это правда, что блоки try-catch-finally могут стать немного беспорядочными (но на самом деле это не уловка-22, как кажется в цитате), когда, например, объект выдает проверенное исключение в своем конструкторе, но для одного по какой-либо причине, что-то должно быть сделано с этим объектом в конце блока в finally. Прекрасный пример этого - работа с ресурсами, которые необходимо закрыть.
Один из способов справиться с этим в прошлом мог быть таким ...
Однако, начиная с Java 7, этот блок finally больше не нужен с использованием try-with-resources, например.
Тем не менее (как следует из названия) это работает только с ресурсами.
И хотя первый пример немного неприятен, это, возможно, больше говорит о способе реализации try-catch-finally или этих классов, чем о локальных переменных и о том, как они реализованы.
Это правда, что поля инициализируются значением по умолчанию, но это немного другое. Когда вы говорите, например,
int[] arr = new int[10];
как только вы инициализировали этот массив, объект существует в памяти в заданном месте. Предположим на мгновение, что нет значений по умолчанию, но вместо этого начальное значение - это любая последовательность единиц и нулей, которая в данный момент находится в этой ячейке памяти. В ряде случаев это может привести к недетерминированному поведению.Предположим, у нас есть ...
Вполне возможно, что это
Same.
может отображаться в одном прогоне иNot same.
может отображаться в другом. Проблема может стать еще более серьезной, если вы начнете говорить о ссылочных переменных.Согласно определению, каждый элемент s должен указывать на строку (или иметь значение NULL). Однако, если начальным значением является любая последовательность нулей и единиц, встречающаяся в этой ячейке памяти, не только нет гарантии, что вы будете каждый раз получать одни и те же результаты, но также нет гарантии, что объект s [0] указывает to (при условии, что он указывает на что-нибудь значимое) даже является String (возможно, это Rabbit,: p )! Это отсутствие заботы о типе противоречит почти всему, что делает Java Java. Таким образом, хотя значения по умолчанию для локальных переменных можно рассматривать в лучшем случае как необязательные, наличие значений по умолчанию для переменных экземпляра ближе к необходимости .
источник
Если я не ошибаюсь, другой причиной может быть
Предоставление значения по умолчанию для переменных-членов является частью загрузки класса
Загрузка класса - это вещь времени выполнения в java, это означает, что когда вы создаете объект, тогда класс загружается с загрузкой класса, только переменные-члены инициализируются значением по умолчанию JVM не требует времени, чтобы присвоить значение по умолчанию вашим локальным переменным, потому что некоторые методы никогда не будут вызывается, потому что вызов метода может быть условным, так зачем тратить время на то, чтобы присвоить им значение по умолчанию и снизить производительность, если эти значения по умолчанию никогда не будут использоваться.
источник
Eclipse даже выдает предупреждения о неинициализированных переменных, так что в любом случае это становится очевидным. Лично я думаю, что это хорошо, что это поведение по умолчанию, иначе ваше приложение может использовать неожиданные значения, и вместо того, чтобы компилятор выдает ошибку, он ничего не сделает (но, возможно, выдаст предупреждение), и тогда вы будете царапать ваша голова относительно того, почему некоторые вещи не совсем так, как должны.
источник
Локальные переменные хранятся в стеке, но переменные экземпляра хранятся в куче, поэтому есть некоторые шансы, что предыдущее значение в стеке будет прочитано вместо значения по умолчанию, как это происходит в куче. По этой причине jvm не позволяет использовать локальную переменную без ее инициализации.
источник
Переменная экземпляра будет иметь значения по умолчанию, но локальные переменные не могут иметь значений по умолчанию. Поскольку локальные переменные в основном находятся в методах / поведении, его основная цель - выполнять некоторые операции или вычисления. Таким образом, не рекомендуется устанавливать значения по умолчанию для локальных переменных. В противном случае очень сложно и долго проверять причины неожиданных ответов.
источник
Ответ заключается в том, что переменные экземпляра могут быть инициализированы в конструкторе класса или любом методе класса, но в случае локальных переменных, как только вы определили что-либо в методе, которое навсегда останется в классе.
источник
Я мог придумать следующие 2 причины
источник