String组成

String总结 - 图1

  • final数组:防止被修改
  • hash:缓存字符串hash码
  • offset
  • coder

    offset

    jdk6 String这个offset是干嘛的?

    This is the first char to use from the array.It has been introducted because some operations like substring create a new String using the original char array using a different offset. What does offset in java.lang.String holds? - Stack Overflow

  1. // Package private constructor which shares value array for speed.
  2. String(int offset, int count, char value[]) {
  3. this.value = value;
  4. this.offset = offset;
  5. this.count = count;
  6. }
  7. public String substring(int beginIndex, int endIndex) {
  8. if (beginIndex < 0) {
  9. throw new StringIndexOutOfBoundsException(beginIndex);
  10. }
  11. if (endIndex > count) {
  12. throw new StringIndexOutOfBoundsException(endIndex);
  13. }
  14. if (beginIndex > endIndex) {
  15. throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
  16. }
  17. return ((beginIndex == 0) && (endIndex == count)) ? this:
  18. new String(offset + beginIndex, endIndex - beginIndex, value);
  19. }
  • jdk6 substring与原String 是共享 Char[] 的。

offset 和count 标记了这个substring 在原来Char[]中的位置。
看似节约空间,但是会有内存泄露问题。

  • java7 不再共享char[],使用Arrays.copyOfRange()

image.png

  • java9 char[] 变为byte[],同时增加一个coder编码格式。

    coder【utf16】

    coder = 0 ,latin-1字符。
    coder = 1,代表utf16编码
    【为什么不用utf8呢?参见:Java 为什么使用 UTF-16 而不是更节省内存的 UTF-8? - 知乎

    构造方法

    构造string使用Arrays.copyOf,防止将引用传递进来
    image.png

    substring()

    构造一个新的String
    image.png

    String编码-如何正确的逐字符遍历字符串

    通常遍历一个字符串

    1. public static void main(String[] args) {
    2. //𤭢,复制到idea后自动就变为utf16编码了
    3. String a = "\uD852\uDF62";
    4. System.out.println("String:" + a);
    5. System.out.println("String length:" + a.length());
    6. char[] chars1 = a.toCharArray();
    7. for (int i = 0; i < chars1.length; i++) {
    8. System.out.println(chars1[i]);
    9. }
    10. }
  1. 输出:
  2. String:𤭢
  3. String length:2
  4. ?
  5. ?

为什么 String lenght是2呢?
因为java String中存储是通过Char[] 来存储(Java 9 是通过Byte[])。存储的编码是utf16。(为什么是utf16)
附上unicode到utf16的转换关系:
String总结 - 图5
属于“基本平面字符”的unicode和utf16二进制是一样的占用16位(一个char可以表示)。
但𤭢字属于“增补平面字符”,转为utf16需要32位(两个char)。

如何才能正确按字符遍历字符串呢?

  1. public static void main(String[] args) {
  2. String a = "\uD852\uDF62";
  3. System.out.println("String:" + a);
  4. System.out.println("String length:" + a.length());
  5. for (int offset = 0; offset < a.length();) {
  6. //从offset开始获取字符,返回字符对应的unicode
  7. int ch_unicode = a.codePointAt(offset);
  8. //查看字符unicode对应的二进制
  9. System.out.println("ch_unicode:" + Integer.toBinaryString(ch_unicode));
  10. //根据unicode编码 转为char[]存储(utf16格式)。
  11. char[] chars_utf16 = Character.toChars(ch_unicode);
  12. //遍历输出string中的char[]对应的二进制
  13. for (int i = 0; i < chars_utf16.length; i++) {
  14. System.out.println("utf16_char " + i + ":" + Integer.toBinaryString(chars_utf16[i]));
  15. }
  16. //将chars转为String
  17. String s = String.valueOf(chars_utf16);
  18. System.out.println("完整字符:" + s);
  19. //增加offset
  20. offset += Character.charCount(ch_unicode);
  21. }
  22. }
  • String.codePointAt():The codePointAt() method returns a non-negative integer that is the Unicode code point value.
  • Character.toChars():根据unicode转为utf16
  • String.valueOf(chars_utf16);:根据utf16数组转为String
  1. 输出:
  2. String:𤭢
  3. String length:2
  4. ch_unicode:10 0100 1011 0110 0010 -> 对应 𤭢 unicodeU+24B62
  5. utf16_char 01101 1000 0101 0010 -> 对应 String a = "\uD852\uDF62"; 中的D852
  6. utf16_char 11101 1111 0110 0010 -> 对应DF62
  7. 完整字符:𤭢

equals() compareTo() 区别

java.lang.Comparable#compareTo

  • 可返回正数、0、负数

java.lang.Object#equals

  • 返回bool

String的HashCode()

image.png