java.lang.string源码_JDK1.8-java.lang.String类源码阅读

String类应该是我们用的最多的一个类了。String类的方法很多,每个都写太麻烦了,也没有必要。我就捡我觉得比较重要的和应该注意的点写了。

1、定义

public final class String

implements java.io.Serializable, Comparable, CharSequence

可以看到有final修饰,说明String是不可变的,实现了Serializable、Comparable接口,说明String可以序列化,可以进行比较,实现了CharSequence接口,说明String是一个有序字符的集合。

2、字段

/** 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 */

// 序列化的UID

private static final long serialVersionUID = -6849794470754667710L;

// 忽略大小写的比较器

public static final Comparator CASE_INSENSITIVE_ORDER

= new CaseInsensitiveComparator();

/**

* 很多人不知道这字段是干嘛的,其实和transient一样都是控制字段的序列化的,

* 只不过transient修饰的字段不被序列化,而serialPersistentFields数组里

* 的字段需要序列化,如果某个字段在serialPersistentFields数组里,且被transient

* 修饰,则以serialPersistentFields为准,serialPersistentFields优先级高于

* transient。

* 这里是一个空数组,所以String类的所有字段都不会序列化

*/

private static final ObjectStreamField[] serialPersistentFields =

new ObjectStreamField[0];

3、构造方法

构造方法有很多,就不写了,贴个图吧😁

9b8635f89f34dc9b05d35e5eb2da0d2b.png

4、equals方法

String类里的equals方法:

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;

}

对比的是每个字符是否相等,还有一个equalsIgnoreCase方法,是忽略大小写对比每个字符是否相等

5、hashCode方法

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;

}

算法不难,就是for循环里的h = 31 * h + val[i],就是这个公式:

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

其中s[i]代表字符串的第i个字符,n代表字符串的长度

我们注意到这里有个31,为什么选31不选别的数呢?这里直接给出原因,具体可以参考这篇文章

31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一

31可以被 JVM 优化,31 * i = (i << 5) - i。因为移位运算比乘法运算更快(PS:hashMap的length为2的整数次方也是基于性能的考虑,hash(key)%length=hash(key)&(length-1),&比%快)

6、toString方法

String类里的toString方法:

public String toString() {

return this;

}

返回字符串本身

7、字符串常量池

为了字符串的复用,减少空间浪费,JVM会维护一个字符串常量池,常量池中的字符串不可重复,并且对长度有限制不能超过65535个字节(注意这里是字节,而不是字符)。有的同学可能有疑问,我有的类里面的

String变量有几十万个字符长,为啥不报错呢??因为那些String变量没放入常量池,有兴趣的可以参考我的这篇博客:String的长度限制

那什么样的字符串会放入常量池呢?

字面量创建的字符串String str1 = "abc"或者由字符串常量拼接出的字符串String str2 = "abc"+"def"会直接放入常量池(当然前提是常量池中不存在该字符串)

调用intern方法,如果常量池中没有该对象,则将该对象添加到池中,并返回池中的引用

对于字符串常量池的详细介绍可以参考我的这篇博客:字符串常量池

8、intern方法

String类里的intern方法:

public native String intern();

可以看到这是一个本地方法。当一个String对象调用intern()方法时,如果常量池中已经有同样的字符串了,则返回该对象的引用(在堆中就返回堆中的引用,在池中就返回池中的引用),如果没有,则将该对象添加到池中,并返回池中的引用。

做个小测试:

public class Test {

public static void main(String[] args) {

String s1 = "liu"; // 这里将"liu"放入常量池

String s2 = s1.intern(); // 因为s1本身就在常量池所以直接返回s1的引用

System.out.println(s1 == s2); // true

String s3 = new String("whut"); // 这里会在堆上创建对象

String s4 = s3.intern(); // 因为s3在堆上,不在常量池中,所以将"whut"添加到池中,此时s4为池中"whut"的引用

System.out.println(s3 == s4); // s3在堆上,s4在常量池中,故false

String s5 = "whut"; // 上面已经将"whut"添加到池中了

System.out.println(s5 == s4); // true

}

}

结果:

3739c2c2e61b4b80cfff3b9792f8ef9a.png

9、String 真的不可变吗

what?被final修饰难道可变?

final只是限制引用不能改变,就是限制内存地址不变,但限制不了我把地址上的内容改变。(。・∀・)ノ我们知道String真正的内容放在value数组里,同样value也是被final修饰的,但依然限制不了我改

value数组里的内容。通过反射就能改,我们试一下:

public class Test {

public static void main(String[] args) throws Exception{

String s = "hello world";

Field value = String.class.getDeclaredField("value");

value.setAccessible(true);

char[] value1 = (char[])value.get(s);

value1[0] = 'H';

System.out.println(s);

}

}

结果:

8c0bfea09cf6519e416a7967bc992bf9.png

可见s被改变了,不过基本不会去改String的值,如果面试官问你String是否可变,你答出这点来是不是就稳了,哈哈哈。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值