2016-08-23 5 views
14

Рассмотрим эти два метода:Как декомпиляторы Java распознают цикл for из цикла while?

public static void forLoop(int start, int limit) { 
    for (int i = start; i < limit; i++) { 

    } 
} 

public static void whileLoop(int start, int limit) { 
    int i = start; 
    while (i < limit) { 
     i++; 
    } 
} 

При компиляции, они производят байткод (это подробный вывод javap):

public static void forLoop(int, int); 
    descriptor: (II)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=3, args_size=2 
     0: iload_0 
     1: istore_2 
     2: iload_2 
     3: iload_1 
     4: if_icmpge  13 
     7: iinc   2, 1 
     10: goto   2 
     13: return 
     LineNumberTable: 
     line 6: 0 
     line 9: 13 
     LocalVariableTable: 
     Start Length Slot Name Signature 
      2  11  2  i I 
      0  14  0 start I 
      0  14  1 limit I 

    public static void whileLoop(int, int); 
    descriptor: (II)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=3, args_size=2 
     0: iload_0 
     1: istore_2 
     2: iload_2 
     3: iload_1 
     4: if_icmpge  13 
     7: iinc   2, 1 
     10: goto   2 
     13: return 
     LineNumberTable: 
     line 12: 0 
     line 13: 2 
     line 14: 7 
     line 16: 13 
     LocalVariableTable: 
     Start Length Slot Name Signature 
      0  14  0 start I 
      0  14  1 limit I 
      2  12  2  i I 

Как вы можете видеть, раздел кода для обоих из них методы точно то же самое. Однако, когда я декомпилировать этот класс, используя JD, он правильно производит:

public static void forLoop(int start, int limit) { 
    for (int i = start; i < limit; i++) {} 
} 

public static void whileLoop(int start, int limit) 
{ 
    int i = start; 
    while (i < limit) { 
    i++; 
    } 
} 

Как это было в состоянии сделать это? Байт-код этих методов точно такой же! Несмотря на то, что атрибуты были разными для каждого метода, я с неохотой полагаю, что это причина, поскольку это не обязательные атрибуты для атрибута Code метода, который должен содержать (за section 4.7 of The Java Language Specification, Java SE 8 Edition).

ответ

5

Номера строк и локальная переменная.

for цикл:

LineNumberTable: 
    line 6: 0 
    line 9: 13 
    LocalVariableTable: 
    Start Length Slot Name Signature 
     2  11  2  i I 
     0  14  0 start I 
     0  14  1 limit I 

while цикл:

LineNumberTable: 
     line 12: 0 
     line 13: 2 
     line 14: 7 
     line 16: 13 
    LocalVariableTable: 
    Start Length Slot Name Signature 
     0  14  0 start I 
     0  14  1 limit I 
     2  12  2  i I 

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

+2

Почему скомпилированный код должен знать локальную область переменных? – shmosel

+0

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

+0

Я предполагаю, что он помогает более эффективно использовать локальный список переменных. Например, если локальная переменная, хранящаяся в индексе 2, выходит из области видимости, компилятор знает, что он может повторно использовать индекс 2 для другой локальной переменной. Если индексы не были повторно использованы, «максимальные локали» для каждого метода были бы эквивалентны количеству локальных переменных, объявленных в методе, * не * максимальное количество локальных переменных, видимых в определенной области внутри метода. –