string源码浅析
1. 示例
先来看几个例子:
1.1 示例一
public static void main(String[] args) {
String s1 = "123";
String s2 = "123";
System.out.println(s1 == s2);
}
结果是true.
s1, s2创建的过程是,先在常量池(常量池属于方法区的一部分)里看有没有,这个字符串,
如果有返回引用,没有则创建。所以这里的s1,s2是属于常量池的同一个字符串,所以指向的引用是相同的。
1.2 示例二
public static void main(String[] args) {
String s1 = "123";
String s2 = new String("123");
System.out.println(s1 == s2);
}
结果是false
s1是从常量池里获取的,而s2是实例化的对象存在于堆中。
1.3 示例三
public static void main(String[] args) {
String s1 = new String("123");
String s2 = new String("123");
System.out.println(s1 == s2);
}
结果是false
s1,s2是存在于堆中的两个不同的实例。
1.4 示例四
public static void main(String[] args) {
String s1 = "123";
String s2 = new String("123");
System.out.println(s1.equals(s2));
}
结果是true
s1,s2是存在于堆中的两个不同实例,但是他们的字符串值是相等的。
1.5 结论
创建一个字符串时,首先会检查池中是否有值相同的字符串对象,
如果有就直接返回引用,不会创建字符串对象;如果没有则新建字符串对象,
返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String
对象是不检查字符串常量池的,而是直接在堆中创建新对象,也不会把对象放入池中。
2. string的不可变
string类是immutable(即不可变的)的类,不可变类的意思是:当类被创建实例化后,就不能改变其成员变量的值,不可变类是线程安全的。
不可变类的设计原则:
- 类被final修饰,保证类不会被继承, 其成员变量不会被子类修改
- 成员变量是私有的,并且被final修饰
- 不提供成员变量的setter方法
- 通过构造器初始化成员变量,并且进行深拷贝
3. string “+ ” 拼接操作的实现
java编译器会把+操作替换为StringBuffer的appen操作或者相似的技术减少string对象的频繁创建。
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects
that are created by evaluation of an expression.
4. stringBuffer,stringbuilder与string的关系
stringBuffer就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类。
stirngBuilder和stringBuffer功能一样都继承自AbstractStringBuilder父类。
stringBuilder是线程不安全的,stringBuffer的方法都被synchronized修饰。
- 字符串常量 直接使用string
- 如果需要对大量字符串进行操作,在多线程下并且要保障线程安全,应该使用stringBuffer
- 如果需要对大量字符串进行操作,在单线程情况下,应该使用stringBuilder
三者的性能比较
stringBuilder > stringBuffer > string
5. String源码分析
5.1 类声明
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
不可变对象,被final修饰,不能被继承。
5.2 成员变量
private final char value[];
private int hash;
底层数据结构是char的数组
5.3 构造方法
5.3.1 String()
public String() {
this.value = "".value;
}
无参构造方法,初始化为空字符串。
5.3.2
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
有参构造方法,初始化成员变量,并且进行深拷贝
5.4 实例方法
5.4.1 length()字符串的长度
public int length() {
return value.length;
}
5.4.2 判断是否为空
public boolean isEmpty() {
return value.length == 0;
}
5.4.3 返回指定索引的字符
public char charAt(int index) {
// 索引如果超范围抛运行时异常
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
5.4.4 字符串是否相等
public boolean equals(Object anObject) {
//是否引用相等
if (this == anObject) {
return true;
}
// 是否传入的参数对象是String的实例
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;
}
}
// 传入的参数不是String的实例,则直接返回false
return false;
}
5.4.5 字符串连接
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
// 拷贝
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
// 返回新的字符串对象
return new String(buf, true);
}
5.4.6 去除字符串首尾的空格
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */
//字符串头是否有unicode编码小于32的字符
while ((st < len) && (val[st] <= ' ')) {
st++;
}
//字符串尾是否有unicode编码小于32的字符
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
//返回首尾去除了编码小于32的字符
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}
参考:
https://www.cnblogs.com/jaylon/p/5721571.html
https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.18.1