考虑以下代码:
fun main(args : Array<String>) {
println("Async" == MetricCategory.Async.toString())
println("Async" === MetricCategory.Async.toString())
}
输出
true
true
虽然我在期待
true
false
为什么第二次检查会打印true
,因为两个引用都不同
答案 0 :(得分:4)
引用相等不是变量名相同,或者以相同的方式访问它,它是内存中的位置是相同的。由于字符串是不可变的,编译器通常可以提前为它们保留内存,并且所有对相同值的引用都指向同一个地方。
不可变性很重要,因为在读/写引用不同的情况下共享只读引用是安全的。如果您在可变数据结构之间不正确地共享引用,则来自一组引用的修改将反映在另一组引用中,从而导致奇怪和不正确的行为。但是,如果数据无法再更改,您可以通过让所有内容指向相同的数据来尽可能多地保存内存。
答案 1 :(得分:3)
根据MetricCategory.Async.toString()
的实施方式,操作的结果可能是任意的。请考虑以下示例:
class MetricCategory {
object Async {
override fun toString(): String {
return "Async"
}
}
}
此实施会导致true
,true
打印出来。据记录,===
运算符会比较referential equality:
当且仅当a和b指向同一个对象时,的计算结果为真。
但为什么2个常量字符串表达式是同一个对象?这是由名为string interning的JVM(和其他运行时)的一个特性引起的:
在计算机科学中,字符串实习是一种只存储一个的方法 每个不同字符串值的副本,必须是不可变的。实习 字符串使一些字符串处理任务更多时间或 节省空间的代价是在字符串需要更多时间 创建或实习。不同的值存储在字符串实习生中 池。
String interning does not happen automatically in JVM但可以手动触发。
class MetricCategory {
object Async {
override fun toString(): String {
val result = "a".toUpperCase() + "SYNC".toLowerCase()
return result.intern()
}
}
}
以上示例将再次打印true
,true
,但仅打印,因为我们已调用String.intern
。
请考虑以下示例:
println("Async" == "Async") // true, obviously
println("Async" === "Async") // true, string interning for literals
println("Async" == java.lang.String("Async").toString())// true, obviously
println("Async" === java.lang.String("Async").toString()) // false, new instance on the right
println("Async" === java.lang.String("Async").toString().intern()) // true, right changed to shared instance
进一步阅读: