java源码阅读——string


String中的final

首先来看String的定义,用了final修饰,所以String类是无法被继承的

public final class String implements java.io.Serializable, Comparable<String>, CharSequence

value这个变量就是用来存储字符串值的关键变量,之前jdk8的时候使用的是char数组,到了jdk9这里换成了byte数组

@Stable
private final byte[] value;

因为用final定义了数组,说明value指向的地址是不可改变的,但是数组的内容是可以改变的,不过因为这个byte数组是私有的,并且String类里面并没有提供修改byte数组的接口,使得String类中的value不可改变,就算是replace方法,在实现里面也是new了一个新的String

用个小例子说明下final修饰数组的情况:

final char[] s = {'2', '4', '5'};
s[0] = '1';
System.out.println(s);  // 输出145
String s = "123";   // 报错

String字符串压缩

再往下看一行,发现多了一个叫做coder的东西,这东西究竟是个啥?

private final byte coder;

结合整个String类的源码研究了一下,原来自从jdk9开始,底层的存储由char数组换成了byte数组,因为在大部分情况下,String所存储的字符可以只用latin1来编码,latin1一个字符只占1个字节,char一个字符要占两个字节,这样子就可以节省空间,所以我们在接下来的源码中可以看到很多对于编码格式的处理

看到COMPACT_STRINGS这个参数,这个参数代表是否支持字符串压缩,默认设置为true

static final boolean COMPACT_STRINGS;

static {
    COMPACT_STRINGS = true;
}

把String翻到最下面,这里定义了String用到的两种编码方式,一种是UTF16,设置值为1,另一种就是LATIN1,设置值为1,只有当COMPACT_STRINGS为真,且编码方式为LATIN1时, isLatin1才会返回真

byte coder() {
    return COMPACT_STRINGS ? coder : UTF16;
}

byte[] value() {
    return value;
}

private boolean isLatin1() {
    return COMPACT_STRINGS && coder == LATIN1;
}

@Native static final byte LATIN1 = 0;
@Native static final byte UTF16  = 1;

构造方法

String中有很多的构造方法,比较重要的应该是这个String(char[] value, int off, int len, Void sig),因为很多构造方法其实就在调用这个
在这里插入图片描述
详细看一看

如果COMPACT_STRINGS设置为true,那么就看看value里面的字符是不是都可以被latin1编码,可以的话就是用Latin1编码,不然就用UTF16编码

String(char[] value, int off, int len, Void sig) {
    if (len == 0) {
        this.value = "".value;
        this.coder = "".coder;
        return;
    }
    if (COMPACT_STRINGS) {
        byte[] val = StringUTF16.compress(value, off, len);
        if (val != null) {
            this.value = val;
            this.coder = LATIN1;
            return;
        }
    }
    this.coder = UTF16;
    this.value = StringUTF16.toBytes(value, off, len);
}

如果用字符串去构造只要复制一下属性就可以了

public String(String original) {
    this.value = original.value;
    this.coder = original.coder;
    this.hash = original.hash;
}

其他api

length

返回value的长度,如果用的UTF16编码那么长度除以2,因为UTF16编码的长度是Latin1的2倍

public int length() {
    return value.length >> coder();
}

isEmpty

就看看value数组长度是否为0

public boolean isEmpty() {
    return value.length == 0;
}

equals

先看看是不是指向同一个地址,如果不是那么根据编码方式调用不同的比较函数,StringLatin1.equals的比较方式就是遍历byte数组,如果全部一样就是true,发现不一样的就返回false

public boolean equals(Object anObject) {
	// 地址是否相同
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String aString = (String)anObject;
        if (coder() == aString.coder()) {
        	// 根据编码调用不同的方法,具体方式是遍历byte数组看看是否相同
            return isLatin1() ? StringLatin1.equals(value, aString.value)
                              : StringUTF16.equals(value, aString.value);
        }
    }
    return false;
}

接下来的substring,startswith,replace,split,join,trim等具体方式就省略了,感觉代码里面对不同编码做了不同处理,其实要看主要过程的话,还是jdk8比较清晰。

换了个jdk结果发现里面的变化还挺大的,所以有事没事翻翻源码还是需要的~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值