String
在Java
中属于用得比较多.既然用得多,那我们就必须对他有所了解.
1.概览
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
上面就是String
的类定义信息,得:
final
修饰,表示String
类不能被继承.- 实现
Serializable
接口,表示可序列化. Comparable
该类支持在集合中排序,而不用传递外部排序器.CharSequence
提供了对不同类型的字符数组进行只读访问的方法.
private final char value[];
每一个String
在内部都是一个char
类型数组.
疑惑点:
String
为什么是不可变的?字符串在
Java
中大量被使用,而final
有一个很好的特性就是:可以通过将多个重复项指向单个实例来共享他们.final
2.方法
String
类其实并没有什么好说的,大部分都是一些工具方法.这边就讲几个常用的:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; 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; }
String
重写的equals()
方法如上所示,先==
判断引用,然后instanceof
判断是否是同一类型,然后判断字符串长度,最后逐个字符比较.
可能存在疑惑的点:
为什么用
==
比较?因为
String
是一个对象,如果要比较的对象地址都一样,那里面的内容自然相等.那为什么我们平常不能使用
==
比较String
?其实很简单,存在下面这种情况:
String s = new String("a"); String s1 = new String("a"); System.out.println(s == s1);// false
就算值是相同的,但是所创建的对象地址不同,使用
==
自然为false
.
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
上面是重写的hashCode()
方法,默认的hashCode
值为0,在调用过hashCode()
方法后才会赋值.
从该方法看出,String
计算hashCode
的公式是:循环 string.length
次31 * hashCode + currentValue
的和.
疑惑点:
为什么乘的是
31
而不是其他的数?主要是
31
有一个很好的特性:乘法可以被移位和减法代替,以获得更好的性能:31 * i = (i<<5)-i
,虚拟机会自动进行这种优化.
public native String intern();
intern()
方法也算比较关键的知识点了,涉及到的点也比较多,这里贴一篇写得比较好的文章:String#intern
3.杂谈
String
类本身并不复杂,主要是底层所涉及到的字符串常量池一类的.
以前一直在纠结一个问题:
String name = "KChaste Sun"
这种创建String
的方式,算不算是一个'String
',直到我 看到了String
的文档注释:
All string literals in Java programs, such as "abc", are implemented as instances of this class.
关于String
还有以下常见问题:
String hello = "hello"; String hell = "hell"; System.out.println(hello == "hell"+"o");// true System.out.println(hello == hell+"o");// false
造成的主要原因Chapter 3. Lexical Structure (oracle.com):
String literals in different classes in different packages likewise represent references to the same String object.
由常量表达式连接起来的字符串在编译时被计算,直接当作字面量处理.
Strings concatenated from constant expressions are computed at compile time and then treated as if they were literals.
在运行时通过连接计算的字符串是新创建,因此是不同的.
至于为什么这里为用==
会相等,原因也很简单,直接使用双引号声明出来的String
对象会直接存储在常量池中,如果已经存在,则返回同一引用.
还有一点就是关于String
的+=
,在底层使用的是StringBuilder
的append()
方法,并且每次+=
都会新建一个该实例,并且都会调用toString()
方法,该方法会进行数组复制,所以在需要进行大批量字符串拼接的时候,请使用StringBuffer
或者StringBuilder
(老生常谈的了...).
说String
不可变其实也就是每次调用String
的方法时,返回的都是一个新的String
实例.
冷知识: 编译时String
最大长度为65535
,运行时受Integer.MAX_VALUE
影响,这个65535
并不是标准的65535
这么长,而是根据你内容的编码的字节数来算的Java Virtual Machine Specification.
关于String
就写到这里,后续写JVM
之类的或许还会提及.