Ограниченная сфера - лучший
Используйте ваш второй вариант:
for ( ... ) {
String s = ...;
}
Область действия не влияет на производительность
Если вы дизассемблируете код, скомпилированный из каждого (с помощью инструмента JDK javap
), вы увидите, что цикл компилируется с одинаковыми инструкциями JVM в обоих случаях. Также обратите внимание, что Брайана Р. Бонди "Вариант № 3" идентичен Варианту № 1. При использовании более узкой области видимости ничего не добавляется и не удаляется из стека, и в обоих случаях в стеке используются одни и те же данные.
Избегать преждевременной инициализации
Единственное различие между этими двумя случаями состоит в том, что в первом примере переменная s
инициализируется без необходимости. Это отдельная проблема от расположения объявления переменной. Это добавляет две потраченные впустую инструкции (загрузить строковую константу и сохранить ее в слоте стекового фрейма). Хороший инструмент статического анализа предупредит вас, что вы никогда не читаете значение, которое вы присваиваете s
, а хороший JIT-компилятор, вероятно, исключит его во время выполнения.
Вы можете исправить это, просто используя пустое объявление (т.е. String s;
), но это считается плохой практикой и имеет другой побочный эффект, который обсуждается ниже.
Часто фиктивное значение типа null
присваивается переменной просто для того, чтобы скрыть ошибку компилятора, что переменная читается без инициализации. Эта ошибка может быть воспринята как подсказка о том, что область действия переменной слишком велика и что она объявляется до того, как она потребуется для получения допустимого значения. Пустые объявления заставляют вас рассмотреть каждый путь кода; не игнорируйте это ценное предупреждение, присваивая фиктивное значение.
Сохранение слотов стека
Как уже упоминалось, в то время как инструкции JVM одинаковы в обоих случаях, существует тонкий побочный эффект, который позволяет на уровне JVM использовать максимально ограниченную возможную область действия. Это видно в «таблице локальных переменных» для метода. Подумайте, что произойдет, если у вас несколько циклов с переменными, объявленными в неоправданно большом объеме:
void x(String[] strings, Integer[] integers) {
String s;
for (int i = 0; i < strings.length; ++i) {
s = strings[0];
...
}
Integer n;
for (int i = 0; i < integers.length; ++i) {
n = integers[i];
...
}
}
Переменные s
и n
могут быть объявлены внутри их соответствующих циклов, но так как они не являются, компилятор использует два «слота» в кадре стека. Если они были объявлены внутри цикла, компилятор может повторно использовать один и тот же слот, уменьшая размер стека.
Что на самом деле имеет значение
Однако большинство из этих вопросов несущественны. Хороший JIT-компилятор увидит, что невозможно прочитать начальное значение, которое вы расточительно присваиваете, и оптимизировать его. Сохранение слота здесь или там не приведет к созданию или повреждению вашего приложения.
Важно сделать ваш код читабельным и простым в обслуживании, и в этом отношении использование ограниченной области явно лучше. Чем меньше область действия переменной, тем легче понять, как она используется и какое влияние окажут любые изменения в коде.