在我学习java的过程中,我了解到比较2个字符串的正确方法是使用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 回答
你没有遗漏任何东西 . 不安全的库正在报告实际发生的情况 .
字节码:
请注意,两个字符串都放在内存中的不同位置,13和15 .
变量存储在内存中的位置存在差异,需要单独的地址,以及是否将新的Object放在堆上 . 在这种情况下,它为两个变量分配两个单独的地址,但它不需要创建新的String对象,因为它识别相同的String文字 . 因此,此时两个变量都引用相同的String .
如果你想获得地址,你可以使用这个问题中找到的答案How can I get the memory location of a object in java? . 请确保在使用前阅读警告,但我做了一个快速测试,它似乎工作 .
我觉得你对
staticFieldOffset
的回归感到困惑 . 它返回指向String
实例的指针的偏移量,而不是String
本身的地址 . 因为有两个字段,它们具有不同的偏移量:即两个指针,它们碰巧具有相同的值 .仔细阅读Unsafe javadoc表明:
换句话说,如果您知道实际的
Class
实例在内存中的位置,那么您可以将此方法返回的偏移量添加到该基址,结果将是内存中您可以找到指针值的位置String
.在上面的代码中,您不是比较字符串的地址,而是它们“存储分配中给定字段的位置”,即保持对(相同)字符串的引用的变量的位置 .
在Java代码中声明的字符串自动 interned .
因此结果与手动调用String.intern()的结果相同 .
输出: