The difference between String class, StringBuffer class and StringBuilder class in java11

Light ink @ ~ non-trace 2022-08-06 14:04:42 阅读数:742

differencestringclassstringbufferclass

String类

基础

所属包:

package java.lang;

继承和实现关系

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {

}

它实现了Serializable, Comparable, CharSequence接口,类被final关键字修饰,所以类不能不能被继承,并且是线程安全的.

实现的类图如下:
在这里插入图片描述
成员变量

// 该值用于字符存储
@Stable
// 源码为java11的,在java8中,String的底层是字符数组: private final char value[];
private final byte[] value;
// The encoding identifier used to encode the bytes
private final byte coder;
// 缓存字符串的哈希码
private int hash;
// 使用 JDK 1.0.2 中的 serialVersionUID 以实现互操作性
private static final long serialVersionUID = -6849794470754667710L;
// If string compression is disabled,则 code value The bytes in always start with UTF16 编码
static final boolean COMPACT_STRINGS;
// 默认是true
static {

COMPACT_STRINGS = true;
}
//用于忽略大小写得比较两个字符串.
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];

构造方法

无参构造

 public String() {

this.value = "".value;
this.coder = "".coder;
}

有参构造: 参数为String字符串

 @HotSpotIntrinsicCandidate
public String(String original) {

this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}

有参构造: 参数为字符数组,Encapsulates private constructors,将 char[] The value is stored into the representation of each byte byte[] 中对应字符的 8 个低位.

 public String(char value[]) {

this(value, 0, value.length, null);
}
// this指针调用方法
String(char[] value, int off, int len, Void sig) {

if (len == 0) {

this.value = "".value;
this.coder = "".coder;
return;
}
if (COMPACT_STRINGS) {

byte[] val = StringUTF16.compress(value, off, len)
if (val != null) {

this.value = val;
this.coder = LATIN1;
return;
}
}
this.coder = UTF16;
this.value = StringUTF16.toBytes(value, off, len);
}

有参构造方法: 参数为字符数组,偏移值,和长度,分配一个新的String,which contains characters from a subarray of the character array parameter.offset参数是子数组第一个字符的索引,count 参数指定子数组的长度.子数组的内容被复制;随后对字符数组的修改不会影响新创建的字符串.

public String(char value[], int offset, int count) {

this(value, offset, count, rangeCheck(value, offset, count));
}
private static Void rangeCheck(char[] value, int offset, int count) {

checkBoundsOffCount(offset, count, value.length);
return null;
}
static void checkBoundsOffCount(int offset, int count, int length) {

if (offset < 0 || count < 0 || offset > length - count) {

throw new StringIndexOutOfBoundsException(
"offset " + offset + ", count " + count + ", length " + length);
}
}

String类中常用方法

length: 返回此字符串的长度.长度等于字符串中Unicode 代码单元的数量.

public int length() {

return value.length >> coder();
}

isEmpty: 长度为0,判断当前字符串是否为空

 public boolean isEmpty() {

return value.length == 0;
}

charAt: 返回指定索引处的char值.索引范围从0 到 length() - 1.序列的第一个 char值在索引 0 处,下一个在索引 1 处,依此类推,与数组索引一样.

 public char charAt(int index) {

if (isLatin1()) {

return StringLatin1.charAt(value, index);
} else {

return StringUTF16.charAt(value, index);
}
}

getByte: 获取字节数,使用命名字符集将此 String 编码为字节序列,将结果存储到新的字节数组中.未指定此字符串无法在给定字符集中编码时此方法的行为.

 public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {

if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, coder(), value);
}
public byte[] getBytes(Charset charset) {

if (charset == null) throw new NullPointerException();
return StringCoding.encode(charset, coder(), value);
}
public byte[] getBytes() {

return StringCoding.encode(coder(), value);
}

equal: 判断是否相等,将此字符串与指定对象进行比较.结果是true当且仅当参数不是 null 并且是一个String 对象,Represents the same sequence of characters as this object.

 public boolean equals(Object anObject) {

if (this == anObject) {

return true;
}
if (anObject instanceof String) {

String aString = (String)anObject;
if (coder() == aString.coder()) {

return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}

compareTo: 按字典顺序比较两个字符串.比较基于字符串中每个字符的 Unicode 值.此 String对象表示的字符序列按字典顺序与参数字符串表示的字符序列进行比较.如果此String对象按字典顺序位于参数字符串之前,则结果为负整数.如果此String对象按字典顺序跟随参数字符串,则结果为正整数.如果字符串相等,则结果为零;当 equals(Object)方法返回true时,compareTo返回 code 0.

 public int compareTo(String anotherString) {

byte v1[] = value;
byte v2[] = anotherString.value;
if (coder() == anotherString.coder()) {

return isLatin1() ? StringLatin1.compareTo(v1, v2)
: StringUTF16.compareTo(v1, v2);
}
return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
: StringUTF16.compareToLatin1(v1, v2);
}

startsWith: 测试此字符串是否以指定的前缀开头.

 public boolean startsWith(String prefix) {

return startsWith(prefix, 0);
}
public boolean startsWith(String prefix, int toffset) {

// Note: toffset might be near -1>>>1.
if (toffset < 0 || toffset > length() - prefix.length()) {

return false;
}
byte ta[] = value;
byte pa[] = prefix.value;
int po = 0;
int pc = pa.length;
if (coder() == prefix.coder()) {

int to = isLatin1() ? toffset : toffset << 1;
while (po < pc) {

if (ta[to++] != pa[po++]) {

return false;
}
}
} else {

if (isLatin1()) {
 // && pcoder == UTF16
return false;
}
// coder == UTF16 && pcoder == LATIN1)
while (po < pc) {

if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) {

return false;
}
}
}
return true;
}
// 测试此字符串是否以指定的后缀结尾.
public boolean endsWith(String suffix) {

return startsWith(suffix, length() - suffix.length());
}

hashCode: 返回此字符串的哈希码.String 对象的哈希码计算为s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

 public int hashCode() {

int h = hash;
if (h == 0 && value.length > 0) {

hash = h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
}
return h;
}

indexOf(): 判断字符从0开始位于字符串的哪个位置

public int indexOf(int ch) {

return indexOf(ch, 0);
}
public int indexOf(int ch, int fromIndex) {

return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex)
: StringUTF16.indexOf(value, ch, fromIndex);
}

