1.String存放位置和GC
Jdk1.7之前常量池在方法区,jdk1.7及以后移动到堆区。str.intern()会判断字符串常量池中是否存在该对象,若果没有则在常量池中创建该对象,并且在堆中存在该对象实例,在jdk1.6及之前复制一份字面量存储在常量池中,返回的也是该字面量在常量池中的引用,jdk1.7及以后堆中实例引用复制一份到常量池中,返回的也是该堆中的实例引用地址;如果有则直接返回该对象引用。当内存充足的时候,字符串常量池对象不会被回收,当内存不足的时候,并且该字符串的没有被其它变量引用,该字符串常量会被回收。
2.String 长度限制
String长度理论上没有限制,实际有限制,限制主要体现在底层存储String类型的容器和String构造函数上。
1)String底层使用bytess数组储存,u2是16位无符号整数,能存储的字符长度为2^16-1=65535
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
2) String构造函数
public String(char value[], int offset, int count) {
…
}
count是字符串长度,使用int表示,int能表示最大长度为2^31-1,
(2^31-1)216/8/1024/1024/1024 = 4GB
3.String的不可继承性
String属于引用类型,但是类被final关键字修饰,所以无法被继承。
4.常用字符串创建方式
常用创建一个字符串String对象的的方式有五种【实际构造函数有13种】。在Java7之前,分配在JVM的方法区中,属于常量池的一部分。在Java7之后字符串常量池被移到堆内存中,以便JVM进行垃圾回收。
(1) String str = “abc”;
该语句创建了一个对象,判断常量池是否有等于该值的字面量,如果有,则str直接指向常量池中该字面量的地址。
String str2 = "abc";
System.out.println(str2.intern());
System.out.println(str2.intern()==str2);//true
(2) String str = new String( “abc”);
首先该条语句创建了两个对象,首先在将字面量“abc”存储在常量池中,然后再在堆上创建“abc”的实例对象,返回的也是在堆上实例对象的引用。
下面验证代码了创建了两个对象和创建的顺序。
String str1 = new String("ABC");
System.out.println(str1.intern()==str1);//false
(3)String str = “A”+“BC”;
创建了一个对象,JVM会进行优化,只会将最终结果”ABC“(literal constant)存储在常量池中。
(4) String str1 = “A”;String str2 = str1+“BC”;或者String str2 = “BC”+str1;
第二个语句两种形式,只会新创建了两个对象,第一个是在堆中创建一个“ABC”对象,第二个是在在常量池中存储“ABC”在堆中的引用。因为只要在字符串相加过程中存在一个变量,底层都会使用StringBuilder累的append方法,在堆上新创建一个对象,并返回该对象的引用,如果新创建的对象字面量在常量池中不存在,还会在常量池中创建一个新的字符创串常量对象。
(5) String str1 = “A”;String str2 = “BC”; String str3 = str1+str2;
创建语句三在堆上创建了一个对象。
5.intern()用法
new String(String str)的时候会首先在常量池中创建str字面量,然后再在堆中创建一个字符串实例,当调用str.intern()的时候,发现常量池中已经有str字面量,不在将堆中实例引用存储在常量池中,而是直接返回常量池str字面量引用
String str = new String("ali");
System.out.println(str== str.intern());//false
new Builder(String str)和newString(String str)创建过程相似,会首先在常量池中创建str字面量,再在堆中创建一个字符串实例,调用str.intern()的时候,发现常量池中已经有str变量,不在将堆中实例引用存储在常量池中,而是直接返回常量池str字面量引用
String str2 = new StringBuilder("美团").toString();
System.out.println(str2 == str2.intern());//false
new StringBuilder(“58”).append(“同城”).toString();语句创建了两个对象,存储在常量池中的“58”,和存储在堆中的“58同城”, 返回的是"58同城"在堆中实例引用,调用str1.intern(),在常量池发现“58同城”不存在, 而在堆中有“58同城”实例对象,直接将该对象的引用放在常量池中(jdk1.6及以前是拷贝字面量在常量池中), 同时也是返回该对象在堆中引用,因此结果为true。
String str1 = new StringBuilder("58").append("同城").toString(); System.out.println(str1==str1.intern());//true
直接在常量池创建字符串实例,str3.intern()也是返回的常量池中字符串实例的引用,因此返回true
String str3 = "javaEESESESE";
System.out.println(str3.intern()==str3);//true