10

在我学习java时,我了解到比较2个字符串的正确方法是使用equals而不是“==”。这条线

静态字符串 s1 = "a";
静态字符串 s2 = "a";
System.out.println(s1 == s2);  

将输出 true 因为 jvm 似乎已经优化了此代码,以便它们实际上指向相同的地址。我试图用我在这里找到的一篇很棒的帖子来证明这一点

http://javapapers.com/core-java/address-of-a-java-object/

但地址似乎不一样。我错过了什么?

导入 sun.misc.Unsafe;
导入 java.lang.reflect.Field;
公共类 SomeClass {
    静态字符串 s1 = "a";
    静态字符串 s2 = "a";
    公共静态 void main (String args[]) 抛出异常 {
        System.out.println(s1 == s2); //真的

        不安全不安全 = getUnsafeInstance();
        字段 s1Field = SomeClass.class.getDeclaredField("s1");
        System.out.println(unsafe.staticFieldOffset(s1Field)); //600

        字段 s2Field = SomeClass.class.getDeclaredField("s2");
        System.out.println(unsafe.staticFieldOffset(s2Field)); //604

    }

    private static Unsafe getUnsafeInstance() 抛出 SecurityException,
        NoSuchFieldException,IllegalArgumentException,IllegalAccessException {
        字段 theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafeInstance.setAccessible(true);
        return (Unsafe) theUnsafeInstance.get(Unsafe.class);
    }
}

4

4 回答 4

11

我认为您对staticFieldOffset返回的内容感到困惑。它返回指向实例的指针的偏移量String,而不是String本身的地址。因为有两个字段,所以它们有不同的偏移量:即两个指针,它们恰好具有相同的值。

仔细阅读Unsafe javadoc可以看出:

报告给定字段在其类的存储分配中的位置。不要期望对这个偏移量执行任何类型的算术运算;它只是一个传递给不安全堆内存访问器的 cookie。

换句话说,如果您知道实际Class实例在内存中的位置,那么您可以将此方法返回的偏移量添加到该基地址,结果将是内存中的位置,您可以在其中找到指向String.

于 2013-05-31T14:58:37.270 回答
3

在上面的代码中,您不是比较字符串的地址,而是比较它们的“存储分配中给定字段的位置”,即持有对(相同)字符串的引用的变量的位置。

于 2013-05-31T14:58:12.167 回答
3

你没有错过任何东西。Unsafe 库正在报告实际发生的情况。

字节码:

static {};
  Code:
   0:   ldc #11; //String a
   2:   putstatic   #13; //Field s1:Ljava/lang/String;
   5:   ldc #11; //String a
   7:   putstatic   #15; //Field s2:Ljava/lang/String;
   10:  return

请注意,两个字符串都放在内存中的不同位置,13 和 15。

变量在内存中的存储位置(需要单独的地址)与是否将新对象放入堆中是有区别的。在这种情况下,它为两个变量分配了两个单独的地址,但它不需要创建一个新的字符串对象,因为它可以识别相同的字符串字面量。所以此时两个变量都引用了相同的字符串。

如果你想获取地址,你可以使用这个问题中的答案,How can I get the memory location of a object in java? . 确保您在使用前阅读了警告,但我做了一个快速测试,它似乎有效。

于 2013-05-31T14:55:45.163 回答
-4

在 Java 代码中声明的字符串会自动被 interned

因此结果与您手动调用String.intern()相同。

    String a = "aa";
    String b = new String(a);
    System.out.println("aa" == "aa");
    System.out.println(a == b);
    System.out.println(a.equals(b));
    System.out.println(a.intern() == b.intern());

输出:

真的

错误的

真的

真的

于 2013-05-31T15:17:19.673 回答