lastIndexOf(): 判断字符从字符串尾部开始位于字符串的哪个位置

public int lastIndexOf(int ch) {

return lastIndexOf(ch, length() - 1);
}
public int lastIndexOf(int ch, int fromIndex) {

return isLatin1() ? StringLatin1.lastIndexOf(value, ch, fromIndex)
: StringUTF16.lastIndexOf(value, ch, fromIndex);
}

substring: 截取字符串中指定位置的字符

 public String substring(int beginIndex) {

if (beginIndex < 0) {

throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = length() - beginIndex;
if (subLen < 0) {

throw new StringIndexOutOfBoundsException(subLen);
}
if (beginIndex == 0) {

return this;
}
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
: StringUTF16.newString(value, beginIndex, subLen);
}
public String substring(int beginIndex, int endIndex) {

int length = length();
checkBoundsBeginEnd(beginIndex, endIndex, length);
int subLen = endIndex - beginIndex;
if (beginIndex == 0 && endIndex == length) {

return this;
}
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
: StringUTF16.newString(value, beginIndex, subLen);
}

concat(): 拼接字符串

public String concat(String str) {

if (str.isEmpty()) {

return this;
}
if (coder() == str.coder()) {

byte[] val = this.value;
byte[] oval = str.value;
int len = val.length + oval.length;
byte[] buf = Arrays.copyOf(val, len);
System.arraycopy(oval, 0, buf, val.length, oval.length);
return new String(buf, coder);
}
int len = length();
int olen = str.length();
byte[] buf = StringUTF16.newBytesFor(len + olen);
getBytes(buf, 0, UTF16);
str.getBytes(buf, len, UTF16);
return new String(buf, UTF16);
}

replace(): 将源字符替换成目标字符

 public String replace(char oldChar, char newChar) {

if (oldChar != newChar) {

String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar)
: StringUTF16.replace(value, oldChar, newChar);
if (ret != null) {

return ret;
}
}
return this;
}
// 用指定的文字替换序列替换此字符串中与文字目标序列匹配的每个子字符串.
// 替换从字符串的开头到结尾进行,例如,将字符串“aaa”中的“aa”替换为“b”将导致“ba”而不是“ab”.
public String replace(CharSequence target, CharSequence replacement) {

String tgtStr = target.toString();
String replStr = replacement.toString();
int j = indexOf(tgtStr);
if (j < 0) {

return this;
}
int tgtLen = tgtStr.length();
int tgtLen1 = Math.max(tgtLen, 1);
int thisLen = length();
int newLenHint = thisLen - tgtLen + replStr.length();
if (newLenHint < 0) {

throw new OutOfMemoryError();
}
StringBuilder sb = new StringBuilder(newLenHint);
int i = 0;
do {

sb.append(this, i, j).append(replStr);
i = j + tgtLen;
} while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);
return sb.append(this, i, thisLen).toString();
}
// Use regex to match and replace all characters
public String replaceAll(String regex, String replacement) {

return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}

matches: Checks whether this string matches the given regular expression

 public boolean matches(String regex) {

return Pattern.matches(regex, this);
}

contains: 当且仅当此字符串包含指定的 char 值序列时才返回 true.

 public boolean contains(CharSequence s) {

return indexOf(s.toString()) >= 0;
}

