String字符串的前世今生
String是我们最常用的数据类型,不过你真的了解它吗?今天让我们一起了解一下它的前世今生。
源码JDK8
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private int hash;
private final char value[];
}
源码JDK9之后
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
private final byte coder;
private int hash;
}
首先,我们发现String实现了众多接口:
- 实现了Serializable,意味着可以序列化。
- 实现了Comparable,意味着可以使用compareTo进行比较。
- 实现了CharSequence,该接口提供对许多不同类型的
char
序列的统一。
其次,观察源码String
本身和属性value
都被final修饰,我们都知道,final修饰类则该类不能被继承,也就是不能有子类,final修饰属性则该属性为常量一旦赋值,则不能改变。
当String本身和value属性都被final修饰,这就使得String具有了不可变性,这样设计的意义何在?
- 可以保证String对象的安全性,保证存储值不会被恶意修改。
- 当保证String本身不可变后间接的影响hash值的唯一,从而使String更适合做键值(Map<String,Object>)。
- 可以用作字符串常量池。
然后,对比JDK8和9,最明显的一个区别就是存储字符的数组怎么由char -> byte
,那么为什么要变成使用byte
数组存储呢?
因为开发人员发现人们使用的字符串值是拉丁字符居多,而每一个char占用两个字节,如果使用的char数组存储只需要一个字节就OK啦,另外一个字节就会造成浪费了,进而造成内存的浪费,gc的更加频繁,因此在jdk9中将String底层的实现改为了byte数组。
除此之外,心细的小伙伴已经发现,JDK9之后多出来的coder是个啥玩意啊?
/**
* The identifier of the encoding used to encode the bytes in
* {@code value}. The supported values in this implementation are
*
* LATIN1
* UTF16
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*/
private final byte coder;
coder属性用来表示编码格式,看源码注释发现coder有两个实现LATIN1、UTF16
@Native static final byte LATIN1 = 0;
@Native static final byte UTF16 = 1;
除此之外,COMPACT_STRINGS这个属性也是十分重要,翻译为压缩字符串,默认静态代码块赋值true。
/**
* If String compaction is disabled, the bytes in {@code value} are
* always encoded in UTF16.
*/
static final boolean COMPACT_STRINGS;
static {
COMPACT_STRINGS = true;
}
注释:如果禁用了字符串压缩,{@code value}中的字节是总是使用UTF16编码,很明显这个就是决定该String对象是否采用压缩策略的关键属性。
最后我们查看构造器
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;
}
}
// 如果没有压缩成功并返回就直接设置为UTF16
this.coder = UTF16;
this.value = StringUTF16.toBytes(value, off, len);
}