六、String
概览
String 被声明为 final,因此它不可被继承。(Integer 等包装类也不能被继承)
在 Java 8 中,String 内部使用 char 数组存储数据。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final byte[] value;
/** The identifier of the encoding used to encode the bytes in {@code value}. */
private final byte coder;
}
value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。
不可变的好处
1. 可以缓存 hash 值
因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
2. String Pool 的需要
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
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
![](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 />(这图表示的是新版的)
<a name="Dxrfq"></a>
#### intern() 1.8
调用字符串对象的intern方法,会将该字符串对象尝试放入到串池中
- 如果串池中没有该字符串对象,则放入成功
- 如果有该字符串对象,则放入失败
无论放入是否成功,都会返回**串池中**的字符串对象
以下是jdk8的代码演示:
```java
public static void main(String[] args) {
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
//-------------------------------------------------------------------
//["1"]本来就有
String s3 = new String("1") + new String("1");
// new String("1")本来就有 new String("11")
//此时常量池中是没有 "11"的
s3.intern();
//创建"11",objc3引用的对象的地址和"11"地址是相同的.
String s4 = "11";
System.out.println(s3 == s4);
}
false true
注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向。
public static void main(String[] args) {
String s = new String("1");
String s2 = "1";
s.intern();
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
String s4 = "11";
s3.intern();
System.out.println(s3 == s4);
}
false false
String常用的方法有哪些
- indexOf():返回指定字符的索引。
- charAt():返回指定索引处的字符。
- replace():字符串替换。
- trim():去除字符串两端空⽩。
- split():分割字符串,返回⼀个分割后的字符串数组。
- getBytes():返回字符串的 byte 类型数组。
- length():返回字符串⻓度。
- toLowerCase():将字符串转成⼩写字⺟。
- toUpperCase():将字符串转成⼤写字符。
- substring():截取字符串。 起始位置[包括] 终止位置[不包括]
- equals():字符串⽐较
- toCharArray(): 将字符串转换为char数组
- 将byte数组一部分转化为字符串
byte[] bytes={97,98,99};
System.out.println(new String(bytes,1,2))//bc