虽然我试图制作一个假的JDBC驱动程序来测试一个安全的类加载器,但我发现了一个奇怪的行为,使用以下代码:
val stringClass = java.lang.String::class.java
val intern = stringClass.getMethod("intern")
val pooledString = intern.invoke("Hello World") as String
val valueField = stringClass.getDeclaredField("value")
valueField.isAccessible = true
val pooledValue = valueField.get(pooledString) as ByteArray
println(
"""|----------------------------------------
| String: ${System.identityHashCode(stringClass)}
| Thread: ${Thread.currentThread()}
| Pooled: ${System.identityHashCode(pooledString)}
| Internal: ${System.identityHashCode(pooledValue)}
|----------------------------------------""".trimMargin()
)
for (index in pooledValue.indices) {
pooledValue[index] = 'X'.toByte()
}
从JDBC驱动程序的伴随对象运行上面的代码可以得到:
String: 349885916
Thread: Thread[main,5,main]
Pooled: 718231523
Internal: 1349414238
但是在加载JDBC驱动程序之前(在程序的相同执行期间)从测试类的方法运行相同的代码会产生以下结果:
String: 349885916
Thread: Thread[main,5,main]
Pooled: 1635756693
Internal: 504527234
我原本以为获取字符串的实习版本应该在两种情况下给出相同的字符串,但似乎即使在程序的同一运行中,2个位置为String.intern提供不同的值,这些冲突用javadoc说:
当调用实习方法时,如果池已包含 字符串等于此 String 对象,由...确定 equals(Object)方法,然后是池中的字符串 回。否则,此 String 对象将添加到 将返回池和对此 String 对象的引用。
这是预期的,如果是这样,为什么值不同?
答案 0 :(得分:2)
最有可能的参考是垃圾收集。合同说如果你得到两次相同的字符串并比较返回的字符串,它们将是相等的。但是,如果你得到一个字符串,释放引用(使池化的字符串可用于gc)然后再次得到一个类似的字符串,没有什么能保证它仍然会被池化。您将获得一个新的池化字符串,它具有不同的identityHashCode。
保持对pooledString的引用,以便它不会被垃圾收集,看看会发生什么!