关于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()方法
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()方法
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™ 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); }