六、String

概览

String 被声明为 final,因此它不可被继承。(Integer 等包装类也不能被继承)
在 Java 8 中,String 内部使用 char 数组存储数据。

  1. public final class String
  2. implements java.io.Serializable, Comparable<String>, CharSequence {
  3. /** The value is used for character storage. */
  4. private final char value[];
  5. }

在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。

  1. public final class String
  2. implements java.io.Serializable, Comparable<String>, CharSequence {
  3. /** The value is used for character storage. */
  4. private final byte[] value;
  5. /** The identifier of the encoding used to encode the bytes in {@code value}. */
  6. private final byte coder;
  7. }

value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。

不可变的好处

1. 可以缓存 hash 值
因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
2. String Pool 的需要
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
String - 图2
3. 安全性
String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 的那一方以为现在连接的是其它主机,而实际情况却不一定是。
4. 线程安全
String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

String, StringBuffer and StringBuilder

1. 可变性

  • String 不可变
  • StringBuffer 和 StringBuilder 可变

2. 线程安全

  • String 不可变,因此是线程安全的
  • StringBuilder 不是线程安全的
  • StringBuffer 是线程安全的,内部使用 synchronized 进行同步

    String Pool

    概念:String str = “abc” str代表字符串常量 “abc”代表字符串对象

    1.使用字面量创建字符串对象

    String str = “abc”
    将字符串常量池中的”abc”地址返回给str,如果没有创建再返回。

    2.使用new关键字新建一个字符串对象

    String str = new String(“abc”);
    将字符串常量池中的”abc”复制一份给堆中,如果没有创建再给,然后将堆中对象的内存地址返回给str。 ```java String s1 = “abc”; String s2 = “abc”; System.out.println(s1==s2);//true

String x = new String(“xyz”); String y= new String(“xyz”); System.out.println(x=y); //false

  1. ![](https://cdn.nlark.com/yuque/0/2021/png/22016332/1637405438509-397fe510-d6cf-4ab1-a7a3-604113f7bdcc.png?x-oss-process=image%2Fresize%2Cw_1152%2Climit_0#averageHue=%23fafafa&from=url&id=Wvi01&originHeight=648&originWidth=1152&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)<br />在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。<br />(这图表示的是新版的)
  2. <a name="Dxrfq"></a>
  3. #### intern() 1.8
  4. 调用字符串对象的intern方法,会将该字符串对象尝试放入到串池中
  5. - 如果串池中没有该字符串对象,则放入成功
  6. - 如果有该字符串对象,则放入失败
  7. 无论放入是否成功,都会返回**串池中**的字符串对象
  8. 以下是jdk8的代码演示:
  9. ```java
  10. public static void main(String[] args) {
  11. String s = new String("1");
  12. s.intern();
  13. String s2 = "1";
  14. System.out.println(s == s2);
  15. //-------------------------------------------------------------------
  16. //["1"]本来就有
  17. String s3 = new String("1") + new String("1");
  18. // new String("1")本来就有 new String("11")
  19. //此时常量池中是没有 "11"的
  20. s3.intern();
  21. //创建"11",objc3引用的对象的地址和"11"地址是相同的.
  22. String s4 = "11";
  23. System.out.println(s3 == s4);
  24. }

false true

注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向。
String - 图3

  1. public static void main(String[] args) {
  2. String s = new String("1");
  3. String s2 = "1";
  4. s.intern();
  5. System.out.println(s == s2);
  6. String s3 = new String("1") + new String("1");
  7. String s4 = "11";
  8. s3.intern();
  9. System.out.println(s3 == s4);
  10. }

false false

String - 图4

String常用的方法有哪些

  • indexOf():返回指定字符的索引。
  • charAt():返回指定索引处的字符。
  • replace():字符串替换。
  • trim():去除字符串两端空⽩。
  • split():分割字符串,返回⼀个分割后的字符串数组。
  • getBytes():返回字符串的 byte 类型数组。
  • length():返回字符串⻓度。
  • toLowerCase():将字符串转成⼩写字⺟。
  • toUpperCase():将字符串转成⼤写字符。
  • substring():截取字符串。 起始位置[包括] 终止位置[不包括]
  • equals():字符串⽐较
  • toCharArray(): 将字符串转换为char数组
  • 将byte数组一部分转化为字符串
    1. byte[] bytes={97,98,99};
    2. System.out.println(new String(bytes,1,2))//bc