I just encountered this decompiled class file of my class:
MyClass
while ((line = reader.readLine()) != null) { System.out.println("line: " + line); if (i == 0) { colArr = line.split(Pattern.quote("|")); } else { i++; } }
The while
loop has been changed to a for
loop in the class file:
Decompiled MyClass
for (String[] colArr = null; (line = reader.readLine()) != null; ++i) { System.out.println("line: " + line); if (i == 0) { colArr = line.split(Pattern.quote("|")); } else { } }
Why has this loop been changed to a for
? I think it might be another way of code optimization by the compiler, I could be wrong. I just wanted to know if it is, what advantages does a for
loop provide over a while
loop or other loop?
What is the category of such code optimizations?
No it doesn't. The first statement in the for loop is executed only once. Once i is set, changing the value of the variable that was used to set i does not effect i . You are allowed to change the value of i explicitly within the for loop body though.
This is a silly example, but it's common for infinite loops to accidentally occur. Most of the times, it's because the variables used in the condition are not being updated correctly, or because the looping condition is in error.
'For' loops are considered obsolete, hence we should avoid using them. In single threaded languages like JavaScript, the 'for' loop acts as a thread blocker, which hinders the scalability of a system. Debugging with 'for' loops can also be difficult.
A "For" Loop is used to repeat a specific block of code a known number of times. For example, if we want to check the grade of every student in the class, we loop from 1 to that number. When the number of times is not known before hand, we use a "While" loop.
In this situation changing while()
to for()
is not an optimization. There is simply no way to know from bytecode which one was used in a source code.
There are many situations when:
while(x)
is the same as:
for(;x;)
Suppose we have a three similar java applications - one with while()
statement, and two with corresponting for()
. First for()
with stopping criterion only like in the standard while()
, and second for()
also with iterator declaration and incrementation.
APPLICATION #1 - SOURCE
public class While{ public static void main(String args[]) { int i = 0; while(i<5){ System.out.println(i); i++; } } }
APPLICATION #2 - SOURCE
public class For{ public static void main(String args[]) { int i = 0; for(; i<5 ;){ System.out.println(i); i++; } } }
APPLICATION #3 - SOURCE
public class For2{ public static void main(String args[]) { for(int i=0;i<5;i++){ System.out.println(i); } } }
If we compile all of them we have got:
APPLICATION #1 - BYTECODE
public class While { public While(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: iconst_5 4: if_icmpge 20 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 14: iinc 1, 1 17: goto 2 20: return }
APPLICATION #2 - BYTECODE
public class For { public For(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: iconst_5 4: if_icmpge 20 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 14: iinc 1, 1 17: goto 2 20: return }
APPLICATION #3 - BYTECODE
public class For2 extends java.lang.Object{ public For2(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: iconst_5 4: if_icmpge 20 7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 14: iinc 1, 1 17: goto 2 20: return }
So you can see, there is no difference associated with for
and while
usage.
As others have already pointed out: The decompiler (usually) cannot distinguish between different source codes that result in the same byte code.
Unfortunately, you did not provide the full code of the method. So the following contains some guesses about where and how this loop appears inside a method (and these guesses might, to some extent, distort the result).
But let's have a look at some roundtrips here. Consider the following class, containing methods with both versions of the code that you posted:
import java.io.BufferedReader; import java.io.IOException; import java.util.regex.Pattern; public class DecompileExample { public static void methodA(BufferedReader reader) throws IOException { String line = null; int i = 0; while ((line = reader.readLine()) != null) { System.out.println("line: " + line); if (i == 0) { String[] colArr = line.split(Pattern.quote("|")); } else { i++; } } } public static void methodB(BufferedReader reader) throws IOException { String line = null; int i = 0; for (String[] colArr = null; (line = reader.readLine()) != null; ++i) { System.out.println("line: " + line); if (i == 0) { colArr = line.split(Pattern.quote("|")); } else { } } } }
Compiling it with
javac DecompileExample.java -g:none
will create the corresponding class file. (Note: The -g:none
parameter will cause the compiler to omit all debug information. The debug information might otherwise be used by the decompiler to reconstruct a more verbatim version of the original code, particularly, including the original variable names)
Now looking at the byte code of both methods, with
javap -c DecompileExample.class
will yield the following:
public static void methodA(java.io.BufferedReader) throws java.io.IOException; Code: 0: aconst_null 1: astore_1 2: iconst_0 3: istore_2 4: aload_0 5: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String; 8: dup 9: astore_1 10: ifnull 61 13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 16: new #4 // class java/lang/StringBuilder 19: dup 20: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 23: ldc #6 // String line: 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: aload_1 29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 38: iload_2 39: ifne 55 42: aload_1 43: ldc #10 // String | 45: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String; 48: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 51: astore_3 52: goto 4 55: iinc 2, 1 58: goto 4 61: return
and
public static void methodB(java.io.BufferedReader) throws java.io.IOException; Code: 0: aconst_null 1: astore_1 2: iconst_0 3: istore_2 4: aconst_null 5: astore_3 6: aload_0 7: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String; 10: dup 11: astore_1 12: ifnull 60 15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 18: new #4 // class java/lang/StringBuilder 21: dup 22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 25: ldc #6 // String line: 27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: aload_1 31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 37: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: iload_2 41: ifne 54 44: aload_1 45: ldc #10 // String | 47: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String; 50: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 53: astore_3 54: iinc 2, 1 57: goto 6 60: return }
(There is a small difference: The String[] colArr = null
is translated into an
aconst null astore_3
at the beginning of the second version. But this is one of the aspects that is related to parts of the code that you have omitted in the question).
You did not mention which one you are using, but the JD-GUI decompiler from http://jd.benow.ca/ decompiles this into the following:
import java.io.BufferedReader; import java.io.IOException; import java.io.PrintStream; import java.util.regex.Pattern; public class DecompileExample { public static void methodA(BufferedReader paramBufferedReader) throws IOException { String str = null; int i = 0; while ((str = paramBufferedReader.readLine()) != null) { System.out.println("line: " + str); if (i == 0) { String[] arrayOfString = str.split(Pattern.quote("|")); } else { i++; } } } public static void methodB(BufferedReader paramBufferedReader) throws IOException { String str = null; int i = 0; String[] arrayOfString = null; while ((str = paramBufferedReader.readLine()) != null) { System.out.println("line: " + str); if (i == 0) { arrayOfString = str.split(Pattern.quote("|")); } i++; } } }
You can see that the code is the same for both cases (at least regarding the loop - there one more is a difference regarding the "dummy variables" that I had to introduce in order to compile it, but this is unrelated to the question, so to speak).
The tl;dr message is clear:
Different source codes can be compiled into the same byte code. Consequently, the same byte code can be decompiled into different source codes. But every decompiler has to settle for one version of the source code.
(A side note: I was a bit surprised to see that when compiling without -g:none
(that is, when the debug information is retained), JD-GUI even somehow manages to reconstruct that the first one used a while
-loop and the second one used a for
-loop. But in general, and when the debug information is omitted, this is simply no longer possible).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With