关于String.intern的解析
因为在JDK1.6中是有永久代的(1.7)也有,不过为了响应JDK1.8的号召,所以在JDK1.7中把字符串常量池从永久代中放到了堆中
所以会导致一些问题
当我们调用intern方法时,会产生一些不一样的东西
String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4); //jdk6 下false false //jdk7 下false true
这个代码为例
在JDK1.6中,如果new了一个String对象,那么会在常量池中建立一个对应的对象,然后在堆中也会建立一个对象
相信很多 JAVA 程序员都做做类似 String s = new String("abc")这个语句创建了几个对象的题目。 这种题目主要就是为了考察程序员对字符串对象的常量池掌握与否。 上述的语句中是创建了2个对象,第一个对象是”abc”字符串存储在常量池中,第二个对象在JAVA Heap中的 String 对象。
同理,JDK1.7也是,但是有些不一样的是intern方法
首先分析1.6,我们new了一个s,然后常量池和堆中就都有“1”这个对象,这个时候调用s.intern()其实就是说,常量池中如果每个这个对象就建立,有就直接返回这个对象的引用
而String s2 = "1";这行代码直接从常量池中获取对象,所以肯定不会相等啊
String s3 = new String("1") + new String("1");也是一样
下面分析1.7
这个就有些不同了,因为常量池移到了堆中啊
new的时候同样会建立两个对象,s指向的是堆中的对象,但是s2指向的是常量池中的,所以还是false
但是s3和s4就不一样了,s3这句代码中现在生成了2个最终对象,是字符串常量池中的“1” 和 JAVA Heap 中的 s3引用指向的对象
此时s3引用对象内容是”11”,但此时常量池中是没有 “11”对象的。
然后这个时候我们调用intern方法,会在常量池中创建“11”这个对象,关键点是 jdk7 中常量池不在 Perm 区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的。
最后String s4 = "11"; 这句代码中”11”是显示声明的,因此会直接去常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向 s3 引用对象的一个引用。所以 s4 引用就指向和 s3 一样了。因此最后的比较 s3 == s4 是 true。
String a = new String("a"); String b = new String("b"); String d = a + b; d.intern(); String c = "ab"; System.out.println(c == d); //////////////// true
为什么String是不可变的?
大概就四点原因吧,string类是final类型(无法继承,底层方法无法修改)+ private修饰,不提供set方法(无法直接在类外部直接访问修改)+ value数组也是final类型(保证数组引用指向不可变,在类内部有一点点作用,还有就是为了实现字符串常量池)+ string里面所有其他的方法的实现都没有动过value数组