您的位置 首页 java

Java的String为什么要设计成final?

java 的String为什么要设计成final?

简单来说,String的初衷是要设计成不可变的(immutable)。String设计成final只是实现String不可变的一部分。

本文首先回答为什么String要设计成不可变的,然后再讲解是如何实现不可变的。

为什么要设计成不可变的呢?

首先,String设计成final是为了支持共享,因为不可变,所以不存在竞态资源,也就是 线程 安全的了,这样就可以方便的共享给其他方法或者线程。试想,如果String是可变的,那么在需要共享的地方,控制并发修改将是多么麻烦的事情。

其次,提高效率。 JVM 提供有常量池。对于已经存在的字符串,如果发现常量池内已经存在了,就会直接将常量池内的对象引用返回,如果不存在,会先创建对象,然后将对象放在常量池内,以便以后复用。这样极大地简化了相同字符串的重复构建,节省了资源。

另外,字段串设计成fianl,更符合自然语言的特征。就像字或者单词,应该就是保持不变的,使用才可靠,如果使用的是一个可能变化的单词,比如你标记自己的性别是male,但是被别人给篡改成了female,将是多么可笑和可怕的事情。

如何实现不可变?

首先看下String的签名及内部数据结构字段的定义。

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

    @Stable
    private final byte[] value;
}
  

数组 value 被关键字 final 修饰。大家都知道final关键字修饰字段时,只能限制修改字段的引用,如果字段是数组或者对象,是无法限制修改数组和对象的内容。所以仅仅限制字段 value final 是无法保证String对象是不可变的。

String主要提供了多种构造方法,以及其他操作方法,比如检查序列中的各个字符,比较字符串,搜索字符串,提取子字符串以及创建字符串的副本,并将所有字符均转换为大写或小写等。这些方法的默认实现都没有修改value字段,或者返回value的引用。以下是 replace 方法和 getBytes 方法的代码。

replace方法

String.replace 方法的代码:

 public String replace(char oldChar, char newChar) {
    if (oldChar != newChar) {
        String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar)
                                : StringUTF16.replace(value, oldChar, newChar);
        if (ret != null) {
            return ret;
        }
    }
    return this;
}
  

并没有将直接修改value的内容,而是调用 StringLatin1.replace 或者 StringUTF16.replace 。以 StringUTF16.replace 方法为例,方法内并没有修改value内容的代码,而是复制了value数组,请看其中代码片段:

 byte buf[] = new byte[value.length];
for (int j = 0; j < i; j++) {
    putChar(buf, j,  getChar (value, j)); // TBD:arraycopy?
}
while (i < len) {
    char c = get Char (value, i);
    putChar(buf, i, c == oldChar ? newChar : c);
    i++;
}
  

getBytes方法

String.get byte s 方法的代码:

 public byte[] getBytes() {
    return StringCoding.encode(coder(), value);
}
  

StringCoding.encode 方法调用其重载方法,重载方法又调用了 encodeUTF8 方法。 encodeUTF8 方法同样没有修改value的内容,而只是读取。以下是其中代码片段:

 dst = new byte[val.length << 1];
for (int sp = 0; sp < val.length; sp++) {
    byte c = val[sp];
    if (c < 0) {
        dst[dp++] = (byte)(0xc0 | ((c & 0xff) >> 6));
        dst[dp++] = (byte)(0x80 | (c & 0x3f));
    } else {
        dst[dp++] = c;
    }
}
  

final修饰类

最后,通过final修饰类,禁止子类复写String方法的逻辑。

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

总结

总的来讲,String的实现者通过四步来实现String的不可变性:

  1. final修饰字段,限制字段引用不可修改;
  2. 默认方法实现,保证了不修改任何的字段的内容;
  3. 保护字段的引用不外泄,避免使用者拿到引用后修改数组或者对象的内容,String的默认实现并没有将字段的引用返回给使用者;
  4. final修饰String类,禁止使用者通过继承的来 复写 String方法逻辑,来破坏String的不可变性。


文章来源:智云一二三科技

文章标题:Java的String为什么要设计成final?

文章地址:https://www.zhihuclub.com/188821.shtml

关于作者: 智云科技

热门文章

网站地图