11

前几天我反编译了一些Java代码,发现了这个:

String s1 = "something";
String s2 = "something_else";

if (s1 == s2) {
// Path 1
} else {
// Path 2
}

显然使用 '==' 来测试字符串是否相等是不好的

但我想知道——这段代码已经被编译和反编译了。如果所有字符串都已在编译时定义并已实习并且代码已编译 - s1.equals(s2) 是否有可能已优化为“s1 == s2”?

4

3 回答 3

8

我对此表示高度怀疑。通常,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        

因此,我强烈怀疑这==是原始源代码的一部分。

于 2013-04-11T20:54:31.603 回答
1

不,看起来 Java 并没有对此进行优化(默认情况下)。

我只是对这两种解决方案进行了基准测试。如果它未优化,我们预计会s1.equals(s2)s1 == s2. 这正是我们所看到的。如果它被优化,那么s1.equals(s2)将花费与s1==s2. 但是,它们需要不同的时间(大约 50,000 纳秒)。这不是对这个编译的直接测量,但它是一个合理的推论。

这不会被优化的原因==是因为等于运算符,对于对象,将比较对象内存地址,而不是对象本身的内容。所以,如果你改变s1了,那么,如果编译器优化了这个,你也会改变s2.

但是,这可能会破坏代码,因此编译器不会这样做。它将留下和的内存s1地址s2

于 2013-04-11T20:50:55.153 回答
0

主要规则是,如果编译器可以从它在单个类中的源代码中扣除确切的值。因为它只使用最小的编译单元 - 类进行所有优化。如果我写一个代码

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实现以及为什么需要这种“奇怪”的方式。

于 2013-04-11T21:09:42.423 回答