我们知道String类最大的特点是不可变性,这意味着对String类的任何修改都会新生成一个字符串,比如你执行了String类的substring,replace,toUpperCase,toLowerCase,trim方法都会生成一个新的字符串,一旦你的程序对字符串有大量修改,那么在jvm的堆内存中就会生成大量的旧的临时垃圾字符串对象,如何解决这一问题呢?
答案是使用StringBuffer或者StringBuilder类,其中StringBuffer是一个旧类,而StringBuilder是在JDK5中新增的一个类。
这里面就涉及一个比较常见的话题,也是java开发者论坛被提问最多的一个问题,在Java里面String,StringBuffer,StringBuilder的区别和联系。
在回答这个问题之前,我们先来看一些String类的基本特点:
(1)string是不可变的,带来的好处主要有两点,第一是线程安全,可以在多个线程中共享而不需要加锁,第二是由于不变性所以它的hashcode可以被缓存后提升效率,这也是为什么我们见到的大多数的HashMap的key都是使用String类型的。
(2)通过双引号定义的字符串我们称为字符串字面量,这部分字符串会被在string pool中创建,在java里面比较一个对象相等,应该优先选择equals方法而不是==方法
(3)对于字符串拼接的 + 号,底层其实是使用StringBuffer 或者 StringBuilder来完成的。
看下面一个案例:
-
String s1=
"a";
-
String s2=
"b";
-
-
String s3=s1+s2;
// internal use StringBuffer orStringBuilder create new String Object
-
String s4=
"ab";
-
-
System.out.println(s3==s4);
//false
但如果使用final修饰后,jvm层面会直接优化成一个字符串字面量,这一点需要特别注意,所以下面代码的结果会返回true:
-
final
String s1=
"a";
-
final
String s2=
"b";
-
-
String s3=s1+s2;
-
String s4=
"ab";
-
-
System.out.println(s3==s4);
//true
(4)通过toString方法,可以把StringBuffer,StringBuilder转成String,通过构造方法可以把String转成StringBuffer,StringBuilder,但不能通过强制转换来操作这三个类,即使他们处于同一类继承层次下,否则会抛出java.lang.ClasscastException异常。
关于StringBuffer,StringBuilder的区别非常简单,大家仅仅需要记住StringBuffer是线程安全和同步的而StringBuilder则不是线程安全和非同步的即可,同时因为StringBuffer是线程线程安全的,所以它的性能低于StringBuilder类的。
结论:
String类是不可变的,但是StringBuffer,StringBuilder是可变的。StringBuffer是同步的,所以它的性能低于StringBuilder。连接操作符 + 号,底层是使用StringBuffer或者StringBuilder实现的。
那么在日常开发中,应该如何选择一个合适的字符串操作类呢?
如果了解上面的内容,其实就很容易回答了:
(1)如果你要求字符串不可变,那么应该选择String类
(2)如果你需要字符串可变并且是线程安全的,那么你应该选择StringBuffer类
(3)如果你要求字符串可变并且不存在线程安全问题,那么你应该选择StringBuilder类
通过前面的文章,我们知道String类最大的特点是不可变性,这意味着对String类的任何修改都会新生成一个字符串,比如你执行了String类的substring,replace,toUpperCase,toLowerCase,trim方法都会生成一个新的字符串,一旦你的程序对字符串有大量修改,那么在jvm的堆内存中就会生成大量的旧的临时垃圾字符串对象,如何解决这一问题呢?
答案是使用StringBuffer或者StringBuilder类,其中StringBuffer是一个旧类,而StringBuilder是在JDK5中新增的一个类。
这里面就涉及一个比较常见的话题,也是java开发者论坛被提问最多的一个问题,在Java里面String,StringBuffer,StringBuilder的区别和联系。
在回答这个问题之前,我们先来看一些String类的基本特点:
(1)string是不可变的,带来的好处主要有两点,第一是线程安全,可以在多个线程中共享而不需要加锁,第二是由于不变性所以它的hashcode可以被缓存后提升效率,这也是为什么我们见到的大多数的HashMap的key都是使用String类型的。
(2)通过双引号定义的字符串我们称为字符串字面量,这部分字符串会被在string pool中创建,在java里面比较一个对象相等,应该优先选择equals方法而不是==方法
(3)对于字符串拼接的 + 号,底层其实是使用StringBuffer 或者 StringBuilder来完成的。
看下面一个案例:
-
String s1=
"a";
-
String s2=
"b";
-
-
String s3=s1+s2;
// internal use StringBuffer orStringBuilder create new String Object
-
String s4=
"ab";
-
-
System.out.println(s3==s4);
//false
但如果使用final修饰后,jvm层面会直接优化成一个字符串字面量,这一点需要特别注意,所以下面代码的结果会返回true:
-
final
String s1=
"a";
-
final
String s2=
"b";
-
-
String s3=s1+s2;
-
String s4=
"ab";
-
-
System.out.println(s3==s4);
//true
(4)通过toString方法,可以把StringBuffer,StringBuilder转成String,通过构造方法可以把String转成StringBuffer,StringBuilder,但不能通过强制转换来操作这三个类,即使他们处于同一类继承层次下,否则会抛出java.lang.ClasscastException异常。
关于StringBuffer,StringBuilder的区别非常简单,大家仅仅需要记住StringBuffer是线程安全和同步的而StringBuilder则不是线程安全和非同步的即可,同时因为StringBuffer是线程线程安全的,所以它的性能低于StringBuilder类的。
结论:
String类是不可变的,但是StringBuffer,StringBuilder是可变的。StringBuffer是同步的,所以它的性能低于StringBuilder。连接操作符 + 号,底层是使用StringBuffer或者StringBuilder实现的。
那么在日常开发中,应该如何选择一个合适的字符串操作类呢?
如果了解上面的内容,其实就很容易回答了:
(1)如果你要求字符串不可变,那么应该选择String类
(2)如果你需要字符串可变并且是线程安全的,那么你应该选择StringBuffer类
(3)如果你要求字符串可变并且不存在线程安全问题,那么你应该选择StringBuilder类