>[success] # 字符串總結
1. `String s = "abc";` 這種直接賦值字符串常量的情況,**此時字符串abc是存在字符串常量池中的**,整個創建過程**先檢查字符串常量池中有沒有字符串abc,如果有,不會創建新的,而是直接復用。如果沒有abc,才會創建一個新的。**
2. 通過 `new` 關鍵字幾種形式創建字符串,一定是在堆里面開辟了一個空間,`new`出來的,在**堆里面的地址**
3. 字符串的比較推薦使用`equals` 方法,因為在 `java` 中`==`號比較
* 如果比較的是**基本數據類型**:比的是具體的數值是否相等
* 如果比較的是**引用數據類型**:比的是地址值是否相等
在 `java` 中 String 是**引用類型**,在使用雙等時候比較其實是內存地址,因此要注意的是比較內容使用提供的`equals` 方法
4. 關于字符串的拼接**加號浪費性能**
* 如果拼接的全部都為字符串,在拼接時候會觸發字符串的優化機制,**在編譯轉換為class 文件后就已經獲取了最終值的結果**

~~~
public class StringTest {
public static void main(String[] args) {
String str = "a" + "b" + "c"; // 實際在編譯后class 就是"abc"
}
}
~~~
* 如果拼接是變量在**JDK8以前**:系統底層會自動創建一個`StringBuilder`對象,然后再調用其`append`方法完成拼接。拼接后,再調用其`toString`方法轉換為`String`類型,而`toString`方法的底層是直接`new String`了一個字符串對象(可看源碼實現)。也就是說**一個加號使用變量拼接字符串會創建兩個對象**分別是`StringBuilder` 和 `String`

