「OpenJdk-11 源码-系列」 : String

类的定义

在平常开发中,String能够说是最经常使用的类之一了,但也是最容易被忽视的类。先来看看String的定义java

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

	private final char[] value; // jdk 8
    private final byte[] value; // jdk 9 使用 byte 减小空间的浪费,

    private final byte coder;
    private int hash; // Default to 0
    private static final long serialVersionUID = -6849794470754667710L;
}
复制代码

首先,Stringfinal所修饰,同时实现了SerializableComparableCharSequence接口。咱们一个个来看:数组

为撒要用 final 修饰

根据源码,咱们能够看到String的值在内部是用一个byte数组value维护的,因此value是一个引用类型,对于引用类型的变量,咱们每每都会十分当心,由于鬼知道在何时,咱们就会掉进一个大坑... 更况且String是一个很是经常使用的类。因此一个很是重要的缘由就是安全性,使得String类须要被final修饰,即不可变。因此对String类加上了final修饰符,防止String被继承,从而重写value。刚才说到value是一个引用类型,因此在value上也加上了final修饰,而且在整个类中,没有对value进行update的操做。全部的update操做都是会新建一个新的string缓存

为撒实现 Serializable 接口

详情可见🔎「OpenJdk-11 源码-系列」Serializable安全

Comparable & Comparator

先来看看Comparable接口app

public interface Comparable<T> {
    public int compareTo(T o);
}
复制代码

Comparable接口是一个排序接口,若一个类实现了该接口并重写了compareTo方法,就意味着这个类支持了排序。当存在一个实现了Comparable接口的类的集合或数组,那么该集合或数组就能够经过Collections.sort / Arrays.sort进行排序。函数

  • 当返回值为正数,那么就意味着 a > b (a.compareTo(b))
  • 当返回值为零,那么a = b
  • 当返回值为负数,那么a < b

再来看看Comparator 接口post

public interface Comparator<T> {
    //最主要的俩方法
    int compare(T o1, T o2);
    boolean equals(Object obj);
}
复制代码

刚才咱们说到若是一个类实现了Comparable接口后,那么该类就有了排序的功能。但有的时候咱们不想修改这个类,减小对类的侵入。那么咱们就能够自定义一个类来实现Comparator接口。ui

总结一下,实现Comparable接口能够作到内部比较(即相同类之间的比较);而实现了Comparator接口的类,可使用该类对任意两个类型的对象进行比较,且无侵入性。this

属性

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    private final byte[] value;
    private final byte coder;
    private int hash; // Default to 0

}
复制代码
  • value编码

    value存储的是 String的内容,即当使用String str = "abc";的时候,"abc"是存储在一个byte类型的数组中的。这在后续的构造函数中会讲解到。

  • coder

    在JDK9中,String维护了这样一个新的属性coder,它是一个编码格式的标识,表示LATIN1或者UTF-16,String生成的时候会自动初始化这个值,若是字符串中都是能用LATIN1就能表示的就是0,不然就是UTF-16。

    那么为何要加这个coder属性呢?先说结论,能够对字符串的空间进行压缩。先看源码

    //jdk8
        public int length() {
            return value.length;
        }
    //jdk9
        static final boolean COMPACT_STRINGS;
        static {
            COMPACT_STRINGS = true; //默认开启压缩
        }    
        byte coder() {
            return COMPACT_STRINGS ? coder : UTF16;
        }
        public int length() {
            return value.length >> coder(); //若是coder() = 1则会右移一位
        }
    复制代码

    因此,若是当咱们调用length方法的时候,若是valueLATIN1编码的话那么就会右移一位,减小一个字节数。

  • hash

    hashString在实例化的时候对hashcode的一个缓存。因为在开发中String会常常拿来比较,好比HashMap中若是keyString类型,每次比较若是都从新计算hashcode的话就会很费时。

构造方法

public String() {
        this.value = "".value;
        this.coder = "".coder;
    }
复制代码

空参构造方法,会建立一个空的字符串序列。通常不会这么建立。

//常见的几种构造方法
	public String(byte[] bytes) {
        this(bytes, 0, bytes.length);
    }
	public String(byte bytes[], String charsetName) throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
	public String(byte bytes[], Charset charset) {
        this(bytes, 0, bytes.length, charset);
    }
    public String(byte bytes[], String charsetName) throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
    
    public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        checkBoundsOffCount(offset, length, bytes.length);
        StringCoding.Result ret =
            StringCoding.decode(charsetName, bytes, offset, length);
        this.value = ret.value;
        this.coder = ret.coder;
    }
	
	public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBoundsOffCount(offset, length, bytes.length);
        StringCoding.Result ret =
            StringCoding.decode(charset, bytes, offset, length);
        this.value = ret.value;
        this.coder = ret.coder;
    }
复制代码

使用字节数组来建立能够分为指定编码或默认编码(ISO-8859-1)进行编码操做。

经常使用方法

比较简单,可直接查看API

其它

String 对 + 号的重载

String str = "this is";
String str1 = str + "str"
复制代码

在底层,Java 对 String 的+的支持使用的是 StringBuilder 以及它的appendtoString方法。即:

String str = "str";
String str1 = (new StringBuilder(String.valueOf(str))).append("str").toString();
复制代码

intern

public native String intern();
复制代码

能够看到 intern 是被 native修饰,这说明该方法是由底层的C/C++进行实现的。它的做用是 若是常量池中存在当前字符串, 就会直接返回当前字符串。若是常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回。经过双引号声明的字符串会放入到常量池中

String str = "123";
String str1 = new String("123");
String str2 = str1.intern();
s
System.out.println(str == str1); //false
System.out.println(str == str2); // true
复制代码
相关文章
相关标签/搜索