前言
这篇文章主要简单的介绍了String
,StringBuffer
,StringBuilder
的区别,并不去刨析底层级
String
初学Java经常会误认为String是Java基本类型,实际上String并非Java基本类型,String本质上是对char数组的封装
//用来存字符串,字符串的本质,是一个final的char型数组
private final char value[];
//缓存字符串的哈希
private int hash; // Default to 0
//实现序列化的标识
private static final long serialVersionUID = -6849794470754667710L;
/**
* 构造方法
* 实质上String 的创建是划分内存,创建字符数组
* Arrays.copyOf()实际调用底层System.arraycopy方法,进行内存划分
**/
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
这里需要注意的重点是:
private final char value[]这是String字符串的本质,是一个字符集合,而且是final的,是不可变的
String类实现的接口
Serializable接口
和Comparable接口
大家都比较熟悉,一个是序列化接口
,一个是比较able类接口
,分别对应
//序列化标记号
private static final long serialVersionUID = -6849794470754667710L;
// 内部比较方法
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
CharSequence接口
这里我们得看一下,为什么常用的String类型
不能算是基本类型?
根据String
的定义来看,字符序列实际的体现就是String
实际内容是char型数组
,只读体现在final上
。final
描述的属性是常量属性
,对于数组来说,其数组引用无法改变。对于这方面的讨论,之后会有详细的讨论。
CharSequence提供了如下方法:
这4种方法也常用到其中subSequence
的实际实现就是调用了subString
方法
此处的toString方法是将实现该接口的对象提供可转换为字符序列的方法,虽然命名上与Object的toString方法重复,但从理念上是不同的
通过上面我们可以知道的重点是:
- String是一个
final类
,既不能被继承
的类; - String类实现了
java.io.Serializable
接口,可以实现序列化
; - String类实现了
Comparable
,可以用于比较大小
(按顺序比较单个字符的ASCII码); - String类实现了
CharSequence
接口,表示是一个有序字符的序列,因为String的本质是一个char类型数组
。
现在让我们创建一个String对象,并操作它,看看可以发现什么:
在上图中我们可以看到,在我创建了一个String类对象之后的HashCode值和我操作之后的HashCode值是不同的
,这说明该类型的值是不可变的,这就意味着我每一次操作这个字符串对象都会生成一个新的String对象,然后将指针指向这个新的对象,然后就可以用原来声明的变量名来访问操作后的这个字符串对象。所以,如果需要大量的操作字符串的话,不建议使用String类型
,因为它会大量的产生新的对象,GC就会工作,这就会影响性能
String常用方法介绍
返回指定位置的字符,从0开始记
public char charAt(int index)
返回指定位置的字符的unicode编码,从0开始记
public int codePointAt(int index)
返回指定位置前一位的字符的unicode编码,从0开始记,其实也可以理解为指定位置字符unicode编码,从1开始记
public int codePointBefore(int index)
查询范围 endIndex-beginIndex
endIndex范围取 0~ length
endIndex范围取 0~ length + 1
public int codePointCount(int beginIndex, int endIndex)
比较大小
从首位开始比较,若不同输出 s - anotherString 值
相同输出0
public int compareTo(String anotherString)
比较大小忽略大小写
public int compareToIgnoreCase(String str)
合并字符串
相当于append
public String concat(String str)
替换字符串
public String replace(char oldChar, char newChar)
正则表达式匹配
public boolean matches(String regex)
判断是否包含
CharSequence接口String及其配套类(StringBuffer、StringBuilfer)实现
public boolean contains(CharSequence s)
替换 正则表达式匹配到的第一个 字符串
public String replaceFirst(String regex, String replacement)
替换 正则表达式匹配到的所有字符串
public String replaceAll(String regex, String replacement)
替换字符串中 target 为 replacement
public String replace(CharSequence target, CharSequence replacement)
StringBuffer 和 StringBuilder
StringBuffer
和StringBuilder
都继承了AbstractStringBuilder
,实际存储方式取消了final修饰符
,所以StringBuffer和StringBuilder的字符数组长度可变且可以重新赋值
// 与String相比,减去了Comparable接口,两个都继承了AbstractStringBuilder
/***
* StringBuffer
*/
public final class StringBuffer extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
/***
* StringBuilder
*/
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
// AbstractStringBuilder的定义如下,其增加了Appendable,对应的就是append方法
abstract class AbstractStringBuilder implements Appendable, CharSequence
// 实际字符数组存储
char[] value;
为了给StringBuilder
和StringBuffer
提供 +
的功能,AbstractStringBuilder
通过Appendable接口
提供了append方法
/**
* 属性,Char 数组
*/
char[] value;
/**
* 属性,标记数组长度
*/
int count;
/**
* 无参数构造方法,子类 序列化必须提供
*/
AbstractStringBuilder() {
}
/**
* 创建一个指定容量的char数组
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
// StringBuffer和StringBuilder都是通过调用父类的构造方法进行构造,默认申请16个长度
public StringBuffer() {
super(16);
}
/**
* 自定义长度申请
*/
public StringBuffer(int capacity) {
super(capacity);
}
StringBuffer
和StringBuilder
实现capacity变换长度
是如何实现的呢?,我还是先dug
String Buffer
和StringBuilder
主要的区别在于,StringBuffer是线程安全的
,StringBuilder非线程安全的
。
String Buffer
由此可见,在我创建了一个StringBuffer
类对象之后的HashCode值
和我操作之后的HashCode值是相同
的,这说明该类的值是可变的,每次的操作都是对对象本身操作,所以不会引发像String
一样的问题。
所以在需要大量操作字符串的时候,建议使用StringBuffer
来创建字符串对象。
然后让我们来看一看StringBuffer
类中的append()方法
:
StringBuilder
由此可见,该类的值也是可变的,其实StringBuilder
类是在 Java 5 中被提出,该类与StringBuffer最大的区别在于,StringBuilder中的方法没有被synchronized关键字修饰
,不是线程安全的。
总结
1.String、StringBuffer、StringBuilder比较。
三者共同之处:都是final类,不允许被继承,主要是从性能和安全性上考虑的,因为这几个类都是经常被使用着,且考虑到防止其中的参数被参数修改影响到其他的应用。
String
:值不可变,影响性能,在对字符串操作较少、单线程情况下使用String
实现了三个接口:Serializable
、Comparable<String>
、CarSequence
StringBuffer
:值可变,线程安全
,在对字符串操作较多、多线程情况下使用StringBuilder
:值可变,线程不安全
,在对字符串操作较多、单线程情况下使用StringBuffer
与StringBuilder
两者共同之处:可以通过append、indert进行字符串的操作
。StringBuffer
和StringBuilder
只实现了两个接口Serializable
、CharSequence
,相比之下String的实例可以通过compareTo方法进行比较
,其他两个不可以。
2.运行速度。
执行速度由快到慢:StringBuilder > StringBuffer > String