String a = "devender";
String b = "devender";
String c = "dev";
String d = "dev" + "ender";
String e = c + "ender";
System.out.println(a == b); //case 1: o/p true
System.out.println(a == d); //case 2: o/p true
System.out.println(a == e); //case 3: o/p false
Run Code Online (Sandbox Code Playgroud)
a和b都指向字符串常量池中的相同字符串文字.所以true在案例1中
String d = "dev" + "ender";
Run Code Online (Sandbox Code Playgroud)
应该在内部使用像 -
String d = new StringBuilder().append("dev").append("ender").toString();
Run Code Online (Sandbox Code Playgroud)
如何一个&d都指向相同的参考及不能一&ê?
T.J*_*der 79
有四件事情在发生:
(你清楚地知道这一点,但对于潜伏者)==测试,看看变量是指向同一个String对象,而不是等效的字符串.所以,即使x是"foo"和y也"foo",x == y可能是真的还是假的,这取决于是否x和y指向同一个String对象或不同的.这就是为什么我们使用equals而不是==比较字符串的等价性.以下所有内容只是为了解释为什么==有时候是真的,这不是==用来比较字符串的建议.:-)
在同一个类中,等效字符串常量(编译器知道的字符串是根据JLS中的各种规则的常量)被编译器引用相同的字符串(它也在类的"常量池"中列出它们).这就是为什么a == b是真的.
当类被加载,它的每一个字符串常量自动扣留 - JVM的字符串池检查等效字符串,如果找到一个,即String使用对象(如果没有,新String的恒新对象添加到池).因此即使x是在类中初始化的字符串常量Foo并且y是在类中初始化的字符串常量Bar,它们也将是==彼此.
上述第2点和第3点部分由JLS§3.10.5涵盖.(关于类常量池的一点是一个实现细节,因此之前是JVM规范的链接; JLS只是谈到了实习.)
如果编译器处理常量值,则编译器会进行字符串连接
String d = "dev" + "ender";
Run Code Online (Sandbox Code Playgroud)
被编译为
String d = "devender";
Run Code Online (Sandbox Code Playgroud)
并且"devender"是一个字符串常量,编译器和JVM将上面的点2和3应用于.例如,没有StringBuilder使用,连接发生在编译时,而不是运行时.这在JLS§15.28 - 常量表达式中有所介绍.所以a == d是真正出于同样的原因a == b是正确的:它们指向同一个常量字符串,所以编译器确保他们指的在类的常量池相同的字符串.
当任何操作数不是常量时,编译器不能这样做,所以它不能用:
String e = c + "ender";
Run Code Online (Sandbox Code Playgroud)
...即使代码分析可以很容易地表明它的价值c肯定会是"dev",因此e肯定会"devender".该规范仅使编译器使用常量值进行串联.因此,由于编译器无法执行此操作,因此它会输出StringBuilder您引用的代码,并且该工作在运行时完成,从而创建一个新String对象.该字符串不会自动实现,因此e最终会引用不同的String对象a,因此a == e也是错误的.
需要注意的是为维诺德说,如果声明c为final:
final String c = "dev";
Run Code Online (Sandbox Code Playgroud)
然后它将是一个常量变量(是的,它们真的被称为),因此§15.28将适用并且编译器将转向
String e = c + "ender";
Run Code Online (Sandbox Code Playgroud)
成
String e = "devender";
Run Code Online (Sandbox Code Playgroud)
而且a == e也是如此.
重申一下:没有一个意味着我们应该==用来比较字符串的等价性.:-)这equals就是为了什么.
The*_*ind 16
编译器在幕后进行了大量优化.
Run Code Online (Sandbox Code Playgroud)String d = "dev" + "ender";
在这里,编译器将取代"dev" + "ender"与"devender"被编译的程序时.如果要添加2个文字(这适用于基元和字符串),编译器会执行此优化.
Java代码:
String d = "dev" + "ender";
Run Code Online (Sandbox Code Playgroud)
字节代码:
0: ldc #16 // String devender
Run Code Online (Sandbox Code Playgroud)
来到一个特例:
Run Code Online (Sandbox Code Playgroud)final String c = "dev"; // mark this as final String e = c + "ender";
使cfinal成为String的编译时常量.编译器将意识到值c不能改变,因此在编译时将c用值"dev" 替换所有出现的值,因此e将在编译期间自行解决.
正如你在内部所说,最后的连接是针对类似的东西完成的
String e = new StringBuilder().append(c).append("ender").toString();
Run Code Online (Sandbox Code Playgroud)
实施toString()的StringBuilder 创建一个新字符串.这是实施.
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
Run Code Online (Sandbox Code Playgroud)
仅当两个字符串相同时才使用==而不是.equals() 返回true来比较字符串.在这种情况下,它们不相同,因为第二个字符串是作为类型的新对象创建的String.
其他连接由编译器直接执行,因此不会创建新的String.
"dev" + "ender"是一个编译时可计算的常量表达式:两个参数都是字符串文字.因此,表达是"devender".
同样不能说c + "ender":某些情况(某些代码在不同的线程上运行)可能导致c设置为不同的值.符合条件c是为了final避免这种可能性,在这种情况下e也会引用相同的对象a.
所以a,b和d所有引用同一个对象.
d和之间的区别在于e,当您连接字符串文字时,连接是在编译时执行的.Java编译器"dev" + "ender"以与"devender"表达式相同的方式处理表达式,在编译时生成相同的文字.由于所有String文字得到实习,d,这是一个结果"dev" + "ender",也结束了引用相同的对象a和b的"devender".
表达式for e,c + "ender"是在运行时计算的.即使它产生相同的字符串,编译器也不会使用这个事实.这就是产生不同String对象的原因,导致比较失败==.
小智 6
字符串d ="dev"+"ender"; 常数+常数,'d'仍然是常数(同一个),所以(a == d)为真;
字符串e = c +"ender"; 变量+常量,结果'e'是一个变量,它将在内部使用StringBuilder,并创建一个新的引用.