在平常开发中,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;
}
复制代码
首先,String
被final
所修饰,同时实现了Serializable
,Comparable
和CharSequence
接口。咱们一个个来看:数组
根据源码,咱们能够看到String
的值在内部是用一个byte
数组value
维护的,因此value
是一个引用类型,对于引用类型的变量,咱们每每都会十分当心,由于鬼知道在何时,咱们就会掉进一个大坑... 更况且String
是一个很是经常使用的类。因此一个很是重要的缘由就是安全性,使得String
类须要被final
修饰,即不可变。因此对String
类加上了final
修饰符,防止String
被继承,从而重写value
。刚才说到value
是一个引用类型,因此在value
上也加上了final
修饰,而且在整个类中,没有对value
进行update的操做。全部的update操做都是会新建一个新的string
。缓存
详情可见🔎「OpenJdk-11 源码-系列」Serializable安全
先来看看Comparable
接口app
public interface Comparable<T> {
public int compareTo(T o);
}
复制代码
Comparable
接口是一个排序接口,若一个类实现了该接口并重写了compareTo
方法,就意味着这个类支持了排序。当存在一个实现了Comparable
接口的类的集合或数组,那么该集合或数组就能够经过Collections.sort / Arrays.sort
进行排序。函数
a.compareTo(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
方法的时候,若是value
是LATIN1
编码的话那么就会右移一位,减小一个字节数。
hash
hash
是String
在实例化的时候对hashcode
的一个缓存。因为在开发中String
会常常拿来比较,好比HashMap
中若是key
是String
类型,每次比较若是都从新计算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 str = "this is";
String str1 = str + "str"
复制代码
在底层,Java 对 String 的+的支持使用的是 StringBuilder
以及它的append
和toString
方法。即:
String str = "str";
String str1 = (new StringBuilder(String.valueOf(str))).append("str").toString();
复制代码
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
复制代码