对于我们经常使用的字符串对象String,你能知道如下细节么?
String a = new String(“a”);创建了多少个对象呢?
JVM进行步骤为:
在常量池中查找是否有“a”对象
有则返回对应的引用引用实例
没有则创建对应的实例对象
在堆中new一个String对象
将对象的地址赋值给a,创建一个引用
所以,常量池中没有“a”则创建两个对象,否则创建一个对象,以及创建一个引用
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。
字符串的比较
对于字符串的比较,一般目的是比较字符串内容是否相等,这种情况下。使用equals()方法进行比较,而不要使用==。
Equals()比较的是内容是否相等,‘==’比较的是地址是否相等
String有没有长度限制呢?
在jdk1.8源码中,根据public String(char value[],int offset,int count)的定义,count是int型,所以char value[]中最多可以保存Integer.MAX_VALUE个,即2147483647字符。
但是事实证明,String a =””;中最多可以有65534个字符,如果超过,就会在编译期报错。上面这个65534是字符串常量池的长度,字符串常量池理论上允许最大长度为2^16=65536.而java得class文件使用UTF-8格式来存放字符,null值使用了两个字节来表示,因此只剩下65534个字节长度。
String的”+”是怎么实现的呢?
/**
* 对应反编译代码
* String s1 = "ab";
* String s2 = "a1";
*/
String s1 = "a" + "b";
String s2 = "a" + 1;
/**
* 对应反编译代码
* String s3 = (new StringBuilder()).append("a").append(s1).toString();
*/
String s3 = "a" + s1;
/**
* 对应反编译代码
* String s4 = (new StringBuilder()).append(s2).append(s3).toString();
*/
String s4 = s2 + s3;
/**
* 对应反编译代码
* String s5 = (new StringBuilder()).append("a").append(new Integer(1)).toString();
*/
String s5 = "a" + new Integer(1);
/**
* 对应反编译代码
* String s6 = new String();
* for(int i = 0 ;i < 3; i++){
* s6 = (new StringBuilder()).append(s6).append(i).toString();
* }
*/
String s6 = new String();
for(int i = 0 ;i < 3; i++){
s6 += i;
}
对于String s1 = “a” + “b”;这种,编译器会进行常量折叠。因为两个都是编译器常量。
对于String s = “a” + 变量;用StringBuilder的append()方法进行替代,最后调用toString()方法。
String和StringBuilder和StringBuffer之间的区别与联系。
String是字符串常量,SringBuffer是字符串变量(线程安全),StringBuilder是字符串变量(非线程安全)。
StringBuffer是线程安全的可变字符序列。一个类似String的字符串缓冲区,可将字符串缓冲区安全的运用于多个线程,其主要操作是append和insert方法,可重载这些方法以接受任意类型类型的数据。
StringBuilder与StringBuffer类似,但不保证同步,用于字符串缓冲区被单个线程使用的时候。建议优先使用,在大多数时候,StringBuilder比StringBuffer更快。
String类型与上两个类型的主要区别其实是String是不可变对象,每次对String对象进行改变其实等同于new出一个新的String对象,然后将指针指向指向新的String对象,所以对于经常改变内容的字符串最好不要使用String,而StringBuffer和StringBuilder,每次操作都是对其对象本身进行操作,而不是一个新的对象,所以一般情况下建议使用StringBuffer(线程安全,相对效率低)或者StringBuilder(线程不安全,效率高)。