String、StringBuffer以及StringBuilder这哥三的区别是面试中经常被问到的问题,回答怎么回答大家都知道,但很少会有人从底层分析为什么不同,今天我会通过底层给大家分析他们的不同
public final class String;
public final class StringBuilder;
public final class StringBuffer;
1、通过代码来看这三者都是被final关键字修饰的类
2、这三者都是用来修饰字符串类型的
前面相同点说了三者都是修饰字符串的,那么为什么需要三个类呐?只用一个String不可以吗?下面会根据这个问题分析三者的不同点
我们先看String,我相信大家在平时使用最多的就是String,而使用StringBuilder和StringBuffer相对较少,但实际根据性能上来讲,除非你String的值是个常量或者不轻易修改的值,那么性能上是不如另外两者的,因为String每一次改变都会在内存里重新开辟出一个内存空间,而之前的空间也并不会被淘汰掉。代码证明:
String str = "你好";
System.out.println(str.hashCode());//现在的hash值:652829
str = str + 1;
System.out,prinln(str.hashCode());//字符串改变后:20237748
通过这里我们可以看出改变后String的内存地址以及改变了,而且String str = new String(“你好”);和String str = "你好"的内存地址也是不一样的。大家可以自己去尝试一下就知道了。
接下来我们来讲StringBuffer,那么StringBuffer每次值修改之后内存地址就不会改变了吗?是的,因为StringBuffer内部封装了一个方法append(),用来拼接字符串
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
// super是继承父类的实现,有兴趣的可以自己去看一下
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
我们用过hashCode()方法来证明一下,前后的hash值是否发生变化:
String str = "你好";
StringBuffer sb = new StringBuffer(str);
// 460141958
System.out.println(sb.hashCode());
sb.append(1);
// 460141958
System.out.println(sb.hashCode());
我们可以看出是没有变化的,而StringBuilder与StringBuffer是一样的,那么这两者又有什么区别呐,我们看一下StringBuffer里的方法就知道了
@Override
public synchronized StringBuffer replace(int start, int end, String str) {
toStringCache = null;
super.replace(start, end, str);
return this;
}
@Override
public synchronized StringBuffer insert(int index, char[] str, int offset,
int len)
{
toStringCache = null;
super.insert(index, str, offset, len);
return this;
}
我们可以直观的看出来StringBuffer的方法都用了synchronized 关键字来进行上锁,所以StringBuffer是线程安全的。
相对应的我们看下StringBuilder的一些方法:
@Override
public StringBuilder replace(int start, int end, String str) {
super.replace(start, end, str);
return this;
}
@Override
public StringBuilder insert(int index, char[] str, int offset,
int len)
{
super.insert(index, str, offset, len);
return this;
}
这里我们看到同样的方法StringBuilder就没有使用synchronized 关键字,所以相对而言是线程不安全的
这里我们可以看出String和StringBuffer以及StringBuilder的区别在于,String每一次改动都会产生一个新的内存地址而另外两者在改动的时候并没有发生内存地址的改变。
而StringBuffer和StringBuilder的区别在于StringBuffer是线程安全的而StringBuilder是线程不安全的,但通过效率上来讲,因为StringBuffer是线程安全的效率上就会比StringBuilder要慢一点。
三者的使用:
1、如果不经常修改的或者常量使用String。
2、如何拼接改动较大的如果使用线程则考虑使用StringBuffer否则使用StringBuilder