一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
Java·String
1. String类特点
public final class String implements Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
private final byte coder;
private int hash;
private static final long serialVersionUID = -6849794470754667710L;
static final boolean COMPACT_STRINGS = true;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new String.CaseInsensitiveComparator();
static final byte LATIN1 = 0;
static final byte UTF16 = 1;
}
- 类不可被继承
- 所创建的对象的value属性的值不可变
- 实现了Serializable接口,支持序列化以及反序列化
- 实现了Comparable接口,支持比较
2. 字符串常量池
为了减少在JVM中创建字符串的数量,JVM内部提供了一个字符串常量池。
当创建String对象时,JVM会先检查字符串常量池,如果字符串常量池中某个String对象的value属性的值与所要创建的String对象的value属性的值相等,就直接返回此对象的引用(即不创建String对象)。
如果不存在这个String对象,就会在堆中开辟空间存放这个String对象,且设置此String对象的属性value的值为字符串常量池中某个value属性的值为所要创建的String对象的value属性的值的String对象的value属性的值。
3. String常见问题
3.1 String类中的equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
} else {
if (anObject instanceof String) {
String aString = (String)anObject;
if (this.coder() == aString.coder()) {
return this.isLatin1() ? StringLatin1.equals(this.value, aString.value) : StringUTF16.equals(this.value, aString.value);
}
}
return false;
}
}
equals方法首先先判断二者引用是否相同,若相同,则返回true;若不相同则判断参数是否为String类的对象。若相同,则向下转型其为String类对象,并判断二者使用的编码是否一致。若一致,则判断其使用的是Latin1编码还是UTF16编码,并进行二者的value属性的值是否相同。
3.2 判断通过对String对象直接赋值以及通过构造函数创建String对象二者是否相同
- 案例
public void case() {
String string1 = "string";
String string2 = new String("string");
System.out.println(string1 == string2); // false
System.out.println(string1.equals(string2)); // true
}
- 解析
String string1 = "string";
在将java文件转换为class文件的时候,在执行此语句时,JVM会先去字符串常量池查找是否存在某个String对象的value属性的值为string,由于不存在此String对象,所以会在字符串常量池中开辟了内存空间来存放这个String对象。
String string2 = new String("string");
同样的,在将java文件转换为class文件的时候,在执行此语句时,JVM会先去字符串常量池查找是否存在某个String对象的value属性的值为string,由于存在此String对象,但我们使用new关键字,所以会堆中先开辟空间存放我们所要创建String对象,并设置这个刚创建的String对象的value值为JVM所找到的String对象的value值。
3.3 通过构造函数创建String对象的个数
- 案例
public void case() {
String string1 = new String("string");
}
- 解析
在将java文件转换为class文件的时候,在执行此语句时,JVM会先去字符串常量池查找是否存在某个String对象的value属性的值为string,由于不存在此String对象,所以会在字符串常量池中创建这个String对象,然后由于我们使用new关键字,所以会堆中先开辟空间存放我们所要创建String对象,并设置这个堆中的String对象的value属性的值为字符串常量池中的String对象的value属性的值。即执行此语句时最终生成两个String对象。
3.4 判断两个字符串对象是否相同
- 案例
public void case() {
String string1 = "string";
String string2 = "str";
String string3 = string2 + "ing";
System.out.println(string1 == string3); # false
System.out.println(string1.equals(string3)); # true
}
- 解析
String string3 = string2 + "ing";
此语句执行时,使用 + 将字符串对象以及其他类型的数据连接起来,得到一个新的字符串对象。这个新的字符串对象本质是通过StringBuffer对象中的append方法将二者连接起来,并调用toString方法返回这个新的字符串对象。
因此这个新的字符串对象并不是处于字符串常量池中的value属性值为string的对象。