关于String

1、String类的修饰符

这是一个final的类,不可继承,创建的对象不可变

public final class String implements java.io.Serializable, Comparable<String>, CharSequence{}

为什么String要设计成不可变的类?

有以下几个方面可以思考:

1、为了实现字符串常量池,提高效率

2、安全,设计为final的,保证在多线程的时候是安全的,可以被多个线程共享

3、String设计的时候缓存了hash值,不需要重新计算,如果是可变的,每次都需要重新计算

2、主要成员变量

private final char value[]; //使用字符数组存放字符串
private int hash; // Default to 0  缓存哈希值

3、equals()方法

重写了Object的equals()方法,判断两个字符串的每一个字符是否相等
public boolean equals(Object anObject) {
    if (this == anObject) { // 如果两个引用指向的是同一个String对象
        return true;
    }
    if (anObject instanceof String) { // 如果第2个引用指向的对象是String实例
        String anotherString = (String)anObject; // 强制类型转换
        int n = value.length; // 获取第1个引用指向的String对象的字符串长度
        if (n == anotherString.value.length) { // 如果两个字符串长度相等
            // 定义字符数组指针
            char[] v1 = value;
            char[] v2 = anotherString.value;
 
            // 字符依次比较
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

4、hashCode()方法

重写了hashCode()方法

public int hashCode() {
    // 获取字符串缓存散列码
    int h = hash;
    if (h == 0 && value.length > 0) { // 如果字符串缓存散列码为0并且字符串数组长度大于0
        // 定义字符数组指针
        char val[] = value;
 
        // 遍历每个字符
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i]; // 31 * h会被JVM优化成(h << 5) - h
        }
        hash = h; // 修改字符串缓存散列码
    }
    return h;
}

5、split()方法

split()方法的坑比较多,试一下执行
public static void main(String[] args) {
    String s = "1"; 
    String[] s1 = s.split("-");
    System.out.println(s1.length);-----------------结果是1
}

这里建议避开如下坑:

(1)不要使用如下字符作为分隔符:.$|()[{^?*+\\,这些是"Dangling meta character";

(2)对于空字符串的,也不要使用split,这样分割后的数组长度是1

(3)注意转义字符

(4)注意分隔符在最前和最后的情况

推荐使用的分隔符:单个字符如     "-"  ","   ";"   ":"

6、native方法 intern()

调用了intern()方法的字符串对象,将会进入常量池

/**
     * Returns a canonical representation for the string object.
     * A pool of strings, initially empty, is maintained privately by the class {@code String}.    
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the method, then the string from the pool is returned. 
   * Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {s} and {t},
     * {s.intern() == t.intern()} is {true}
     * if and only if {s.equals(t)} is {true}.
     * <p>
     * All literal strings and string-valued constant expressions are interned. --------所有字面意义的字符串值和具有字符串值的常量表达式
   * String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
public native String intern();

7、关"+"操作符

  • String 在执行 + 操作时,编译时期会进行优化。
  • 如果是多个字面量相加,则会在编译时期直接把组合好的字符串放入常量池。
  • 如果有字符串常量参与,以最左边的字符串为参数创建StringBuilder对象,然后依次对右边进行append操作,最后将StringBuilder对象通过toString()方法转换成String对象(注意:中间的多个字符串常量不会自动拼接)
String s1 = "aa"; // 编译期在常量池中创建 "aa"
String s2 = "bb"; // 编译期在常量池中创建 "bb"
String s3 = "aa" + "bb"; // 编译期JVM进行优化,在常量池中创建 "aabb"

String s4 = s1 + s2 ;
// 等价于 String s4 = new StringBuilder().append("aa").append("bb").toString();
String s5 = "11" + "22" + s1 + "33" + "44" + s2;
//等价于 String s5 = new StringBuffer("1122").append("aa").append("33").append("44").append("bb").toString();

 

8、关于字符串常量池

(1)双引号字符串:String s1 = "aa";  只在常量池上创建"aa"对象

(2)new String():  String s2 = new String("aa")    在常量池中创建常量"aa",堆上创建对象"aa",其中堆上创建的"aa",value属性指向常量池中的"aa"

(3)new String()相加:String s3 = new String("a") + new String("a");

  在堆上创建两个"a",一个"aa"。在常量池中创建了"a"

(4)String s4 = new String("a") + new String("a");   s4.intern();

  在堆上创建两个"a",一个"aa"。在常量池中创建了"a",并将该对象aa的引用保存到常量池上

9、关于字符串在类加载时的几个特点

常量与常量的拼接结果在字符串常量池,池中不会存在相同内容的常量,如String s = "hello"+" world";

只要其中一个是变量,结果就在堆中。如String s1 = "hello";String s2 = s1 + "world";但是如果定义的是final的,结果仍然在常量池中,如final String s1 = "hello";String s2 = s1 + "world";

如果拼接的结果调用intern()方法,返回值在常量池中 如 String s1 = "hello";String s2 = "world" ;String s3 = (s1+s2).intern();

10、一些容易忽视或忘记的方法

    public boolean matches(String regex) {
        return Pattern.matches(regex, this);
    }
    //获取字节数组,是encode过程,默认字符集是UTF8
    public byte[] getBytes() {
        return StringCoding.encode(value, 0, value.length);
    }
    //字节数组为入参的构造方法,是decode过程,默认字符集UTF8,出现乱码的话首先应该想是encode和decode使用的字符集不一致造成
    public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }

 

posted @ 2020-03-03 13:27  鼠标的博客  阅读(165)  评论(0编辑  收藏  举报