String是我们使用最频繁的一个对象类型,但它的性能问题却是最容易忽略的。
它作为Java语言中重要的数据类型,是内存中占据空间最大的一个对象。高效的使用字符串,可以提升系统的整体性能。
思考:通过三种方式创建三个对象,两两匹配,每组被匹配的对象是否相等:

  1. String str1 = "abc";
  2. String str2 = new String("123");
  3. String str3 = str2.intern();
  4. System.out.println(str1 == str2);
  5. System.out.println(str2 == str3);
  6. System.out.println(str1 == str3);

1. String 对象是如何实现的?

1.1 在Java 6 以及之前的版本

  • String对象是对char数组进行了封闭实现的对象
  • 主要有四个成员变量
    • char数组
    • 偏移量 offset
    • 字符数量 count
    • 哈希值 hash
  • String 通过 offset 和 count 来定位 char[] 数组,获取字符串

    • 这么做可以高效、快速地共享数组对象,同时节省内存空间
    • 但有可能会导致内存泄漏

      1.2 从Java 7 开始到 Java 8

      String类中不再有 offset 和 count 两个变量
  • 好处是对象占用内存稍微少了些

  • substring() 方法也不再共享 char[] ,从而解决了可能导致的内存泄漏问题

    1.3 从Java 9 版本开始

    char[] 字段改成了 byte[] 字段,又维护了一个新的属性 coder,它是编码格式的标识
    为什么这么修改?

  • char 字符占16位,2个字节。存储单字节编码内的字符就显得非常浪费

  • coder 的作用是,在计算字符串长度或者使用 indexOf() 时,需要根据这个字段,判断如何计算字符串长度
    • coder 默认属性有 0 和 1 两个值, 0代表 Latin-1(单字节编码),1 代表 UTF-16 。
    • 如果String 判断字符串只包含Latin-1,则 coder 属性值为0,反之则为1

      2. String 对象的不可变性