### String
在JAVA語言中有八種基本類型包裝類和一種比較特殊的類型String。這些類型為了使他們在運行過程中速度更快,更節省內存,都提供了一種常量池的概念。常量池就類似一個JAVA系統級別提供的緩存。因為String不可變的性質,因此Java內部實現了String常量池。當一個String被創建時,會先去常量池查看有沒有值相同的示例,有的話直接返回。節省了內存,加快了字符串的加載速度。不可變的對象也可以保證在并發中保持線程安全
其中String類型的常量池比較特殊。它的主要使用方法有兩種:
* 直接使用雙引號聲明出來的String對象會直接存儲在常量池中。
* 如果不是用雙引號聲明的String對象,可以使用String提供的intern方法。intern 方法會從字符串常量池中查詢當前字符串是否存在,若不存在就會將當前字符串放入常量池中
### 構造器


### 特性
* 字符串常量,實際上也是String對象
* 所有不是通過new創建的String都是放在常量池中
* String類型的對象是不可變的
* String實現了CharSequence接口
### String對象創建方式
```
String str1 = "abcd";
String str2 = new String("abcd");
```
這兩種不同的創建方法是有差別的,第一種方式是在常量池中拿對象,第二種方式是直接在堆內存空間創建一個新的對象。
只要使用new方法,便需要創建新的對象
### 連接表達式+\(加號\)
1. 只有使用引號包含文本的方式創建的String對象之間使用“+”連接產生的新對象才會被加入字符串池中。
2. 對于所有包含new方式新建對象(包括null)的“+”連接表達式,它所產生的新對象都不會被加入字符串池中
```
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";
String str4 = str1 + str2;
System.out.println(str3 == str4);//false
String str5 = "string";
System.out.println(str3 == str5);//true
```
```
1、 Sting s; //定義了一個變量s,沒有創建對象;
2、 = // 賦值,將某個對象的引用(句柄)賦給s ,沒有創建對象;
3、 “abc” //創建一個對象;
4、 new String(); // 創建一個對象。
```
### 常用方法





* length 返回字符串長度
* isEmpty 判斷字符串是否為空
* charAt 根據索引位置獲取char
* getChars 復制對應位置范圍的char到數組中
* equals, equalsIgnoreCase 對比順序依次為引用地址,char數組長度,char數組內容
* compareTo 對比字符串大小
* startsWith, endsWith 判斷前后綴
* hashCode 計算hash值, 公式為s\[0\]\*31^\(n-1\) + s\[1\]\*31^\(n-2\) + ... + s\[n-1\]
* indexOf 查找首次出現的位置
* lastIndexOf 查找最后出現的位置
* substring 返回子串(舊版本是返回一個引用在父串的一個新串,節省重新分配內存。但實際如果子串引用了一個占用極大的父串,會因為子串一直被使用導致父串沒法被垃圾回收,新版本substring每次重新復制char數組)
* concat 拼接字符串(拼接char數組,重新創建字符串)
* replace 用新字符替換所有的舊字符(會先遍歷一次char數組,尋找時候存在,再去替換,避免每次都要分配char數組)
* matches 判斷是否符合正則 (復用Pattern.matches\(\)方法)
* contains 判斷是否包含子串(復用indexOf\(\)方法)
* replaceFirst 只替換一次
* replaceAll 替換所有正則符合的地方
* split 按照正則分割字符串
* toLowerCase 返回小寫
* toUpperCase 返回大寫
* trim 去除前后空格
* toCharArray 重新復制char數組返回
* join\(CharSequence delimiter, CharSequence... elements\)
```
String.join(",", "you", "bao", "luo");
//out: you,bao,luo
```
* equals\(Object anObject\)
String.equals\(\)代碼邏輯:
1. 判斷傳入的對象與當前對象是否為同一個對象,如果是就直接返回true;
2. 判斷傳入的對象是否為String,若不是則返回false\(如果為null也不成立\);
3. 判斷傳入的String與當前String長度是否一致,若不一致則返回false;
4. 循環對比兩個字符串的char\[\]數組,逐個對比字符是否一致,若不一致則直接返回false;
5. 循環結束沒有找到不匹配的則返回true;
```
JDK8源碼:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
```
* intern\(\):naive方法,直接返回常量池中的引用
當調用intern\(\)方法時,JVM會在常量池中通過equals\(\)方法查找是否存在等值的String,如果存在則直接返回常量池中這個String對象的地址;如果不存在則會創建等值的字符串放入常量池\(即等值的char\[\]數組字符串,但是char\[\]是新開辟的一份拷貝空間\),然后再返回這個新創建空間的地址;
注意:String的String Pool是一個固定大小的Hashtable,默認值大小長度是1009
在常量池查找等值String時,通常不止一個字符串而是多個字符串因此效率會比較低,另外為保證唯一性,需要有鎖的介入;
```
String str1 = "ab";
String str2 = new String("ab");
System.out.println(str1== str2);//false
System.out.println(str2.intern() == str1);//true
System.out.println(str1== str2);//false
str2 = str2.intern();
System.out.println(str1== str2);//true
```
### 知識點
* 在調用x.toString\(\)的地方可以用""+x替代;
* 字符串的+拼接操作
```
public static void main(String[] args) throws InterruptedException {
String s = "a";
String st = s + "b" + "c";
}
javap out====>
Code:
stack=3, locals=3, args_size=1
0: ldc #19 // String a
2: astore_1
3: new #21 // class java/lang/StringBuilder
6: dup
7: aload_1
8: invokestatic #23 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
11: invokespecial #29 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
14: ldc #32 // String b
16: invokevirtual #34 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #38 // String c
21: invokevirtual #34 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #40 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore_2
28: return
```
* StringBuffer是線程安全操作
```
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
```
* StringBuilder非線程安全
```
public StringBuilder append(String str) {
super.append(str);
return this;
}
```
```
System.err.println("hello,world"); ##hello,world實際是String對象
```
### printf格式化輸出

