我在一本书中查了一下,在解释方面通常比网站更全面。
以前为例:
if (nickname == "Bob")
仅当nickname
引用相同的String对象时,条件才为真。
这是一个令我困惑的句子,任何人都可以解释为什么会这样:
为了提高效率,Java只为每个字符串常量生成一个字符串对象。
这本书指出,组装对象“Bob”的方式也会影响条件是否真实,会让我感到困惑。
例如:
String nickname = "Bob";
...
if (nickname == "Bob") //TRUE
但如果“Bob”是从.substring()
方法创建的,则条件为FALSE。
String name = "Robert";
String nickname = name.substring(0,3);
...
if (nickname == "Rob")//FALSE
为什么会这样?
编辑:在本书的解释结束时,我发现了一句让我困惑的句子:
因为字符串对象总是由编译器构造,所以你永远不会对是否共享两个字符串对象感兴趣。
我们编写的所有内容都不是由编译器构建的吗?
答案 0 :(得分:10)
你需要了解两件事
String a = "Bob";
String b = "Bob";
System.out.println(a.equals(b));
System.out.println(a == b);
您怎么看?什么输出?
true
true
这是做什么的?在永久生成内存的字符串池中创建的第一个字符串第二个字符串从池中获取现有对象。
String a = "Bob"; // create object in string pool(perm-gen)
String b = "Bob"; // getting existing object.
你的注意力如何:
为了提高效率,Java只为每个字符串常量生成一个字符串对象。
String nickname = name.substring(0,3);
由于String是不可变对象name.substring(0,3);
在堆内存中创建new String("Rob")
,不在perm-gen 中。
在Java 8中,字符串池是在Heap的PermGen区域中创建的,垃圾收集可以在perm空间中进行,但依赖于JVM到JVM。从JDK 1.7更新开始,String池被移动到创建对象的堆区域。
了解更多here。
答案 1 :(得分:2)
字符串文字由JVM内部处理,因此对于每个唯一的字符串文字,如果它具有相同的值,它总是引用同一个对象。例如,一个字符串文字" test"在A类中将与字符串文字完全相同的对象" test"在B班。
我们编写的所有内容都不是由编译器构建的吗?
编译器在编译时只是将字符串文字添加到类constant pool
中,并使用名为LDC
的特殊指令加载它,其余部分由JVM处理,JVM从a加载字符串常量永远不会删除/垃圾收集任何对象的特殊字符串常量池(以前是permgen)。
但是,您可以获得内部'使用String#internal()
的任何字符串的版本(就好像它是一个字符串文字),这会导致==
运算符再次运行。
答案 2 :(得分:1)
它是关于物体的。
由于这些不是原始的==
并不能比较它们是什么。 ==
比较 where (在堆内存中)。
.equals()
应该(如果已实施)比较该内存中包含的内容。
这是一个容易被遗忘的细节,因为小字符串和盒装数字在创建时通常不会获得新内存,因为它更适合指向同一事物的缓存版本。因此,你可以要求一个新的" Bob"一遍又一遍,只需将参考(记忆地址)交给同一" Bob"。这诱使我们将它们比作原语,因为它似乎以相同的方式工作。但并不是每个对象都会发生这种情况,所以让自己发展是一个坏习惯。
这个技巧只有在1)匹配的对象已经存在时才有效,2)它是不可变的,所以你不能让其他人的副本惊喜#34;通过改变它。
滥用一个旧的比喻,如果两个人拥有相同的地址,那么可以肯定他们在家里保留相同的东西,因为它是同一个家。然而,仅仅因为两个人有不同的地址并不意味着他们不会在家里保持完全相同的东西。
在比较这些对象中保存的内容时,实现.equals()
就是要定义我们关心的内容。
因此,只需信任==
来比较基元的值。使用.equals()
向对象询问它的想法是什么等等。
此外,这不仅仅是一个java问题。每个面向对象的语言都可以让你直接处理原语和对象引用/指针/内存地址,这会迫使你以不同的方式处理它们,因为对象的引用不是它自己的对象。
对象值与身份不同。如果它只有一个具有相同内容的对象的副本。由于语言无法完美地实现这一点,因此您不得不以不同的方式处理这两个概念。