本文基于jdk 1.8.0_181全面分析String源码
Java类定义
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
-
final
String类是不可变的(Immutable),不可变的好处有很多, 以及String类的不可变是怎么实现的,详见:。
将String类定义成final的,就是保证String类不可变的一个重要因素 。final关键字的好处,及详细分析,详见:tbd
此处final用于修饰String类时,表示String类不能被继承,否则会有编译期错误
- 实现Serializable接口
Serializable是一个标识性接口,实现了这个接口的类被标识成可序列化的类。
假设有这样一个类,实现了Serializable接口:
import java.io.*;
import java.util.Date;
public class SerializableTest implements Serializable {
private String testName ;
private int testNum;
public String getTestName(){
return this.testName;
}
public int getTestNum(){
return this.testNum;
}
public void setTestName(String name){
this.testName = name;
}
public void setTestNum(int num){
this.testNum = num;
}
public String toString(){
return testName + ", " + testNum;
}
public static void main(String[] args){
SerializableTest test = new SerializableTest();
test.setTestName("seeMe?");
test.setTestNum(10000);
serialize(test,"serializeTest.ser");
deserialize("serializeTest.ser");
}
}
也就是说可以通过以下方式将类的对象在内存中的状态保存下来:
private static void serialize(SerializableTest test, String s) {
FileOutputStream fs = null;
try {
fs = new FileOutputStream("serializeTest.ser");
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject("testSTring");
os.writeObject(new Date());
os.writeObject(test);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
也可以通过以下方式,将一个对象在内存中恢复(注意,不只限于本机本地恢复,在其他机器上只要有该类的定义,都可以恢复原来的对象):
private static void deserialize(String s) {
FileInputStream fis = null;
try {
fis = new FileInputStream(s);
ObjectInputStream ois = new ObjectInputStream(fis);
String str = (String) ois.readObject();
Date date = (Date) ois.readObject();
System.out.println(str);
System.out.println(date.toString());
SerializableTest test = (SerializableTest)ois.readObject();
System.out.println(test.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
关于java序列化的深入研究,详见:
- 实现Comparable接口
实现了Comparable接口的类所构成的数组(Array)或者列表(List)可以使用Collections.sort()或者Arrays.sort()方法进行自动排序。
实现了Comparable接口的类的对象可以在SortedMap和SortedSet里作为key值,而不用指定Comparator.
此处Java实现了Comparable接口,方便进行字符串的比较(高频操作),以及使用String作为SortedMap或者SortedSet的key值。
- 实现CharSequence接口
CharSequence实际上代表一组有序的字符数组,其实现类主要有三个,分别是String,StringBuffer,StringBuilder。
该接口定义了有序字符串上常见操作,比如length(长度),charAt(取第几个字符),方便String,StringBuffer,StringBuilder进行一些比较、拼接等操作。
(没有CharSequence这层抽象,需要先判断对象属于哪个类,再调用该类的同类操作的具体实现; 有了这层抽象,直接调用接口中的方法就可以了,运行时自动会调用对象所属类的具体实现)
此处需要注意的是,java 8引入了codepoint概念。常规的char对应2个字节,但是有一些特别的unicode字符,比如emoij表情符号,对应4个字节。
并提供了相应的获得当前字符串的codepoint流的方法,codePoint(), 以及获得当前字符串的char流的方法,chars()。
成员变量
/** The value is used for character storage. */
private final char value[]; //字符串实际值
/** Cache the hash code for the string */
private int hash; // Default to 0 //字符串的哈希值
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L; //实现序列化的标识
本质上一个字符串,底层就是靠char[]实现的。
image.png构造函数
- 通过无参数构造函数new String()初始化对象时不会报错,而其他构造函数,有可能报NullPointerException。
- 各构造函数对应内存分配情况,详见:
Java内存详解 - 内存分区实例之String
重要方法解析
- hashCode()
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;
}
String不可变,因此初始化String对象时,就会计算其hascode,避免重复计算对象的hash值。
另外这个方法比较有意思的是乘积因子为何会选择为31.
简要的说有两个原因:
- 31是个适合的质数,数值太小,哈希值可能值的范围就较小。 数值太大,哈希值可能值会超过int型的最大数值。
- 31可被jvm优化,为2<<5-1
详见:https://www.cnblogs.com/nullllun/p/8350178.html
- intern()
本地方法。
获得字符串对应的字符串常量池中的位置,避免重复创建字符串对象。
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} 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 {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code 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();
备注:访方法初衷是提高对象创建的性能,以及重用内存。实际上使用不当会导致内存问题,详见:Java基础源码分析2 - 【String类】intern方法导致的内存问题
- subString
本方法需要注意的是在java 6上如果使用不当可能导致OutOfMemory问题
详见:Java基础源码分析1 - 【String类】subString导致OutOfMemory
- getBytes
getBytes编码问题: https://segmentfault.com/a/1190000011136555
深度探秘
- 都说Java中String是不可变的?它果真是不可变的么?
详见:java基础源码分析4 - 【String类】真的不可变么?
网友评论