spilt: Split a string according to a regular expression,Split into multiple strings and form an array

 public String[] split(String regex) {

return split(regex, 0);
}
public String[] split(String regex, int limit) {

/* fastpath if the regex is a (1)one-char String and this character is not one of the RegEx's meta characters ".$|()[{^?*+\\", or (2)two-char String and the first char is the backslash and the second is not the ascii digit or ascii letter. */
char ch = 0;
if (((regex.length() == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{

int off = 0;
int next = 0;
boolean limited = limit > 0;
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {

if (!limited || list.size() < limit - 1) {

list.add(substring(off, next));
off = next + 1;
} else {
 // last one
//assert (list.size() == limit - 1);
int last = length();
list.add(substring(off, last));
off = last;
break;
}
}
// If no match was found, return this
if (off == 0)
return new String[]{
this};
// Add remaining segment
if (!limited || list.size() < limit)
list.add(substring(off, length()));
// Construct result
int resultSize = list.size();
if (limit == 0) {

while (resultSize > 0 && list.get(resultSize - 1).isEmpty()) {

resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}

区别

汇总

String类是不可变类,当一个String对象被创建,then the sequence of characters contained in the object is immutable,直至对象被销毁;StringBufferobject represents a mutable string object,且线程安全;StringBuilderClasses represent mutable string objects,且非线程安全

区别一

String是final类不能被继承且为字符串常量,而StringBuilder和StringBuffer均为字符串变量.

在Java中字符串使用String类进行表示,但是String类表示字符串有一个最大的问题:“字符串常量一旦声明则不可改变,而字符串对象可以改变,但是改变的是其内存地址的指向.”所以String类不适合于频繁修改的字符串操作上,所以在这种情况下,往往可以使用StringBuffer类,即StringBuffer类方便用户进行内容修改.

public static void main(String[] args) {

String str="123";
System.out.print("String字符串常量str:"+str+"的初始地址是:");
System.out.println(str.hashCode());
str=str+"456";
System.out.print("String字符串常量str:"+str+"The address after the assignment is :");
System.out.println(str.hashCode());
StringBuffer sb=new StringBuffer().append("123");
System.out.print("StringBuffer字符串变量sb:"+sb+"的初始地址是:");
System.out.println(sb.hashCode());
sb.append("456");
System.out.print("StringBuffer字符串变量sb:"+sb+"The address after assignment is:");
System.out.println(sb.hashCode());
StringBuilder sbd=new StringBuilder().append("123");
System.out.print("StringBuilder字符串变量sbd:"+sbd+"的初始地址是:");
System.out.println(sbd.hashCode());
sbd.append("456");
System.out.print("StringBuilder字符串变量sbd:"+sbd+"The address after assignment is:");
System.out.println(sbd.hashCode());
}

结果: String类型是字符串常量,when doing string operations,The address changes.StringBuffer和StringBuilderThe type is a string variable,当使用apend()等操作时,内存地址不发生变化
在这里插入图片描述

区别二:

Class definitions are different,It can be found through three defining structures,String、StringBuffer和StringBuilder类都是CharSequence接口的子类,也就证明String、StringBuffer和StringBuilderObjects of a class can take advantage of the automatic upcast operationCharSequence接口实例化.
String类:

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {

}

StringBuilder类:

public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
{

}

StringBuffer类:

public final class StringBuffer
extends AbstractStringBuilder
implements Serializable, Comparable<StringBuffer>, CharSequence
{

String类

String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁.
实例代码:

 public static void main(String[] args) {

String a = "abcd";
a = "123";
System.out.println(a);
}

结果:
在这里插入图片描述
不是说不可变吗,Why can the assignment be successful?Actually this is a new object reference.You can understand it by viewing the compiled source code.Or the picture below:可以看出来,再次给a赋值时,并不是对原来堆中实例对象进行重新赋值,而是生成一个新的实例对象,并且指向“456”这个字符串,a则指向最新生成的实例对象,之前的实例对象仍然存在,如果没有被再次引用,则会被垃圾回收.
在这里插入图片描述

StringBuffer 类

StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列.一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象.
实例代码:

 public static void main(String[] args) {

StringBuffer sb=new StringBuffer().append("123");
System.out.print("StringBuffer字符串变量sb:"+sb+"的初始地址是:");
System.out.println(sb.hashCode());
sb.append("456");
System.out.print("StringBuffer字符串变量sb:"+sb+"The address after assignment is:");
System.out.println(sb.hashCode());
}

结果: 可以证明StringBuffer对象是一个字符序列可变的字符串,它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串.
在这里插入图片描述

StringBuilder类

StringBuilder类也代表可变字符串对象.实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同.不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高.
StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全.
实例代码:

 public static void main(String[] args) {

StringBuilder sbd=new StringBuilder().append("123");
System.out.print("StringBuilder字符串变量sbd:"+sbd+"的初始地址是:");
System.out.println(sbd.hashCode());
sbd.append("456");
System.out.print("StringBuilder字符串变量sbd:"+sbd+"The address after assignment is:");
System.out.println(sbd.hashCode());
}

结果:
在这里插入图片描述

java8Improvements in future versions

Java9改进了字符串(包括String、StringBuffer、StringBuilder)的实现.在Java9以前字符串采用char[]数组来保存字符,因此字符串的每个字符占2字节;而Java9的字符串采用byte[]数组再加一个encoding-flag字段来保存字符,因此字符串的每个字符只占1字节.所以Java9的字符串更加节省空间,字符串的功能方法也没有受到影响.

copyright:author[Light ink @ ~ non-trace],Please bring the original link to reprint, thank you. https://en.javamana.com/2022/218/202208061355338316.html