~~~
public class StringTest {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b" + s1; // 實際做了 new StringBuilder().append(s1).append("b").toString();
}
}
~~~
* **JDK8版本**:系統會預估要字符串拼接之后的總大小,把要拼接的內容都放在數組中,此時也是產生一個新的字符串。(?????)
*****
* 加號浪費性能案例說明method1 > method2 ,結論**字符串拼接的時候有變量參與:在內存中創建了很多對象浪費空間,時間也非常慢不要直接使用加號創建**
~~~
public static void main(String[] args) {
int times = 1_0000;
long startTime = System.currentTimeMillis();
method1(times);
System.out.println("method1 耗時:" + (System.currentTimeMillis() - startTime));
startTime = System.currentTimeMillis();
method2(times);
System.out.println("method2 耗時:" + (System.currentTimeMillis() - startTime));
}
public static void method1(int times) {
String s = "";
for (int i = 0; i < times; i++) {
s += i;
}
}
public static void method2(int times) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < times; i++) {
sb.append(i);
}
String s = sb.toString();
}
~~~
5. 在字符串的拼接時候更多應該使用`StringBuilder` 這種內容可變的容器,創建后只有一個對象在內存空間
6. [參考鏈接](https://www.jianshu.com/p/dc8a458d5933),`StringBuilder`這種擴容也是存在一定規則的(**容量和長度的區別,容量表示最多能裝多少,長度表示實際內容**)
* 默認創建一個長度為16的字節數組
* 添加的內容長度小于16,直接存
* 添加的內容大于16會擴容(原來的容量*2+2)
* 如果擴容之后還不夠,以實際長度為準
~~~
public class StringTest {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder();
sb1.append("abc");
System.out.println(sb1.capacity()); // 容量 16
System.out.println(sb1.length()); // 長度 3
// 添加內容大于目前的容量。那么會進行擴容公式 原始容量*2 + 2
sb1.append("defghijklmnopqrstvuwxzy");
System.out.println(sb1.capacity()); // 容量 16*2 +2 = 34
System.out.println(sb1.length()); // 長度 26
// 如果添加內容大于 實際擴容長度應該為16*2 +2 = 34,但添加的內容遠遠大于34 因此
// 擴容和長度值相等
StringBuilder sb2 = new StringBuilder();
sb2.append("111111111111111111111111111111111111");
System.out.println(sb2.capacity()); // 容量 36
System.out.println(sb2.length()); // 長度 36
}
}
~~~
* 如果你默認有構造參數初始容量為**16 + 字符串長度**
~~~
public class StringTest {
public static void main(String[] args) {
StringBuilder sb1 = new StringBuilder("abc");
sb1.append("abc");
System.out.println(sb1.capacity()); // 容量 19
System.out.println(sb1.length()); // 長度 6
}
}
~~~
>[danger] ##### 面試題
* 因為 s3 在拼接后實際是產生新對象在堆中,但`s1` 實際在字符串常量池中,因此比較后地址值不同
~~~
public class StringTest {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "ab";
String s3 = s2 + "c";
System.out.println(s1 == s3); // false
}
}
~~~
* s2 轉換為class 文件后實際 "abc",s1 已經在常量池中創建了,因此s2直接復用即可,所以內存地址指向相同
~~~
public class StringTest {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "a" + "b" + "c"; // 轉換為class 文件后實際 "abc"
System.out.println(s1 == s2); // true
}
}
~~~
- windows -- 環境變量
- Vscode -- 編寫java
- 初始java
- java -- 關鍵字
- 編寫第一個java程序
- java -- 注釋
- 計算機存儲 -- 進制
- java -- 類型
- java -- 變量
- 數字類型
- 布爾類型
- 字符類型
- 類型轉換
- 雙等比較是什么
- java -- 運算符
- 算數運算符
- 字符串拼接
- 關系/比較運算符
- 自增減運算符
- 邏輯運算符
- 三目運算
- 賦值運算符
- 移位運算符
- 位運算符
- 運算符優先級
- java -- 流程控制語句
- if /else if /if -- 判斷
- switch case分支結構
- for -- 循環
- 用雙重for循環
- while -- 循環
- do while -- 循環
- 案例練習
- java -- 數組
- 數組的存儲
- 數組的增刪改查
- 數組的特點
- 數組案例
- 二維數組
- 數組的工具方法
- java -- 方法
- java -- 方法的重載
- java -- 方法的調用流程
- java -- 類方法傳參注意事項
- java -- 方法練習案例
- 對比 return break continue
- for each循環
- java -- 基礎練習
- java -- 面向對象
- java -- 創建類和對象
- java -- 訪問控制符
- java -- 類成員方法
- java -- 構造方法
- java -- this
- java -- 封裝
- java -- 對象內存圖
- java -- 創建對象案例
- java -- static
- java -- 繼承
- super -- 關鍵字
- java -- 構造塊和靜態代碼塊
- java -- 重寫
- java -- final
- java -- 多態
- java -- 抽象類
- java -- 接口
- 引用類型數據轉換
- 綜合案例
- java -- 內部類
- java -- 回調模式
- java -- 枚舉類型
- java -- switch 使用枚舉
- java -- 枚舉方法使用
- java -- 枚舉類實現接口
- java -- javaBean
- java -- package 包
- java -- import
- java -- 遞歸練習
- java -- 設計模式
- 單例模式
- java -- 注解
- java -- 元注解
- Java -- 核心類庫
- java -- 處理字符串
- Java -- String
- String -- 常用方法
- String -- 正則
- Java -- StringBuilder 和 StringBuffer
- 知識點
- Java -- StringJoiner 字符串拼接
- 練習題
- 字符串的總結
- Java -- 包裝類
- Integer
- Double
- Boolean
- Character
- java -- 集合類
- java -- util.Collection
- Iterator接口
- java -- util.List
- java -- ArrayList
- java -- util.Queue
- java -- util.Set
- java -- util.Map
- java -- util.Collections
- Java -- Math
- Java -- java.lang
- Java -- Object
- Java -- 獲取當前時間戳
- Java -- 異常
- Java -- java.util
- java -- Date
- java -- Calender
- Java -- java.text
- Java -- SimpleDateFormat
- Java -- java.time
- Java -- java.io
- java -- io.File
- java -- 泛型
- IDEA -- 用法