Java - 反编译时字符串等于

时间:2013-04-11 20:45:26

标签: java string optimization equality string-interning

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

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

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

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

但我想知道 - 这段代码已被编译和反编译。 如果在编译时已经定义了所有字符串并且已经编译并且代码已经编译了 - 是否可以将s1.equals(s2)优化为's1 == s2'? < / p>

3 个答案:

答案 0 :(得分: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        

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

答案 1 :(得分:1)

不,看起来Java似乎没有优化它(默认情况下)。

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

这不会优化为==的原因是因为对象的equals运算符将比较对象内存地址,而不是对象本身的内容。因此,如果您更改s1,那么,如果编译器对此进行了优化,那么您也将更改s2

然而,这可能会破坏代码,因此编译器不会这样做。它将保留s1s2的内存地址。

答案 2 :(得分: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")

因此,考虑优化编译器可以做什么,首先要考虑如果以后可以重新编译任何类,编译器的行为如何。

作为另一个例子,你可以看到how enum is implemented,为什么它需要这种“怪异”的方式。