前几天我反编译了一些Java代码,发现了这个:
String s1 = "something";
String s2 = "something_else";
if (s1 == s2) {
// Path 1
} else {
// Path 2
}
显然使用 '==' 来测试字符串是否相等是不好的
但我想知道——这段代码已经被编译和反编译了。如果所有字符串都已在编译时定义并已实习并且代码已编译 - s1.equals(s2) 是否有可能已优化为“s1 == s2”?
前几天我反编译了一些Java代码,发现了这个:
String s1 = "something";
String s2 = "something_else";
if (s1 == s2) {
// Path 1
} else {
// Path 2
}
显然使用 '==' 来测试字符串是否相等是不好的
但我想知道——这段代码已经被编译和反编译了。如果所有字符串都已在编译时定义并已实习并且代码已编译 - s1.equals(s2) 是否有可能已优化为“s1 == s2”?
我对此表示高度怀疑。通常,Java 编译器通过字节码优化做的很少,将优化留给 JIT 阶段。
我已经对此进行了一些实验,并且我的编译器对以下内容没有做任何有趣的事情:
public class Clazz {
public static void main(String args[]) {
final String s1 = "something";
final String s2 = "something_else";
if (s1.equals(s2)) {
System.out.println("yes");
} else {
System.out.println("no");
}
}
}
这可能是最容易优化的情况。但是,字节码是:
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String something
2: astore_1
3: ldc #18 // String something_else
5: astore_2
6: ldc #16 // String something
8: ldc #18 // String something_else
10: invokevirtual #20 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
13: ifeq 27
16: getstatic #26 // Field java/lang/System.out:Ljava/io/PrintStream;
19: ldc #32 // String yes
21: invokevirtual #34 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
24: goto 35
27: getstatic #26 // Field java/lang/System.out:Ljava/io/PrintStream;
30: ldc #40 // String no
32: invokevirtual #34 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: return
因此,我强烈怀疑这==
是原始源代码的一部分。
不,看起来 Java 并没有对此进行优化(默认情况下)。
我只是对这两种解决方案进行了基准测试。如果它未优化,我们预计会s1.equals(s2)
比s1 == s2
. 这正是我们所看到的。如果它被优化,那么s1.equals(s2)
将花费与s1==s2
. 但是,它们需要不同的时间(大约 50,000 纳秒)。这不是对这个编译的直接测量,但它是一个合理的推论。
这不会被优化的原因==
是因为等于运算符,对于对象,将比较对象内存地址,而不是对象本身的内容。所以,如果你改变s1
了,那么,如果编译器优化了这个,你也会改变s2
.
但是,这可能会破坏代码,因此编译器不会这样做。它将留下和的内存s1
地址s2
。
主要规则是,如果编译器可以从它在单个类中的源代码中扣除确切的值。因为它只使用最小的编译单元 - 类进行所有优化。如果我写一个代码
public class Test
{
private static final String i = "1";
public static void main(String[] args)
{
if(i == "2")
System.out.println("hello");
System.out.println("world");
}
}
编译器会看到与此类中的语句相关的所有代码并优化 if 条件。反编译后代码如下
public class Test
{
private static final String i = "1";
public static void main(String[] paramArrayOfString)
{
System.out.println("world");
}
}
(我用过jd-gui)
但是,如果您替换==
为.equals
,编译器无法假设该方法是如何.equals
工作的。因为,在Test
类编译之后,你可以破解你的 JDK 并放置另一个版本的类,java.lang.String
它返回true
."1".equals("2")
所以,考虑到编译器可以做的优化,首先要考虑如果以后可以重新编译任何类,编译器会如何表现。
作为另一个示例,您可以看到如何enum
实现以及为什么需要这种“奇怪”的方式。