String/StringBuffer/StringBuilder的区别及源码分析
1.String详解
- String 底层是一个最终类,即字符串是一个常量,不可变也不可继承,且String底层是一个不可变的char数组,所以每次对字符串的操作都为创建一个新的数组
- String实现的接口Serializable是序列化的标识,仅表示序列化语义;Comparable接口用于实现对象的自然排序,该接口只有一个方法:compareTo()方法,用于比较对象,小于返回负整数,等于返回0,大于返回正整数
- String实现的接口 CharSequence(可读字符序列),可以实现对不同类型的char数组只读访问,StringBuffer和StringBuilder也实现了这个接口
package java.lang;import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];/** Cache the hash code for the string */private int hash; // 哈希值的缓冲,默认为o
- String支持多种参数类型的初始化方法,但本质上就是将接收的参数传递给成员变量value[],由于String内部实际上是由char[]数组实现的,所以String的方法本质上都是在内部调用数组的方法
/*** Initializes a newly created {@code String} object so that it represents* the same sequence of characters as the argument; in other words, the* newly created string is a copy of the argument string. Unless an* explicit copy of {@code original} is needed, use of this constructor is* unnecessary since Strings are immutable.** @param original* A {@code String}*/public String(String original) {this.value = original.value;this.hash = original.hash;}
/*** Allocates a new {@code String} so that it represents the sequence of* characters currently contained in the character array argument. The* contents of the character array are copied; subsequent modification of* the character array does not affect the newly created string.** @param value* The initial value of the string*/public String(char value[]) {this.value = Arrays.copyOf(value, value.length);}
-
1.1.equals()和HashCode()方法
- equals()方法先比较地址值,如果地址值即引用相同,返回true;String重写了equals方法,加上比较两个对象的内容,如果长度相遍历每个字符相等且顺序相同则返回true,所以equals方法是String能广泛用于Map[key,value]中的key的关键所在,另外String也提供了contentEquals()方法只用于比较内容
- HashCode()方法,由于String封装了一个哈希值的缓存hash,当对象不是新创建时直接调用hash将哈希值赋给对象,新建对象的hash缓存默认为0,需要根据哈希公式计算,Java 底层提供了一个计算公式(使用质数31可以有效的减少哈希值计算相同导致的哈希碰撞)
//String的equals()方法,比较的是地址值或内容
/*** Compares this string to the specified object. The result is {@code* true} if and only if the argument is not {@code null} and is a {@code* String} object that represents the same sequence of characters as this* object.** @param anObject* The object to compare this {@code String} against** @return {@code true} if the given object represents a {@code String}* equivalent to this string, {@code false} otherwise** @see #compareTo(String)* @see #equalsIgnoreCase(String)*/public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}//String的HashCode方法,使用了hash缓存,hash是String实例化的HashCode的一个缓存,因为字符串常用于比较,将HashCode缓存在hash中,进行比较时就不需要重新计算哈希值,即可直接比较,提高程序的效率/*** Returns a hash code for this string. The hash code for a* {@code String} object is computed as* * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]*
* using {@code int} arithmetic, where {@code s[i]} is the* ith character of the string, {@code n} is the length of* the string, and {@code ^} indicates exponentiation.* (The hash value of the empty string is zero.)** @return a hash code value for this object.*/public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}
1.2.字符串常量池
String—亨元模式(Flyweigth):可以理解为缓存(cache)是一种设计模式或者说优化策略
-
常量池(Constant pool)被确定的,并且直接保存在已编译的class文件中的一些数据,包括类,方法,接口中的常量以及字符串常量等,程序执行时,常量池会储存在方法区.
-
JVM在内存中专门开辟了一块内存区域用于存放字符串对象,成为字符串常量池,在Java1.7以后字符串常量池移到了堆内存区域
在JDK6.0及之前版本中,String Pool里放的都是字符串常量 在JDK7.0中,由于String#intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用. -
Java中的八种基本类型以及String类型,由于会频繁使用这些类型,为了使其运行更快,节省内存,Java引入了常量池的概念,常量池就是类似一个Java提供的缓存
1.3.Java中有两种创建字符串的方式:
String a = "Hello World";
String b = "Hello World";
System.out.print(a==b);//true
1.采用字面值创建String对象,JVM会先去字符串常量池中查找是否有 "Hello World"这个对象,如果没有则在池中创建一个 "Hello World"对象,然后将这个对象的引用赋给a,a就会指向池中的 "Hello World"对象,但创建b对象时,由于JVM在池中找到了 "Hello World"对象,所以JVM会将池中这个对象的引用直接赋给b,这样a,b两个引用都指向了同一个对象,所以a==b为true
String c = new String("Hello World");
String d = new String("Hello World");
System.out.print(c==d);//false
2.采用new创建对象,JVM会先去字符串常量池中查找是否有 "Hello World"这个对象,如果没有则在池中创建一个 "Hello World"对象,并将引用指向堆中,然后new String在堆中开辟了一个空间,栈中的引用a指向这个空间,每new一次都会在堆中开辟一个空间,所以b指向了堆中新开辟的new String空间,c,d在堆中的地址值不同,即c==d为false.

