字符串-intern()在Java 6和Java 7中的行为不同
class Test {
public static void main(String...args) {
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1.intern());
String s2 = "Goodmorning";
if (s1 == s2) {
System.out.println("both are equal");
}
}
}
此代码在Java 6和Java 7中产生不同的输出。在Java 6中s1==s2条件返回false,而在Java 7中s1==s2返回true。为什么?
为什么该程序在Java 6和Java 7中产生不同的输出?
9个解决方案
26 votes
似乎JDK7处理实习生的方式与以前不同。
我用版本1.7.0-b147进行了测试,结果“都是相等的”,但是用1,6.0_24执行它(相同的字节码)时,我没有收到消息。
它还取决于intern行在源代码中的位置。 以下代码也不会输出该消息:
class Test {
public static void main(String... args) {
String s1 = "Good";
s1 = s1 + "morning";
String s2 = "Goodmorning";
System.out.println(s1.intern()); //just changed here s1.intern() and the if condition runs true
if(s1 == s2) {
System.out.println("both are equal");
} //now it works.
}
}
在其字符串池中找不到字符串后,好像intern,将实际实例s1插入池中。 创建s2时,JVM正在使用该池,因此它获得与s1相同的引用。 另一方面,如果首先创建s2,则该引用将存储到池中。
这可能是由于将永久字符串从Java堆的永久生成中移出的结果。
在这里找到:JDK 7中解决的重要RFE
在JDK 7中,不再将永久字符串分配给Java堆的永久代中,而是与应用程序创建的其他对象一起分配给Java堆的主要部分(称为年轻代和老一代)中。 。 此更改将导致更多数据驻留在主Java堆中,而永久生成中的数据更少,因此可能需要调整堆大小。 由于此更改,大多数应用程序只会看到相对较小的堆使用差异,但是加载许多类或大量使用String.intern()方法的大型应用程序将看到更大的差异。
不知道这是一个错误,以及是哪个版本... JLS 3.10.5声明
显式内联计算所得字符串的结果是与任何先前具有相同内容的文字字符串相同的字符串。
因此,问题是如何在编译时或执行时解释预先存在的内容:“ Goodmorning”是否预先存在?
我更喜欢它在7之前实现的方式...
Carlos Heuberger answered 2019-12-31T06:17:52Z
24 votes
让我们从示例中省略不必要的细节:
class Test {
public static void main(String... args) {
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6.
}
}
让我们将this视为黑匣子。 根据运行的一些测试案例,我得出的结论是,实现如下:
Java 6:
如果池包含的对象等于this,则返回对该对象的引用,否则,创建新字符串(等于this),放入池中,并返回对该创建实例的引用。
Java 7:
如果池包含的对象等于this,则返回对该对象的引用,否则将this放入池中,并返回this。
Java 6和Java 7都没有违反该方法的约定。
似乎新的内部方法行为是此错误的修复结果:[http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962931。]
Andrey answered 2019-12-31T06:18:43Z
9 votes
==比较这些参考。 intern方法可确保具有相同值的字符串具有相同的引用。
String.intern方法的javadoc说明:
public String intern()
返回字符串对象的规范表示。
最初为空的字符串池由主机专用维护。 类String。
调用intern方法时,如果池中已经包含一个 等于由equals(Object)确定的此String对象的字符串 方法,然后返回池中的字符串。 不然这样 将String对象添加到池中,并对此String进行引用 返回对象。
因此,对于任意两个字符串s和t,s.intern()== 当且仅当s.equals(t)为true时,t.intern()才为true。
所有文字字符串和字符串值常量表达式都是 实习生。 字符串文字在Java语言的第3.10.5节中定义 规格
返回:与该字符串具有相同内容的字符串,但 保证来自唯一字符串池。
因此,无需进行内部编译,编译器便会查看Java代码中的常量并从中构建其常量池。 String类维护着一个不同的池,interning对照该池检查传入的字符串,并确保引用是唯一的(以便==起作用)。
Nathan Hughes answered 2019-12-31T06:19:44Z
7 votes
在jdk6中:String s1="Good";在常量池中创建一个字符串对象“ Good”。
if(s1==s2)在常量池中创建另一个String对象“ morning”,但这一次实际上是JVM:s1。
现在当if(s1==s2)运算符在堆中创建一个对象时,因此s1中的引用是堆而不是常量池并且String s2="Goodmorning";在常量池中创建一个字符串对象“ Goodmorning”,其引用存储在s2中。
因此,if(s1==s2)条件为假。
但是在jdk7中会发生什么呢?
Mohammad Faisal answered 2019-12-31T06:20:22Z
6 votes
第一种情况:
在第一个代码段中,您实际上是在字符串池中添加了三个字符串。1. s1 =“好”
2. S1= “早安”(串联后)3. s2 =“早上好”
在执行if(s1 == s2)时,对象相同但引用不同,因此为假。
第二种情况:
在这种情况下,您使用的是s1.intern(),这意味着,如果池中已经包含等于equals(Object)方法确定的此String对象的字符串,则将返回池中的字符串。 否则,将此String对象添加到池中,并返回对此String对象的引用。
s1 =“好”
s1 =“早上好”(连接后)
对于字符串s2 =“ Goodmorning”,新的字符串未添加到池中,并且您获得了s2的现有字符串的引用。 因此,if(s1 == s2)返回true。
Sachin Karjatkar answered 2019-12-31T06:21:17Z
5 votes
您需要使用s1.equals(s2)。将==与String对象一起使用会比较对象引用本身。
编辑:当我运行您的第二个代码段时,我不会得到打印出来的“两者相等”。
Edit2:澄清了使用“ ==”时会比较引用。
Datajam answered 2019-12-31T06:21:46Z
4 votes
比较字符串主要有4种方法:
“ ==运算符”:它只是比较字符串对象的引用变量。 因此,根据创建字符串的方式,它可能会给您带来意想不到的结果,即使用String类的构造函数或简单地使用双引号,因为两者获取内存的方式不同(分别在堆和池中)。
“ equals(Object)方法”:这是对象类的方法,并由字符串类重载。 它比较整个字符串并且区分大小写。
“ equalsIgnoreCase(String)方法”:这是字符串类的方法,用于比较整个字符串,并且不区分大小写。
“ compares(String)方法”:比较两个字符串的字符,如果返回值为0,则返回它们的差,这意味着字符串相等。
user837569 answered 2019-12-31T06:22:25Z
3 votes
每当在两个String之间进行比较时,请不要使用==和eqauls(),因为要比较的对象不是引用:
string1.equals(string2);
Eng.Fouad answered 2019-12-31T06:22:45Z
2 votes
结果代码取决于运行时:
class Test {
public static void main(String... args) {
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6.
}
}
如果您这样写:
class Test {
public static void main(String... args) {
String s = "GoodMorning";
String s1 = "Good";
s1 = s1 + "morning";
System.out.println(s1 == s1.intern()); // Prints false for both jdk7 and jdk6.
}
}
原因是“ ldc #N”(从常量池加载字符串)和String.intern()都将在热点JVM中使用StringTable。 有关详细信息,我写了一篇池英语文章:[http://aprilsoft.cn/blog/post/307.html]
Hatter Jiang answered 2019-12-31T06:23:14Z