做为做为一个已经入了门的java程序猿,确定对Java中的String、StringBuffer和StringBuilder都略有耳闻了,尤为是String 确定是常常用的。但确定你有一点很好奇,为何java中有三个关于字符串的类?一个不够吗!先回答这个问题,黑格尔曾经说过——存在必合理,单纯一个String确实是不够的,因此要引入StringBuffer。再后来引入StringBuilder是另外一个故事了,后面会详细讲到。
要了解为何,咱们就得先来看下这三者各自都有什么样的特色,有什么样的异同,对其知根知底以后,一切谜团都会被解开。java
点开String的源码,能够发现String被定义为final类型,意味着它不能被继承,再仔细看其提供的方法,没有一个能对原始字符串作任何操做的,有几个开启了貌似是操做原字符串的,好比replaceFirst replaceAll,点进去一看,实际上是从新生成了一个新的字符串,对原始内容没有作任何修改。
是的,从实现的角度来看,它是不可变的,全部String的变动其实都会生成一个新的字符串,比String str = "abcdefghijklmnopqrstuvwxy"; str = str + "z";
以后新生成的a-z并不包含原来的a-y,原来的a-y已经变成垃圾了。简单归纳,只要是两个不一样的字符,确定都是两个彻底不一样不相关的对象,即使其中一个是从另外一个subString出来的,两个也没有任何关系。 若是是两个相同的字符串,状况比较复杂,多是同一份也可能不是。若是在JVM中使用G1gc,并且开启-XX:+UseStringDeduplication
,JVM会对字符串的存储作优化,因此若是你的服务中有大量相同字符串,建议开启这个参数。
Java做为一个非纯面向对象的语言,除了提供分装对象外,也提供了一些原始类型(好比:int long double char),String的使用竟然能够像用原始类型同样不须要new,直接String str = "a"
这样声明,我以为String更像是面向对象和非面向对象结合的一个产物。
String最大的特色就是 __ 不可变__,这是它的优势,由于不可变意味着使用简单,没有线程安全的问题。 但这也是它的缺点,由于每次变动都会生成一个新的字符串,明显太浪费空间了。git
我以为StringBuffer是彻底由于String的缺点而生的。咱们平常使用String的过程当中,确定常常会用到字符串追加的状况,按String的实现,没次追加即使只是一个字符,都是生成一个彻底不一样的对象,若是此次操做很频繁不少的话会大幅提升内存的消耗,而且增长gc的压力。对于这种问题,StringBuffer是如何解决的呢?咱们直接从源码上来看。
但看StringBuffer里,几乎全部的方法都会调super父类,其实它全部的实现都是在AbstractStringBuilder里的。鉴于咱们对其最长用的方法是append,因此咱们就从append入手,其实append也是StringBuffer比较核心的功能。数组
/** * The value is used for character storage. */ char[] value; AbstractStringBuilder(int capacity) { value = new char[capacity]; } public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } private int newCapacity(int minCapacity) { // overflow-conscious code int newCapacity = (value.length << 1) + 2; if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; }
原来是StringBuffer父类AbstractStringBuilder有个char数组value,用来存放字符串全部的字符,StringBuffer默认初始大小是16。StringBuffer在每次append的时候,若是value的容量不够,就会申请一个容量比当前所需大一倍的字符数组,而后把旧的数据拷贝进去。这种一次性扩容一倍的方式,在咱们以前HashMap源码浅析中已经看到过了。一次性多申请内存,虽然看起来会有大段的内存空闲,但其实能够减小String append时频繁建立新字符串的问题。
因此记住,若是你代码中对String频繁操做,千万不用用String而是选择用StringBuffer或者咱们下面要讲的StringBuilder。还有一个优化点,若是你能提早知道你字符串最大的长度,建议你在建立StringBuffer时指定其capacity,避免在append时执行ensureCapacityInternal,从而提高性能。
对于StringBuffer还有一个点没提到,注意看它源码的全部方法,除构造函数外,全部的方法都被__synchronized__修饰,意味着它是有个线程安全的类,全部操做查询方法都会被加同步,可是若是咱们只是单线程呢,想用StringBuffer的优点,但又以为加同步太多余,太影响性能。这个时候就轮到StringBuilder上场了。安全
StringBuilder从类图上看和StringBuffer彻底没有任何区别,再打开它的源码,和StringBuffer同样几乎啥逻辑都没有,全是调调super父类AbstractStringBuilder,它和StringBuffer最大的区别就是全部方法没有用synchronized修复,它不是一个线程安全的类,但也意味着它没有同步,在单线程状况下性能会优于StringBuffer。多线程
看完上面内容,我以为你应该知道上面时候用String、何时用StringBuffer、何时用StringBuilder了。app
咱们来看个比较底层的东西,是关于jvm对String优化的,如今有以下代码。jvm
public class StringTest { public static void main(String[] args) { String str = "abc"; str = str + "d"; str = str + "e"; } }
咱们用javac StringTest.java
编译成class文件,而后用 javap -c StringTest
生成字节码,内容以下函数
public class StringTest { public StringTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String abc 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String d 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_1 23: return } ➜ java git:(master) ✗ javap -c StringTest Compiled from "StringTest.java" public class StringTest { public StringTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String abc 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String d 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_1 23: return } ➜ java git:(master) ✗ javac StringTest.java ➜ java git:(master) ✗ javap -c StringTest Compiled from "StringTest.java" public class StringTest { public StringTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String abc 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String d 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_1 23: new #3 // class java/lang/StringBuilder 26: dup 27: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 30: aload_1 31: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: ldc #8 // String e 36: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 42: astore_1 43: return }
其实能够看出,java底层实现字符串+的时候实际上是用StringBuilder的append()来实现的,若是有字符串的连续+,jvm用StringBuilder append也能够实现优化。性能
备注:源码来自JDK11优化
本文由博客一文多发平台 OpenWrite 发布!