一:String
涉及String提到的应该就是太监类
、定长数组实现
、字面量与对象比较
这三方面,接下来就是从继承结构、主要属性、方法、构造函数解析这个类
1.1 继承结构
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
实现Serializable
、Comparable
接口,可序列化标志性接口与比较器接口
,最后一个比较陌生的CharSequence
接口个人理解为字符串描述接口
,查看String、StringBuilder、StringBuffer源码发现都实现了该接口。针对实现CharSequence接口最大的作用莫过于在StringBuilder类的append()方法参数为CharSequence,这就不难理解为什么会把CharSequence接口描述为字符串描述接口
1.2 主要属性
private final char value[];
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
// 最后在返回String的时候是new创建
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
注意保存数据的value属性为char类型数组
,数组是不可更改的数据结构
,所以String创建后的任何修改操作都会产生新对象,例如subString()。少数字符串拼接修改操作可以使用String,频繁亦或是大规模改动使用StringBuilder
1.3 构造函数
String提供六种对应构造,参数分别为空、String、char[]、int[]、byte[]、StringBuilder/StringBuffer。其中参数为空的构造函数没有任何实际使用意义
,String使用定长数组实现,任何修改都会产生新对象。使用String、StringBuffer/StringBuilder构造为同一原理,只需将对应的value属性取出并进行赋值即可
char[]、int[]数组又类似,value属性类型为char[]自不必多说,int[]类型参数多了一步将int数据转为对应Unicode编码数据再构造新数组
1.4 主要方法
对于String方法描述一点不想重复阐述API,个人理解就分为两类,查询value属性与修改value属性值
,总结大部分方法实现都是对数组操作实现
1.6 字符串比较
- String有
字面量
与对象
两种 - 字面量String位于
常量池
中,相同字面量内存地址引用相同 - String对象内存地址位于
堆
中,相同内容两个对象地址不一致 - intern()方法在jdk1.7开始判断堆地址存在该对象后,将
堆地址引用到常量池保存一份
,故后面所有创建相同内容的String字面量都为同一内存地址
String a1 = "a";
String a2 = "a";
String a3 = new String("a");
String a4 = new String("a");
String a5 = new String("a") + new String("b");
a5.intern();
String a6 = "ab";
System.out.println(a1 == a2); //true
System.out.println(a2 == a3); //false
System.out.println(a3 == a4); //false
System.out.println(a5 == a6); //true
String s = null;
String ss = "";
除了上述使用==判定字符串相等之外,还需要注意字符串的null
与""
,字符串的判定一般都会这两方面同时判断,因为前者代表空对象
会引发空指针异常
后者则代表空字符串
二:StringBuffer/StringBuilder
StringBuilder/StringBuffe是变长数组
,变长即动态扩容是关注重点。当然还有一点就是线程安全
问题
2.1 继承结构
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
abstract class AbstractStringBuilder implements Appendable, CharSequence
StringBuilder/StringBuilder都继承抽象类AbstractStringBuilder,该抽象类继承结构上的作用就是实现接口Appendable,Appendable接口定义append()
方法系列重载。同时类中定义储存元素的char类型value[]
数组以及记录数组使用量count
元素
2.2 构造方法
public StringBuffer() {
super(16);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
......
StringBuilder/StringBuffer实例化默认创建16
长度数组亦或是比String参数长度多16
2.2 变长数组实现
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len); // count表示现在字符串长度
str.getChars(0, len, value, count);
count += len;
return this;
}
append()方法为例,找到对应AbstractStringBuilder类中的该方法,这里首先是判空后求出增加对象字符串长度(CharSequence定义length属性记录字符串长度),将其作为参数与原字符串长度求和传至ensureCapacityInternal()
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
ensureCapacityInternal()方法直接比较字符串长度之和与现有数组长度大小,如果大于现有value数组长度则进入expandCapacity(minimumCapacity)方法
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2; //对现有数组进行扩容
if (newCapacity - minimumCapacity < 0) //判断扩容后容量是否满足
newCapacity = minimumCapacity; //如果过小则采用新字符串所需长度
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity); //对原数组进行赋值并扩容的操作
}
2.3 线程安全
StringBuffer相对于StringBuilder是线程安全
的,这点的实现仅仅是在方法上加synchronized
关键字而已,当然线程安全也就意味着效率上StringBuilder优于StringBuffer
三:StringJoiner
JDK1.8提供具有强大API的字符串操作类,底层采用StringBuilder
实现,线程非安全。在前缀
、后缀
、分隔
方面表现优异
3.1 构造函数
public StringJoiner(CharSequence delimiter) {
this(delimiter, "", "");
}
public StringJoiner(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
Objects.requireNonNull(prefix, "The prefix must not be null");
Objects.requireNonNull(delimiter, "The delimiter must not be null");
Objects.requireNonNull(suffix, "The suffix must not be null");
// make defensive copies of arguments
this.prefix = prefix.toString();
this.delimiter = delimiter.toString();
this.suffix = suffix.toString();
this.emptyValue = this.prefix + this.suffix;
}
前缀prefix
、后缀suffix
、分隔符delimiter
3.2 主要方法
// 底层实现所需的StringBuilder实例
private StringBuilder value;
//在进行添加操作的时候就是调用StringBuilder的append()方法
//方法prepareBuilder就是进行StringBuilder实例化操作
public StringJoiner add(CharSequence newElement) {
prepareBuilder().append(newElement);
return this;
}
//私有方法实例化StringBuilder
private StringBuilder prepareBuilder() {
if (value != null) {
value.append(delimiter); //已进行实例化,需加分隔符
} else {
//未实例化,需要加上前缀
value = new StringBuilder().append(prefix);
}
return value;
}
StringJoiner类的append()方法通过调用StringBuilder的append()方法实现,相较于StringBuilder的区别就在于在进行StringBuilder实例化的时候对prefix、suffix、delimiter进行了处理
3.3实践举例
StringJoiner sj1 = new StringJoiner(",");
sj1.add("1");
sj1.add("2");
StringJoiner sj2 = new StringJoiner(",","prefir","suffix");
sj1.add("add");
System.out.println(sj1); //输出结果为 1,2
System.out.println(sj2); //输出结果为 prefix,add,suffix