# 字符串易混淆知识

# 字符串的生成方式

  1. 直接赋值: String s = "hello"
  2. 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修饰符修饰,这就意味着其数值一旦被初始化之后就不能再被更改了
  • 作用:
    1. 安全性。假设String对象是可变的,那么String对象将可能被恶意修改。
    2. 唯一性。这个做法可以保证hash属性值不会频繁变更,也就确保了唯一性,使得类似HashMap的容器才能实现相应的key-value缓存功能
    3. 功能性。可以实现字符串常量池

# 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);
        }

}