Java·String

91 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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的对象