有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java字符串s1==字符串s2(true),但FieldOffset不同

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

static String s1 = "a";
static String s2 = "a";
System.out.println(s1 == s2);  

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

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

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

import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class SomeClass {
    static String s1 = "a";
    static String s2 = "a";
    public static void main (String args[]) throws Exception {
        System.out.println(s1 == s2); //true

        Unsafe unsafe = getUnsafeInstance();
        Field s1Field = SomeClass.class.getDeclaredField("s1");
        System.out.println(unsafe.staticFieldOffset(s1Field)); //600

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

    }

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


共 (4) 个答案

  1. # 1 楼答案

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

    仔细阅读Unsafe javadoc可以看出:

    Report the location of a given field in the storage allocation of its class. Do not expect to perform any sort of arithmetic on this offset; it is just a cookie which is passed to the unsafe heap memory accessors.

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

  2. # 2 楼答案

    你没有错过任何东西。不安全的图书馆正在报告实际发生的情况

    字节码:

    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?。使用前请务必阅读注意事项,但我做了一个快速测试,它似乎有效

  3. # 3 楼答案

    Java代码中声明的字符串将自动插入

    因此,结果与手动调用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());
    

    输出:

    true

    false

    true

    true

  4. # 4 楼答案

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