-
String字面值运算时,并不会把每个String对象放入池中,只会把计算结果放入池中
-
JVM对字符串常量池的优化可以总结为:未声明放结果,已声明放引用,所以字符串常量池中可以同时放字符串常量和引用
2.AbstractStringBuilder
AbstractStringBuilder 在java.lang 包中,是一个抽象类,实现 Appendable 接口和 CharSequence 接口,这个类的诞生是为了解决 String 类在创建对象的时候总是创建新的对象的问题的。AbstractStringBuilder 有两个子类,分别是 StringBuilder 和 StringBuffer.
AbstractStringBuilder类和String类很类似,String类内部维护了一个char数组,用final修饰,即不可变.
AbstractStringBuilder类中同样维护了一个char数组但是是个变量,即是可变数组,这就是为了解决String每次操作都要创建新对象的关键所在.
package java.lang;import sun.misc.FloatingDecimal; import java.util.Arrays; abstract class AbstractStringBuilder implements Appendable, CharSequence {/*** The value is used for character storage.*/char[] value;//可变数组/*** The count is the number of characters used.*/int count;//计数/*** This no-arg constructor is necessary for serialization of subclasses.*/AbstractStringBuilder() {//无参构造}/*** Creates an AbstractStringBuilder of the specified capacity.*/AbstractStringBuilder(int capacity) {//含参构造value = new char[capacity];}/*** Returns the length (character count).** @return the length of the sequence of characters currently* represented by this object*/2.1.主要方法:
1.length(),返回char[]的实际长度(count),重写的CharSequence接口的length()方法.
2.capacity():capacity意思是’容量’,即得到目前该value数组的实际大小
@Overridepublic int length() {return count;}public int capacity() {return value.length;//初始容量为16}StringBuilder builder = new StringBuilder("adadsagfaesgsa");System.out.println(builder.length());//14,数组的长度System.out.println(builder.capacity());//30,数组的容量,不够时自动扩容(扩容机制:容量不够用时,先将当前容量+1的二倍(newCapacity)与需要的容量(minimumCapacity)比较如果比需要的容量大,那就将容量扩大到容量+1的二倍;如果比需要的容量小,那就直接扩大到需要的容量。)3.toString是AbstractStringBuilder抽象类唯一的一个抽象方法;唯一的一个final方法:getValue(),得到value数组。可以对其直接操作
@Overridepublic abstract String toString();//继承自Object类final char[] getValue() {return value;}4.append()方法—AbstractStringBuilder类及其子类中最重要的操作;Appendable接口的具体实现,源码中所有的append()方法返回值都是原类型AbstractStringBuilder,所以append()可以无限调用.
/*append(Object obj):利用Object(或任何对象)的toString方法转成字符串然后添加到该value[]中*/public AbstractStringBuilder append(Object obj) {return append(String.valueOf(obj));}/*public static String valueOf(Object obj) {return (obj == null) ? "null" : obj.toString();}*//**/public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);//扩容str.getChars(0, len, value, count);//将原数组拼接到目标数组(value从str下标为0处开始拼接count长度)count += len;return this;}// Documentation in subclasses because of synchro differencepublic AbstractStringBuilder append(StringBuffer sb) {if (sb == null)return appendNull();int len = sb.length();ensureCapacityInternal(count + len);sb.getChars(0, len, value, count);count += len;return this;}// Documentation in subclasses because of synchro difference@Overridepublic AbstractStringBuilder append(CharSequence s) {if (s == null)return appendNull();if (s instanceof String)//判断s是否是String的实例return this.append((String)s);if (s instanceof AbstractStringBuilder)return this.append((AbstractStringBuilder)s);return this.append(s, 0, s.length());}private AbstractStringBuilder appendNull() {int c = count;ensureCapacityInternal(c + 4);final char[] value = this.value;//如果str 是 null,就賦予str = "null",即字符串长度为4value[c++] = 'n';value[c++] = 'u';value[c++] = 'l';value[c++] = 'l';count = c;return this;}5.setCharAt(int index, char ch):直接设置下标为index的字符为ch
public void setCharAt(int index, char ch) {if ((index < 0) || (index >= count))throw new StringIndexOutOfBoundsException(index);value[index] = ch;}6.replace(int start, int end, String str):用字符串str替换掉value[]数组的[start,end)部分–左闭右开
public AbstractStringBuilder replace(int start, int end, String str) {if (start < 0)throw new StringIndexOutOfBoundsException(start);if (start > count)throw new StringIndexOutOfBoundsException("start > length()");if (start > end)throw new StringIndexOutOfBoundsException("start > end");if (end > count)end = count;int len = str.length();int newCount = count + len - (end - start);ensureCapacityInternal(newCount);System.arraycopy(value, end, value, start + len, count - end);str.getChars(value, start);count = newCount;return this;}7.insert(int index, char str[], int offset,int len):在value[]的下标为index位置插入数组str的一部分[offset,offest+len)–左闭右开
public AbstractStringBuilder insert(int offset, Object obj) {return insert(offset, String.valueOf(obj));//插入数组} public AbstractStringBuilder insert(int index, char[] str, int offset,int len){if ((index < 0) || (index > length()))throw new StringIndexOutOfBoundsException(index);if ((offset < 0) || (len < 0) || (offset > str.length - len))throw new StringIndexOutOfBoundsException("offset " + offset + ", len " + len + ", str.length "+ str.length);ensureCapacityInternal(count + len);//扩容System.arraycopy(value, index, value, index + len, count - index);System.arraycopy(str, offset, value, index, len);//数组增加count += len;return this;}8.在value[]查找字符串str,返回其下标
public int indexOf(String str) {return indexOf(str, 0);//返回str在value[]第一次出现的下标值}public int lastIndexOf(String str) {return lastIndexOf(str, count);//返回str在value[]最后一次出现的下标值}9.删除方法,删除value[]中下标为[start,end)的一段数组,左闭右开
public AbstractStringBuilder delete(int start, int end) {if (start < 0)throw new StringIndexOutOfBoundsException(start);if (end > count)end = count;if (start > end)throw new StringIndexOutOfBoundsException();int len = end - start;if (len > 0) {System.arraycopy(value, start+len, value, start, count-end);count -= len;//删除指定长度数组}return this;}3.StringBuilder
StringBuilder类继承自AbstractStringBuilder类,是一个可变的字符序列(char[] value,可变数组),相对于String,有其特有的append(),insert(),delete()方法,由于StringBuilder的字符串拼接调用的是append()方法,所以不会产生大量的临时新对象,提高了拼接效率,线程不安全,在JDK1.5之后引入,也是作为String对象在进行"+"操作时,其实本质上就是调用的StringBuilder的append()方法.
String a = "HelloWorld";String b = "Hello" + "World";String c = "Hello";String d = "World";String e = c + d; cSystem.out.println(a);//输出为HelloWorldSystem.out.println(e);//HelloWorldSystem.out.println(a == e);//false,两个String对象+操作时,底层调用的是StringBuiler的append()方法,先拼接Hello,再拼接上World,返回的结果e存入一个新的对象,所以说String字符串引用相加的计算效率要比直接相加的效率低System.out.println(a == b);//trueSystem.out.println(b == e);//false
public final class StringBuilderextends AbstractStringBuilderimplements java.io.Serializable, CharSequence{public StringBuilder() {super(16);//初始化容量public StringBuilder(int capacity) {super(capacity);}public StringBuilder(String str) {super(str.length() + 16);append(str);//使用str初始化,容量str大小的基础上加16}@Overridepublic StringBuilder append(String str) {super.append(str);return this;}
}
4.StringBuffer
StringBuffer和StringBuilder都是继承自AbstractStringBuilder类(char[] value,可变数组),两者实现的方法功能基本一致,不过StringBuffer的方法都是被 synchronized关键字修饰,所以StringBuffer是线程安全的,但是保证安全的同时牺牲了效率(同步锁),所以效率:StringBuilder>StringBuffer.
public final class StringBufferextends AbstractStringBuilderimplements java.io.Serializable, CharSequence{@Overrideprivate transient char[] toStringCache;public synchronized StringBuffer append(String str) {//同步关键字--线程安全toStringCache = null;super.append(str);return this;}}
总结:1.String,StringBuilder和StringBuffer的区别
| String | StringBuilder | StringBuffer |
|---|---|---|
| 最终类不可继承 | 最终类不可继承 | 最终类不可继承 |
| String字符串是常量不可变 | 变量可变 | 变量可变 |
| 线程不安全 | 线程安全 | |
| 进行大量字符串拼接时,回新建大量临时对象,浪费内存空间且效率低下 | 采用append()拼接字符串,可变数组拼接,效率高,常用于字符串拼接 | 与StirngBuilder相比,加入了同步锁(synchronized),保证了线程安全,当降低了效率 |
| String继承自Object类 | StringBuilder继承自AbstractStringBuilder类 | StringBuffer继承自AbstractStringBuild |
2.三者的继承关系:

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