**FAQ**
1. String str1 = "abc"; System.out.println\(str1 == "abc"\);
步驟:
a> 棧中開辟一塊空間存放引用str1;
b> String池中開辟一塊空間,存放String常量"abc";
c> 引用str1指向池中String常量"abc";
d> str1所指代的地址即常量"abc"所在地址,輸出為true;
1. String str2 = new String\("abc"\); System.out.println\(str2 == "abc"\);
步驟:
a> 棧中開辟一塊空間存放引用str2;
b> 堆中開辟一塊空間存放一個新建的String對象"abc";
c> 引用str2指向堆中的新建的String對象"abc";
d> str2所指代的對象地址為堆中地址,而常量"abc"地址在池中,輸出為false;
注意:對于通過new產生的對象,會先去常量池檢查有沒有 “abc”,如果沒有,先在常量池創建一個 “abc” 對象,然后在堆中創建一個常量池中此 “abc” 對象的拷貝對象;
2. String s2 = new String\(“Hello”\); 產生幾個對象?
首先,在jvm的工作過程中,會創建一片的內存空間專門存入string對象。我們把這片內存空間叫做string池;
String s2 = new String\(“Hello”\);jvm首先在string池內里面看找不找到字符串"Hello",如果找到不做任何事情;否則創建新的string對象,放到string池里面。由于遇到了new,還會在內存Heap上(不是string池里面)創建string對象存儲"Hello",并將內存上的(不是string池內的)string對象返回給s2。
Re: 如果常量池中原來沒有“Hello”, 則創建兩個對象。如果原來的常量池中存在“Hello”時,就是一個對象;
3. 其它
\`\`\`
String str1 = "a";
String str2 = "b";
String str3 = str1 + "b";
//str1 和 str2 是字符串常量,所以在編譯期就確定了。
//str3 中有個 str1 是引用,所以不會在編譯期確定。
//又因為String是 final 類型的,所以在 str1 + "b" 的時候實際上是創建了一個新的對象,在把新對象的引用傳給str3
final String str1 = "a";
String str2 = "b";
String str3 = str1 + "b";
//這里和\(3\)的不同就是給 str1 加上了一個final,這樣str1就變成了一個常量。
//這樣 str3 就可以在編譯期中就確定了
\`\`\`
【參考資料】
[https://tech.meituan.com/in\_depth\_understanding\_string\_intern.html](https://tech.meituan.com/in_depth_understanding_string_intern.html) \#\#【深入解析String\#intern -- 美團】
[http://rednaxelafx.iteye.com/blog/774673](http://rednaxelafx.iteye.com/blog/774673)
- java演變
- JDK各個版本的新特性
- JDK1.5新特性
- JDK1.6新特性
- JDK1.7新特性
- JDK1.8新特性
- JAVA基礎
- 面向對象特性
- 多態
- 方法重載
- 方法重寫
- class
- 常量
- 訪問修飾符
- 類加載路徑
- java-equals
- 局部類
- java-hashCode
- Java類初始化順序
- java-clone方法
- JAVA對象實例化的方法
- 基礎部分
- JAVA基礎特性
- JAVA關鍵字
- javabean
- static
- 日期相關
- final
- interface
- 函數式接口
- JAVA異常
- 異常屏蔽
- try-with-resource資源泄露
- JAVA引用
- WeakReference
- SoftReference
- PhantomReference
- 位運算符
- try-with-resource語法糖
- JDK冷知識
- JAVA包裝類
- JAVA基本類型與包裝類
- java.lang.Boolean
- java.lang.Integer
- java.lang.Byte
- java.lang.Short
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Character
- 日期相關
- TemporalAdjusters
- String
- 字符串常量池
- String拼接
- String編譯期優化
- StringBuilder&StringBuffer
- intern
- 注解
- java標準注解
- 內置注解
- 元注解
- 自定義注解
- 注解處理器
- JVM注解
- Java8 Annotation新特性
- 反射-Reflective
- Reflection
- Class
- Constructor
- Method
- javabean-property
- MethodHandles
- 泛型
- 類型擦除
- bridge-method
- Accessor&Mutator方法
- enum
- JAVA數組
- finalize方法
- JAR文件
- JAVA高級編程
- CORBA
- JMX
- SPI
- Java SPI使用約定
- ServiceLoader
- 實際應用
- IO
- 工具類
- JDK常用工具類
- Objects
- System
- Optional
- Throwable
- Collections
- Array
- Arrays
- System
- Unsafe
- Number
- ClassLoader
- Runtime
- Object
- Comparator
- VarHandle
- 數據結構
- 棧-Stack
- 隊列(Queue)
- Deque
- PriorityQueue
- BlockingQueue
- SynchronousQueue
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- ConcurrentLinkedQueue
- 列表
- 迭代器
- KV鍵值對數據類型
- HashMap
- TreeMap
- Hash沖突
- ConcurrentHashMap
- JDK1.7 ConcurrentHashMap結構
- jdk7&jdk8區別
- 集合
- Vector
- Stack
- HashSet
- TreeSet
- ArrayList
- LinkedList
- ArrayList && LinkedList相互轉換
- 線程安全的集合類
- 集合類遍歷性能
- 并發容器
- CopyOnWriteArrayList
- ConcurrentHashMap
- 同步容器
- BitMap
- BloomFilter
- SkipList
- 設計模式
- 設計模式六大原則
- 單例模式
- 代理模式
- 靜態代理
- 動態代理
- JDK動態代理
- cglib動態代理
- spring aop
- 策略模式
- SpringAOP策略模式的運用
- 生產者消費者模式
- 迭代器模式
- 函數式編程
- 方法引用
- 性能問題
- Lambda
- Lambda類型檢查
- Stream
- findFirst和findAny
- reduce
- 原始類型流特化
- 無限流
- 收集器
- 并行流
- AOP
- 靜態織入
- aspect
- aspect的定義
- AspectJ與SpringAOP
- 動態織入
- 靜態代理
- 動態代理
- JDK動態代理
- CGLib動態代理
- Spring AOP
- SpringAOP五種通知類型
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
- Aspect優先級
- SpringAOP切點表達式
- within
- execution
- 嵌套調用
- 系統優化與重構
- 重疊構造器模式
- 工具類構造器優化
- 常見面試題
- new Object()到底占用幾個字節
- 訪問修飾符
- cloneable接口實現原理
- 異常分類以及處理機制
- wait和sleep的區別
- 數組在內存中如何分配
- 類加載為什么要使用雙親委派模式,有沒有什么場景是打破了這個模式
- 類的實例化順序
- 附錄
- JAVA術語
- FAQ
- 墨菲定律
- 康威定律
- 軟件設計原則
- 阿姆達爾定律
- 字節碼工具
- OSGI