Kum*_*kit 39 java optimization loops
我刚刚遇到了我的类的反编译类文件:
我的课
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
Run Code Online (Sandbox Code Playgroud)
该while环路已更改为一个for在类文件中循环:
反编译MyClass
for (String[] colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
Run Code Online (Sandbox Code Playgroud)
为什么这个循环被改为for?我认为它可能是编译器进行代码优化的另一种方式,我可能是错的.我只是想知道它是否,for循环在while循环或其他循环中提供了什么优势?
这种代码优化的类别是什么?
dge*_*ert 45
在这种情况下,改变while()到for()是不是优化.根本无法从字节码中知道在源代码中使用了哪一个.
有很多情况:
while(x)
Run Code Online (Sandbox Code Playgroud)
是相同的:
for(;x;)
Run Code Online (Sandbox Code Playgroud)
假设我们有三个类似的java应用程序 - 一个带while()语句,两个带对应for().首先for()是停止标准,只是在标准中while(),第二个for()也是迭代器声明和增量.
申请#1 - 来源
public class While{
public static void main(String args[]) {
int i = 0;
while(i<5){
System.out.println(i);
i++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
申请#2 - 来源
public class For{
public static void main(String args[]) {
int i = 0;
for(; i<5 ;){
System.out.println(i);
i++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
申请#3 - 来源
public class For2{
public static void main(String args[]) {
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们编译所有这些,我们得到:
申请#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
}
Run Code Online (Sandbox Code Playgroud)
申请#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
}
Run Code Online (Sandbox Code Playgroud)
申请#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
}
Run Code Online (Sandbox Code Playgroud)
所以你可以看到,没有任何关联for和while使用.
Mar*_*o13 15
正如其他人已经指出的那样:反编译器(通常)无法区分导致相同字节代码的不同源代码.
不幸的是,您没有提供该方法的完整代码.因此,下面包含一些关于此循环在方法中出现的位置和方式的猜测(这些猜测在某种程度上可能会扭曲结果).
但是,让我们来看看这里的一些往返.请考虑以下类,其中包含您发布的两个版本代码的方法:
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 {
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
用它编译
javac DecompileExample.java -g:none
Run Code Online (Sandbox Code Playgroud)
将创建相应的类文件.(注意:该-g:none参数将导致编译器省略所有调试信息.反编译器可能会使用调试信息来重建原始代码的更多逐字版本,特别是包括原始变量名称)
现在查看两种方法的字节码,用
javap -c DecompileExample.class
Run Code Online (Sandbox Code Playgroud)
将产生以下结果:
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
Run Code Online (Sandbox Code Playgroud)
和
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
}
Run Code Online (Sandbox Code Playgroud)
(有是一个小的区别:String[] colArr = null被转换成一个
aconst null
astore_3
Run Code Online (Sandbox Code Playgroud)
在第二个版本的开头.但这是与您在问题中省略的部分代码相关的方面之一.
你没有提到你正在使用哪一个,但是来自http://jd.benow.ca/的JD-GUI反编译器将其反编译为以下内容:
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++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
你可以看到两种情况下的代码是相同的(至少关于循环 - 还有一个是关于为了编译它而必须引入的"虚拟变量"的区别,但这与问题无关,可以这么说).
的TL;博士信息是明确的:
可以将不同的源代码编译成相同的字节代码.因此,相同的字节代码可以被反编译成不同的源代码.但每个反编译器都必须满足一个版本的源代码.
(旁注:我有点惊讶地看到,在没有编译时-g:none(也就是说,当保留调试信息时),JD-GUI甚至以某种方式设法重建第一个使用while-loop而第二个使用a for-loop.但总的来说,当省略调试信息时,这根本就不再可能了).
小智 6
这基本上是因为字节码的性质.Java字节码就像汇编语言一样,所以没有for和while循环这样的东西,只有跳转指令:goto.因此,while和for循环之间可能没有区别,两者都可以编译成类似的代码,而反编译器只是猜测.
两个for环和while环的代码段可被翻译成类似的机器代码.之后,当反编译时,解编译器必须选择其中一个two possible场景.
我想这就是这里发生的事情.
只是:
compile(A) -> C
compile(B) -> C
Run Code Online (Sandbox Code Playgroud)
所以,当你被给予时C,那么应该有一个猜测选择A或B