# 字符串易混淆知识
# 字符串的生成方式
- 直接赋值:
String s = "hello"
- new对象实例化:
String ss = new String("hello")
# 两种生成方式的区别
- 直接赋值: 没有开辟新的堆内存空间,而是存储在了对象池中
- new实例化: 每次都会开辟新的内存空间
# 字符串手工入池
通过new关键字进行的实例化,对象并没有保存在内存池中,可以通过intern()方法来手工入池。在比较就为true了。
String str1="hello";
String str2=new String("hello");
System.out.println(str1==str2); //false
System.out.println(str2.intern()==str1); //true
System.out.println(str2==str1); //false
# String对象
# 抽象结构
public final class String implements Serializable, Comparable<String>, CharSequence {
private final char[] value;
private int hash;
public String(String var1) {
this.value = var1.value;
this.hash = var1.hash;
}
public native String intern();
public int hashCode() {
int originalHash = this.hash;
if (originalHash == 0 && this.value.length > 0) {
char[] originalValue = this.value;
for(int i = 0; i < this.value.length; ++i) {
originalHash = 31 * originalHash + originalValue[i];
}
this.hash = originalHash;
}
return originalHash;
}
public boolean equals(Object target) {
if (this == target) {
return true;
} else {
if (target instanceof String) {
String targetValue = (String)target;
int valueLength = this.value.length;
if (valueLength == targetValue.value.length) {
char[] charArray = this.value;
char[] targetCharArray = targetValue.value;
for(int i = 0; valueLength-- != 0; ++i) {
if (charArray[i] != targetCharArray[i]) {
return false;
}
}
return true;
}
}
return false;
}
}
}
# 字符串常量池
- 常量池是JVM为了减少字符串对象的重复创建,特别维护了一个特殊的内存,这段内存被称为字符串常量池或者字符串字面量池
# 创建了几个对象
- 在字符串常量池中不存在对应的字面量的情况下,new String()会创建两个对象,一个放入常量池中(字面量),一个放入堆内存中(字符串对象). 使用构造函数的方式创建字符串时,JVM同样会在字符串常量池中先检查是否存在该字面量,只是检查后的情况会和使用字面量创建的方式有所不同。如果存在,则会在堆中另外创建一个String对象,然后在这个String对象的内部引用该字面量,最后返回该String对象在内存地址中的引用;如果不存在,则会先在字符串常量池中创建该字面量,然后再在堆中创建一个String对象,然后再在这个String对象的内部引用该字面量,最后返回该String对象的引用
# String对象比较
- 比较两个String对象是否相等,通常是有【==】和【equals()】两个方法
- 在基本数据类型中,只可以使用【==】,也就是比较他们的值是否相同
- 对于对象(包括String) 来说,【==】比较的是地址是否相同,【equals()】才是比较他们内容是否相同;
- 而equals()是Object都拥有的一个函数,本身就要求对内部值进行比较。
# String对象的不可变性
- String类用了final修饰符,这就意味着这个类是不能被继承的
- 类中的数组char[] value来看,这个类成员变量被private和final修饰符修饰,这就意味着其数值一旦被初始化之后就不能再被更改了
- 作用:
- 安全性。假设String对象是可变的,那么String对象将可能被恶意修改。
- 唯一性。这个做法可以保证hash属性值不会频繁变更,也就确保了唯一性,使得类似HashMap的容器才能实现相应的key-value缓存功能
- 功能性。可以实现字符串常量池
# String性能优化
- 拼接字符串的性能优化:
- 尽量减少使用【+】进行字符串拼接操作。这是因为使用【+】进行字符串拼接,会在得到最终想要的结果前产生很多无用的对象
- 将代码优化成使用StringBuilder或者StringBuffer对象来优化字符串的拼接性能,因为StringBuilder和StringBuffer都是可变对象,也就避免了过程中产生无用的对象
- StringBuffer对象,这个对象是支持线程安全的
- 在不需要线程安全的情况下,选用StringBuilder对象,因为StringBuilder对象的性能在这种场景下,要比StringBuffer对象或String对象要好得多
- 使用intern()方法优化内存占用:
- 使用intern()方法将原本需要创建到堆内存中的String对象都放到常量池中,因为常量池的不重复特性(存在则返回引用),也就避免了大量的重复String对象造成的内存浪费问题
- 字符串分割的性能优化:
- 在字符串分割时,应该慎重使用split()方法,而首先考虑使用String.indexOf()方法来进行字符串分割,在String.indexOf()无法满足分割要求的时候再使用Split()方法
- split()方法不稳定的原因:
- 传入的参数长度为1,且不包含“.$|()[{^?*+\”regex元字符的情况下,不会使用正则表达式
- 传入的参数长度为2,第一个字符是反斜杠,并且第二个字符不是ASCII数字或ASCII字母的情况下,不会使用正则表达式
- 正则表达式的性能是非常不稳定的,使用不恰当的话可能会引起回溯问题并导致CPU的占用居高不下
# StringBuffer对象
# 抽象结构
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
public AbstractStringBuilder append(String targetValue) {
if (targetValue == null)
return appendNull();
int len = targetValue.length();
ensureCapacityInternal(count + len);
//使用 System.arrayCopy,进行拷贝
targetValue.getChars(0, len, value, count);
count += len;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// 溢出 -> 扩容
if (minimumCapacity - value.length > 0) {
// 创建一个新数组,将原有的值拷贝进去, 使用 System.arraycopy 实现
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
// 扩容2倍
int newCapacity = (value.length << 1) + 2;
// 扩容两倍,仍然不够,则用目标长度
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
// 进行边界判断
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
}
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
private transient char[] toStringCache;
public StringBuffer() {
super(16);
}
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public synchronized StringBuffer append(StringBuffer sb) {
toStringCache = null;
super.append(sb);
return this;
}
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
}
# StringBuilder对象
# 抽象结构
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
public StringBuilder() {
super(16);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
public String toString() {
return new String(value, 0, count);
}
}