引言
这题应该是最最经典的一轮必问了,三个亲兄弟问的多了,网上自然而然被整理的也很多。我觉得我自己也应该整理一篇,以加深自己的印象,不至于假如真的有哪一天被问到了,答不出个所以然来。
String
源码位置(Android O):/libcore/ojluni/src/main/java/java/lang/String.java
1. String对象非基本数据类型,存储在堆中;
2. 源码显示:
String类为final类,所以不能被继承,并且一旦被创建,就无法改变它的值。
String既然是final类,那么它是究竟怎么操作的呢?
假如说有这样一段代码:
String str = "hello";
str = str + "world";
答:1. JVM先创建了一个String对象str,并将"hello"赋值给str;
2. 再创建一个新的String对象str;
3. 把"hellp"和"world"合起来赋值给了新的str;
4. 由于大量的String拼接操作,JVM会出现内存紧张,基本上都会实时进行GC,引起运行变慢。
这些操作在JVM是这样子的:
所以:对String的操作是一个不断创建新的对象和回收老对象的过程。
值得注意的一点:
String str = "hello" + "world";
StringBuilder stringBuilder = new StringBuilder().append("hello").append("world");
前者要比后者快得多。因为前者这段代码等同于:
String str = "helloworld";
StringBuffer
源码位置(Android O):/libcore/ojluni/src/main/java/java/lang/StringBuffer.java
StringBuffer是一个类似于String的字符缓冲区,且为字符串变量;在实际的使用中,最常用的是它的append()方法和toString()方法,先用append()方法将字符串拼接,返回的时候用toString()方法将该拼接完成的对象以字符串的形式返回。
因为StringBuffer为字符串变量,那么他的拼接速度肯定是要比String要快了,但刚要打开它的源码,就发现映入眼帘的synchronized关键字,存在这些互斥,它的性能肯定是要受很大影响的。
StringBuilder
源码位置(Android O):/libcore/ojluni/src/main/java/java/lang/StringBuilder.java
StringBuilder的源码好像与StringBuffer的差距并不是很大,但与StringBuffer差距较明显的是,synchronized关键字都没有了。
缺少了synchronized关键字,StringBuilder方法的运行便不受互斥的影响,有调用即可运行,那它的性能肯定是要比StringBuffer的速度要快。当然,有得也有失,synchronized关键字可以保证在多线程的情况下保持线程安全,所以,StringBuilder只能在单线程中使用,在多线程中,必须使用StringBuffer。
三者运行速度的对比
StringBuilder > StringBuffer > String
因为String是常量,无法完成拼接字符串,只能不停创建新对象和回收老对象,所以速度最慢;
StringBuffer虽然是字符串变量,但它的方法里做了很多互斥,一个方法结束后,将锁释放,再进行下一步操作,所以比String快,但还不够快;
StringBuilder既是字符串变量,在方法中也不像StringBuffer那样做很多限制,所以速度最快。
三者线程安全的对比
String、StringBuffer为线程安全的;StringBuilder为线程不安全的。
String为不可变量,所有的不可变量都是线程安全的,所以String线程也是线程安全的;
StringBuffer加了那么多synchronized,为此消耗了很多的性能,线程也是安全的;
StringBuilder因为什么都没干,得到了速度,失去的就是线程的安全。
结论
String:适合用在对字符操作少的情况;
StringBuffer:适合在多线程的情况下对字符串进行大量的操作;
StringBuilder:适合在单线程的情况下对字符串进行大量操作。