2013-05-26 3 views
4

Я знаю, что уже были заданы и уже ответили на очень похожие вопросы, я прочитал те, которые я смог найти и все еще не на 100% понятен.Объявление локальной переменной как конечной в цикле

Учитывая этот фрагмент кода:

public static void fooMethod { 

    while(<...>) { 
    .... 
    final int temp = <something>; 
    .... 
    } 
} 

Нет внутренних классов, больше ничего особенного или необычного. Кажется контр-интуитивным для меня.

Означает ли локальная переменная final в приведенном выше примере какой-либо цели?

Правильно ли я понимаю, что с или без final здесь компилятор будет производить точно такой же байт-код?

Я что-то упустил? Если это случай с RTFM, пожалуйста, укажите мне в правильном направлении.

Последующие меры Вопрос (если можно)

Что я получаю и/или потерять переписав, как это (при том понимании, что temp не должен быть примитивным)?

public static void fooMethod2 { 

    int temp; 
    while(<...>) { 
    .... 
    temp = <something>; 
    .... 
    } 
} 
+1

Предположительно он выполняет ту же задачу, что и в другом месте: при попытке изменить переменную компилятор останавливает вас. – Cairnarvon

+0

Эта переменная * очень * локальная (только для тела цикла). Он даже не упоминается нигде в одном цикле. Защищает ли это читаемость, запрещая повторное использование для другой цели? –

ответ

9

В нескольких словах:final ключевое слово, при использовании локальных переменных и параметров , не попадает в сгенерированный байт-код (файл .class) и, как ожидается, его использование не влияет на время выполнения. (Compile время, он мог бы сделать различию, хотя, проверьте ниже.)

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

Приведенные ниже тесты подтверждают эту информацию.



1: Если компилятор может сделать что-то из этого, используя final делает различие:

Посмотрите на этот фрагмент:

boolean zZ = true; 
while (zZ) { 
    int xX = 1001;   // <------------- xX 
    int yY = 1002;   // <------------- yY 
    zZ = (xX == yY); 
} 

Два int переменных, xX и yY. Первый раз объявили как final, и во второй раз забрали final от обоих. Вот генерируемые байткоды (печатные с javap -c):

И final:

 0: iconst_1    // pushes int 1 (true) onto the stack 
    1: istore_1    // stores the int on top of the stack into var zZ 
    2: goto   15 
    5: sipush  1001 // pushes 1001 onto the operand stack 
    8: istore_2    // stores on xX 
    9: sipush  1002 // pushes 1002 onto the operand stack 
    12: istore_3    // stores on yY 
    13: iconst_0    // pushes 0 (false): does not compare!! <--------- 
    14: istore_1    // stores on zZ 
    15: iload_1    // loads zZ 
    16: ifne   5  // goes to 5 if top int (zZ) is not 0 
    19: return   

Оба не- final:

// 0: to 12: all the same 
    13: iload_2    // pushes xX onto the stack 
    14: iload_3    // pushes yY onto the stack 
    15: if_icmpne  22  // here it compares xX and yY! <------------ 
    18: iconst_1  
    19: goto   23 
    22: iconst_0  
    23: istore_1  
    24: iload_1  
    25: ifne   5 
    28: return   

В приведенном выше случае, когда они final, компилятор знает, что они не равны и никогда их не сравнивают (false генерируется в байт-коде где угодно xX == yY).

Из этого мы можем заключить, байткод мудрые, компилятор делает может сделать некоторую оптимизацию генерируемого кода при использовании final. (Я не говорю, что они имеют смысл, но наверняка final не только стиль выбор здесь.)


2: Если компилятор не может ничего сделать вывод, используя final на местном Варс это просто дизайн выбор:

Теперь возьмите следующий код:

boolean zZ = true; 
int aA = 1001; 
int bB = 1002; 
while (zZ) { 
    final int xX = aA; // <------- took away the "final" here, didnt matter 
    final int yY = bB; // <------- took away the "final" here, didnt matter 
    zZ = (xX == yY); 
} 

В этом случае, даже при использовании final, компилятор annot рассказать компилятор-время, если xX и yY равны, верно?

Из-за этого, мы можем видеть: сгенерированный байткод точно такой же, когда мы создаем класс с или без final (такой же MD5!).

Хотя, в общем случае , some say и others disagree, что есть преимущества в производительности использования final, в локальных блоках, final, безусловно, только стиля выбор.


3: Локальные переменные внутри или снаружи петли - нет никакой разницы на всех:

Сформированный байткод для этого фрагмента ...

boolean zZ = true; 
int aA = 1001, bB = 1002; 
while (zZ) { 
    int xX = aA;      // <--- declaration is inside WHILE 
    int yY = bB; 
    zZ = (xX == yY); 
} 

... и сгенерированный байткод для этого фрагмента ...

boolean zZ = true; 
int aA = 1001, bB = 1002; 
int xX, yY;       // <--- declaration is outside WHILE 
while (zZ) { 
    xX = aA; 
    yY = bB; 
    zZ = (xX == yY); 
} 

... являются точно такими же (только номера строк изменились, конечно).

Другие тесты с использованием объектов (а не только примитивные типизированные переменные) показали одинаковое поведение.

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

Примечание: Все испытания проводились под JRE Oracle, версия 1.7.0_13.

+0

1) Неразумно делать выводы о способности Java оптимизировать код, просматривая файлы байт-кода. Серьезная работа по оптимизации выполняется компилятором JIT во время выполнения. 2) Фактически, JIT-компилятор отлично работает, если переменная не изменяется за пределами определенной точки. Маловероятно, что добавление «финала» поможет. –

+0

@StephenC Спасибо за ввод, я ценю его. Должен признаться, действительно неразумно делать выводы из файлов байт-кода. Но вы видите, я только пришел к выводу, что сгенерированный байт-код тот же (с или без 'final'). В тот момент, когда JIT берет его, он не может определить, есть ли там ключевое слово «final» или нет. Так что это не может изменить ситуацию. Как вы думаете? – acdcjunior

+1

Вы правы. Если бы были различия, они могли бы быть теоретически закодированы в других дескрипторах, но из моего краткого описания спецификации JVM они не ... –

4

final это ключевое слово для постоянных переменных. Объявление последнего не позволяет вам переназначить его позже внутри цикла.

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

, например:

while (...) 
{ 
    final int temp = ...; 

    temp = 5; // compiler error 
} 

Но если его не постоянна (конечная):

while (...) 
{ 
    int temp = ...; 

    temp = 5; // fine 
} 
+0

Итак, это как примечание к себе: «У меня нет намерений изменить эту переменную»? И не влияет ли на время выполнения? –

+0

@ PM77-1 Вот несколько полезных ответов на этот вопрос: http://stackoverflow.com/questions/4279420/does-use-of-final-keyword-in-java-improve-the-performance – Supericy

+0

Спасибо. Этого я еще не видел. –

1

Рассмотрим это с другой точки зрения укомплектовать: В функциональных языков программирования, это обычный случай, что почти все задания являются окончательными, и что классы неизменны. Это означает, что не конечные назначения и/или изменяемые классы представляют собой исключение .

Если ваш код был написан на Scala, в IntelliJ IDE появится подсказка «что это назначение может быть изменено как окончательное».

Я действительно ценю «финал», потому что, если вы прочтете свой код позже, вы увидите на первый взгляд, что это назначение никогда не меняет некоторые строки дальше. И если вы знаете, что экземпляр неизменен, это также поможет.

Кроме того, если вы используете «финалы» последовательно, то неконфиденциальные будут получать видимость, и эти переменные обычно являются наиболее важными для наблюдения.

Смежные вопросы