String
一、 底层解析
1. JVM底层
在JVM底层中,声明的String变量的地址存储在栈区,而后面的赋值,也就是字符串常量池,在方法区。方法区的常量不重复。
当对字符串进行重新赋值,对现有字符串进行连接操作,调用String的replace方法修改指定字符或字符串时,都需要重新指定内存区域赋值,不能使用原有的内存区域。
String对象的赋值有两种形式:
- String str = "hello"
- String s1 = new String("hello")
这两种形式的区别在于:
直接赋值存储中的是常量池中字符串的地址值
创建对象存储的是堆中对象的地址值,该堆中的对象会指向常量池中的地址值
2. 底层源码
final byte[] value; // String的底层使用byte数组来存储的,final意味着String是不可变的字符序列
public String() {
this.value = "".value;
this.coder = "".coder;
} // String初始化相当于new char[0]
另外所有的方法都不加synchronized锁,所以String是线程不安全的
二、 常用方法
· String toLowerCase():将String中的所有字符转换为小写
· String toUpperCase():将String中的所有字符转换为大写
· String trim():返回字符串的副本,忽略前导空白和尾部空白
· boolean equalsIgnoreCase
· String concat(String str)
· int compareTo(String anotherString)
· boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
· boolean startsWith(String prefix)
· boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
· boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回true
· String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的
· String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串
· String replaceAll(String regex, String replacement):使用给定的replacement替换此字符串所有匹配给定的正则表达式的子字符串
· String replaceFirst(String regex, String replacement):使用给定的replacement替换此字符串匹配给定的正则表达式的第一个子字符串
· boolean matches(String regex):告知此字符串是否匹配给定的正则表达式
· String[] split(String regex):根据给定正则表达式的匹配拆分此字符串
· String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部放到最后一个元素中
三、 常见算法题
StringBuffer和StringBuilder
一、 底层解析
/*
StringBuffer和StringBuilder的底层都是用byte数组存储,
没有final意味着他们是可变的字符序列
*/
byte[] value;
/*
StringBuffer和StringBuilder支持自定义容量的对象创建,
通常建议使用这种方式创建,尽量避免因为容量不够大而append,
因为在开辟空间的过程中需要复制原字符串
*/
public StringBuffer(int capacity) {
super(capacity);
}
/*
StringBuffer和StringBuilder初始容量都是初始字符串长度+16
*/
public StringBuffer(String str) {
super(str.length() + 16);
this.append(str);
}
/*
StringBuffer和StringBuilder的append方法继承自父类,
如果append长度小于当前剩余长度,则不扩容;
如果大于,则通过ensureCapacityInternal方法的规则进行扩充
*/
public AbstractStringBuilder append(String str) {
if (str == null) {
return this.appendNull();
} else {
int len = str.length();
this.ensureCapacityInternal(this.count + len);
this.putStringAt(this.count, str);
this.count += len;
return this;
}
}
/*
ensureCapacityInternal方法的扩容方式是通过newCapacity方法得到扩充大小后,
复制原字符串到新开辟的字符串
*/
private void ensureCapacityInternal(int minimumCapacity) {
int oldCapacity = this.value.length >> this.coder;
if (minimumCapacity - oldCapacity > 0) {
this.value = Arrays.copyOf(this.value, this.newCapacity(minimumCapacity) << this.coder);
}
}
/*
newCapacity方法的扩容方式是先将当前数组大小*2+2,
如果该大小还不够大,则扩容到刚好能放下原字符串+append字符串的长度;
另外,由于该方法通过将数组大小左移1位的方式进行倍增,
还额外加了判断语句判断newcapacity的符号
*/
private int newCapacity(int minCapacity) {
int oldCapacity = this.value.length >> this.coder;
int newCapacity = (oldCapacity << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
int SAFE_BOUND = 2147483639 >> this.coder;
return newCapacity > 0 && SAFE_BOUND - newCapacity >= 0 ? newCapacity : this.hugeCapacity(minCapacity);
}
StringBuffer和StringBuilder的底层源码基本一致,唯一的区别在于StringBuffer的很多方法加上了synchronized锁,而StringBuilder不加锁。
因此StringBuffer线程安全,但效率不高;而StringBuilder线程不安全,但效率高。
二、 常用方法
· int indexOf(String str)
· String substring(int start, int end):查
· int length()
· char charAt(int n):查
· void setCharAt(int n, char ch):改
· StringBuffer append:增
· StringBuffer delete(int start, int end):删
· StringBuffer replace(int start, int end, String str):改
· StringBuffer insert(int offset, xxx):增
· StringBuffer reverse():翻转
三、String、StringBuffer、StringBuilder异同
· String:不可变的字符序列
· StringBuffer:可变的字符序列;线程安全的,效率低
· StringBuilder:可变的字符序列;线程不安全的,效率高
· 底层都是用byte[]存储,区别是有无final关键字