String类简介:
String类是Java基本数据类型 char 的包装类,String不是Java基本数据类型。
String 类代表字符串,它的内部是使用了 char[] (被final修饰)数组实现的。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例来实现。字符串是常量;它们的值在创建之后不能改变(不能改变是指不能给一个字符串常量重新赋值,如 “a” = “b” 会出现编译时错误)。字符串缓冲区(StringBuffer、StringBuilder)支持可变的字符串。因为 String 对象是不可变的,所以可以共享它们(内存中只有一份)。
String类是Java中非常基本的类,面试中也经常能遇到关于它的问题。如:下面这行代码一共创建了几个对象?
String s = new String("hu");
那么我们来分析,这句代码是使用String类的有参构造 public String(String original){…},并传入字符串常量 “hu” 作为参数创建了一个对象;并声明了一个String类型的变量 s 来指向该对象。首先看等号左边,String s 是一个引用,存放在内存中的栈(栈、堆和常量池都是内存中的一部分容器)中,它不是对象。其次是等号右边,new String()是一个对象,它是在内存中的堆中开辟了一个空间用来储存该对象。new String()中传入的字符串常量 “hu” 也是一个对象,它存放在内存中的常量池中。那么这行代码总共就创建了两个对象,一个是new String(),一个是字符串常量 “hu” 。其中,new String()的值指向的也是常量池中的 “hu” ,也就是说内存中只有一个 “hu”。
上面那个面试题其实并不能很好的突显出String类的特性。接下来看看下面这个面试题:下面这行代码一共创建了几个对象?
String s = "hu" + "12340";
在了解了上一道面试题后,这个题就简单了很多。首先,”hu” 和 “12340” 是两个字符串常量,也就是两个对象。这个题的考点在于,String 对象是不可变的。”hu” 和 “12340” 两个字符串的拼接并不是简单的将两个字符串连起来。因为String对象不可修改,所以只能新创建一个字符串对象来承接它们拼接后的结果,即 “hu12340”。它们两个常量的拼接并不会对自身产生改变,它们还是存在于常量池中。这时,常量池中有 ”hu“ 、”12340“和 ”hu12340“ 。也就是说,这行代码共创建了三个对象。
StringBuffer和StringBuilder:
既然String对象是不可改变的,那么如果遇到大量或循环的字符串拼接,岂不是要一直在常量池中添加元素吗。显然这样会造成内存的大量浪费,有这样的需求,也就会有其解决方法。StringBuffer和StringBuilder也是字符串操作类,它们都是AbstractStringBuilder抽象类的直接子类。StringBuffer和StringBuilder的区别在于:StringBuffer是线程安全的,它内部的很多方法都加了synchronized
关键字。它们在用法上并没有太大区别,但因为StringBuffer的大多方法上加了同步锁,所以在效率上比StringBuilder低。AbstractStringBuilder是一个抽象类,它对字符串的操作也是通过 char[] 数组实现的。与Stirng类不同的是,它内部的 char[] 数组并没有被final关键字修饰。也就是说,它是可变的。
StringBuilder和StringBuffer在对字符串的拼接上与String类是有区别的,它们并不是使用 +
在进行拼接的。因为直接使用 +
来进行拼接的话,又会创建出新的字符串对象,那么就和直接只用Stirng类没什么区别了。它们的字符串拼接是通过调用append()
方法。
public static void main(String[] args) {
//StringBuffer的用法与StringBuilder几乎一致
StringBuilder sb = new StringBuilder();
System.out.println(sb);
sb.append("hu");
System.out.println(sb);
sb.append("12340");
System.out.println(sb);
sb.append(".vip");
System.out.println(sb);
}

通过StringBuilder或StringBuffer的append方法实现的字符串拼接,都是在对它自身进行修改。如上代码中的对象有 “hu”、“12340”、“.vip”和newStringBuilder(),在常量池中并没有拼接后的 ”hu12340“和”hu12340.vip“。那么趁热看下下面这个题:下面的代码中共创建了几个对象,控制台打印出什么?
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
StringBuilder hu = sb.append("hu");
StringBuilder hu13240 = hu.append("12340");
StringBuilder hu12340vip = hu13240.append(".vip");
System.out.println(hu12340vip);
System.out.println(sb == hu12340vip);
}
那么首先,”hu“、”12340“ 和 ”.vip“ 是字符串常量,即三个对象。new StringBuilder() 新建一个StringBuilder对象,也是一个对象。剩下的 sb
、hu
、hu12340
和hu12340vip
都是声明的引用,并不是对象。所以一共有四个对象。

StringBuilder或StringBuffer的append方法都有返回值,返回的都是StringBuilder或StringBuffer对象。那么,这个方法返回的对象是否是新的对象呢?答案是否。从AbstractStringBuilder中的参数为String的append方法的实现中,可以看到return的是this 即自身。那么该方法返回的对象并不是新的对象,而是本身。所以引用sb
和引用hu12340vip
都是指向的同一个对象, sb
== hu12340vip
为true。
public AbstractStringBuilder append(String var1) {
if (var1 == null) {
return this.appendNull();
} else {
int var2 = var1.length();
this.ensureCapacityInternal(this.count + var2);
var1.getChars(0, var2, this.value, this.count);
this.count += var2;
return this;
}
}
String、StringBuilder和StringBuffer的区别:
String字符串常量不可变,使用字符串拼接时会开辟新空间。
StringBuffer字符串变量可变,线程安全,字符串拼接直接在字符串后追加。
StringBuilder字符串变量可变,非线程安全,字符串拼接直接在字符串后追加。
1.StringBuilder执行效率高StringBuffer,高于String。
2.String是一个常量,是不可变的,所以对于每一次+=赋值都会创建一个新的对象;StringBuffer和StringBuilder都是可变的,当进行字符串拼接时采用append方法,在原来的基础上进行追加,所以性能比String要高。
3.StringBuffer是线程安全的,而StringBuilder是线程非安全的,所以StringBuilder的效率高于StringBuffer.
4.对于大数据量的字符串的拼接,采用StringBuffer或StringBuilder.