String s1 == String s2 (true) 但 FieldOffset 不同
Posted
技术标签:
【中文标题】String s1 == String s2 (true) 但 FieldOffset 不同【英文标题】:String s1 == String s2 (true) but FieldOffset is different 【发布时间】:2013-05-27 10:24: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);【问题讨论】:
见***.com/questions/513832/… 阅读问题。他说他知道如何比较字符串。这是内存管理和 JVM 的问题,而不是字符串比较。 是的,请阅读问题 【参考方案1】:你没有错过任何东西。 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?。确保您在使用前阅读了注意事项,但我做了一个快速测试,它似乎有效。
【讨论】:
但是如果他们在不同的位置,为什么==
返回true?
@aardvarkk 因为 staticFieldOffset 为您提供了s1
和s2
在SomeClass
中的位置。这些是不同的引用,它们甚至有不同的名称,s1
和 s2
。然而,这两个引用都指向同一个字符串对象,这就是==
将评估的事实。
@greedybuddha 您写道“但是这两个引用都指向同一个字符串对象”。我知道这是准确的,但是有没有办法通过输出一些地址来证明它(不查看字节码)。
查看我的更新答案,我将答案与代码链接以获取地址【参考方案2】:
在上面的代码中,您不是比较字符串的地址,而是比较它们的“存储分配中给定字段的位置”,即持有对(相同)字符串的引用的变量的位置。
【讨论】:
【参考方案3】:我认为您对 staticFieldOffset
返回的内容感到困惑。它将 pointer 的偏移量返回到 String
实例,而不是 String
本身的地址。因为有两个字段,所以它们有不同的偏移量:即两个指针,它们恰好具有相同的值。
仔细阅读Unsafe javadoc 可以看出:
报告给定字段在其存储分配中的位置 班级。不要期望对此执行任何类型的算术运算 抵消;它只是一个传递给不安全堆内存的 cookie 访问器。
换句话说,如果您知道实际的 Class
实例在内存中的位置,那么您可以将此方法返回的偏移量添加到该基地址,结果将是您可以在内存中找到的位置指向String
的指针的值。
【讨论】:
@AndrewW 至少在你玩 Unsafe 之前! @AndrewW 了解 C 确实非常重要,因为大多数当前的编程语言都从 C 派生了许多语义和机制。但那是 C,而不是 C++。像那样混合它们不是很准确。相反,C++“教”了太多 C++ 特有的东西。 @Theodoros 我不是指语义。 (尽管我同意它对此很有用。)我的意思是学习指针是如何工作的,而 C 和 C++ 都适用于此。 @AndrewW 哦,是的,为了理解偏移是如何工作的,我绝对同意。无论语言如何,每个人都应该了解指针运算及其应用(即使它是“隐式”完成的)。【参考方案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());
输出:
是的
假
是的
是的
【讨论】:
问题是关于 FieldOffset以上是关于String s1 == String s2 (true) 但 FieldOffset 不同的主要内容,如果未能解决你的问题,请参考以下文章
关于以下程序段的输出正确的是:String s1=“abc“+“def“;String s2=new String(s1); if(s1.equals(s2))System.out.println(代
为什么 String s1="hello" String s2 = new String("hello") s1==s2 为flase
String s1 == String s2 (true) 但 FieldOffset 不同