一、String对象的创建
java中的String创建有2种方式,字符串的创建有2种方式,一种是字面常量的方式,如:
String s1 = "abc";
另外一种是构造函数的方式,如:
String s2 = new String("def");
两种方式的创建的字符串在内存中的存放有所区别。
- 通过字面常量申明的字符串,JVM会先去字符串常量池中找有没有相同的字符串,有的话直接返回其引用,没有到话在字符串常量池中创建,并返回其引用。
- 使用构造函数创建字符串时,JVM同样也会先到字符串常量池中找找有没有相同的字符串,有的话,会先在堆中创建一个String对象,然后在这个String对象内部引用常量池中的字符串,最后返回String对象的引用。如果字符串常量池中没有该字符串,则会在字符串常量池中创建该字符串,然后再在堆中创建String对象。在String对象的内部引用常量池中的字符串,最后返回String对象的引用。
看了字符串的创建过程,这里必须要说下字符串常量池,字符串常量池是JVM为了减少因字符串的重复创建而造成内存大量消耗而产生的一块缓存区域。由上面的字符串的创建过程可以看出,字符串常量池中的字符串是所有String对象共享的。创建过程中,只要寻找到了相同字符串,否可以拿去用。所以String对象是不能被修改的,因为一旦修改,所有引用这个字符串的String对象都会随之改变。所有String类是用final修饰的。
二、String对象的比较
String对象的比较有两种方式,“==”和“equals”。
基本数据类型的比较,可以用“==”比较他们的值,而引用数据类型(包含String),用“==”只能比较他们的内存地址。
再看看String的“equals”源码
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = length();
if (n == anotherString.length()) {
int i = 0;
while (n-- != 0) {
if (charAt(i) != anotherString.charAt(i))
return false;
i++;
}
return true;
}
}
return false;
}
这里会将字符串拆分成单个字符进行逐个比较,所以比较的是字符串的内容。
String s1 = "abc";
String s2 = new String("abc");
String s3 = "abc";
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s3.equals(s2));
输出结果:
18:16:36.370 22845-22845/com.example.zzq I/System.out: false
18:16:36.370 22845-22845/com.example.zzq I/System.out: true
18:16:36.371 22845-22845/com.example.zzq I/System.out: true
如果理解了以上内容,看这个输出结果就不难理解了。
三、字符串拼接的优化
由于String的拼接会造成大量对象的创建,从而会导致内存碎片的问题,所以java还提供了另外两个类类扩展字符串的功能,StringBuilder和StringBuffer,这两个类都实现了Appendable接口,都可以进行字符串拼接。
StringBuffer进行字符串拼接的接口都加了锁,所以是线程安全的,但执行效率偏低。
StringBuilder进行字符串拼接的接口 没有加锁,所以是线程不安全的,但执行效率高。