前面的文章中已经学习到了类加载子系统,JVM运行时数据区(内存模型),执行引擎的一系列文章,接下来写一篇结合String类和Stringtable的文章
1、String的基本特性
测试不可变性
第一种情况
其实s1和s2都指向了字符串常量池中的“abc”。所有为true第二种情况
其实这种情况在我们的常量池中,刚开始他们两个指向的“abc”,这个在常量池是不会改变的,当我们的s1进行修改后,是在常量池中重新造了一个。看另外一种情况
对s2进行修改,同样的也是再制造一个,而不是在原来的基础上修改。另外一种情况:进行替换。【同样的也没有改变】
(2)不存储相同的值
String底层Hashtable结构说明。
如果你在jdk8和之后的版本中,你设置更小的,是报错的。
java8方法区是落实到了元空间,而字符串常量池还是不变的。
当再次加载到相同的内容时,是不会再去加载相同的内容的。
第一种情况
编译期优化,就是在编译的时期就认为我们的s1=“abc”。第二种情况
只要拼接的过程有一个是变量,就在我们的堆中,具体的结果时拼接的结果。new了对象就不会相同。另外一个种情况就是intern方法的情况。
(2)拼接的底层原理
当我们有变量的情况下,它的拼接原理是如下。
StringBuilder调用toString方法就约等于去new 了String【看清楚是约等于,一会在后面的intern方法讲为什么是约等于】
【因为我们这个StringBuilder是在我们的JDK1.5的时候提供的,那之前我们的String用什么进行拼接的呢?其实用的StringBuffer来处理的】
当我们是非变量的时候【即常量的方式】
分析原因1
通过“+”效率低的原因就是我们每次去创建的StringBuilder和String的情况。
通过创建它StringBuilder的一个对象来进行添加,所以它的效率高很多了。
分析原因2
通过“+”,由于创建了较多的对象,而且是用了就不要了,它内存占用是比较严重的,而且如果进行GC的话,花费的时间也是比较多的情况。
改进的空间也是有的
我们的StringBuilder默认创建的数组大小为16,如果我们在开发中确定字符串的长度是不高于某一个值的情况下,那可以直接调用我们StringBuilder的另外一个构造器,直接确定大小。
首先它是去调用的C的方法库。
new String()首先来看会制造几个对象?
答案是两个(怎么证明,看字节码)原因就是: new了在堆空间中创建了一个,另外一个就是ldc在字符串常量池中创建了一个字面量。
那我们new String(“a”)+ new String(“b”)制造了几个对象呢?
答案是第一:new了StringBuilder第二:new了String(“a”)对象第三:在字符串常量池里面“a”对象四:new String(“b”)对象5:常量池中放了一个“b”如果再细纠的话:调用了toString了,又去new了一个String类对象。
而且发现它在常量池没有生成一个“ab”的常量引用。 (2)intern()的面试题
jdk6执行结果
第一个返回结果时false原因在于,s显然返回的是堆里面的对象,因为你调用了intern()方法的时,你常量池里面已经存在了一个“1”的对象了,所以你返回的是堆空间的对象地址,而另外一个s2则是字符串常量池里面的对象。
第二结果时false原因是
执行了s3的那行代码执行后,在字符串常量池里面不存在“11”,此时调用我们的intern()方法则要在字符串常量池里面生成一个“11”的引用。
jdk7/8执行结果
在jdk7/8后上面个s和s2相比较当然也不用说都知道,肯定是false而下面种情况则是有不同,因为我们在jdk7的将字符串常量池从永久代移动到了堆空间,所以当你调用s3.intern()方法的时候,它仅仅是去堆空间里面找了一下,“11”的这个的对象。
jdk6 vs jdk7/8
jdk6的时候
在jdk8/7都是如下样子
还有个例子
都是在jdk8中
如果这样,则会是false
(7)intern()的空间效率测定
调用了我们的intern()方法的情况,它不会去创建重复的字符串对象。
当你开发中发现大量存在的字符串,尤其其中存在很多重复字符串时,使用intern()可以节省内存空间。
结论
6、StringTable的垃圾回收测试(有参数设置)
可以设置这样的参数来测试
去重操作,指的是堆中的对象去重。