[TOC]
>[success] # Java -- String
1. `String `是java 定義好的一個類,定義在 **java.lang** 包中,所以使用不需要導包
2. `String `對象用于保存字符串,也就是一組字符序列,從jdk1.9開始該類的底層不使用**char\[\]** 來存儲數據,而是改成 **byte\[\]** 加上編碼標記,從而節約了一些空間
2.1. **java1.8** 使用**UTF-16**,一個字符(不區分字母還是漢字)占兩個字節
2.2. **jdk1.9** 新增一個`coder` 來劃分存儲,如果其本身是一個字節就用**byte**,如果大于一個字節使用**UTF-16**
3. 創建 java 字符串方式有兩種,一種是**字符串字面值**,一種使**用String 類**
4. **java程序中**所有**字符串文字** 例如 `String name = "1"` 都被實例為`String `類的對象
5. 字符串不可變,它們的值在創建后**不能被更改**
6. **String 是final 類**,**不能被其他的類繼承**
7. java8 `String` 有屬性 `private final char value[]`; 用于存放字符串內容,java9 則在 `private final byte[] value`
8. 以**字符串字面值** 即""方式給出的字符串,只要字符序列相同(順序和大小寫),無論在程序代碼中出現幾次,**JVM 都只會建立一個 String 對象**,并在字符串池中維護
* 注關于第二條參考資料
[# UTF-16 揭秘Java String中的字符編碼,全程高能無廢話 #安員外很有碼](https://www.bilibili.com/video/BV13U4y1y7LP/?spm_id_from=333.788&vd_source=1e4d2f8cd0a4f142adfbc4bf47b6c113)
[# jdk9為何要將String的底層實現由char\[\]改成了byte\[\]?](https://www.zhihu.com/question/447224628)
>[info] ## String類接口實現和父類
~~~
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}
~~~
1. `String` 繼承自 `Object` 因此具備 `Object` 方法
2. 實現了接口 `implements Serializable, Comparable<String>, CharSequence`
2.1. **Serializable**: **String 可以串行化:可以在網絡傳輸**
2.2. **Comparable** : **String 對象可以比較大小**
2.3. **CharSequence**: **描述字符串結構**
3. **final 修飾類不可繼承**,即String類不存在子類,Java程序中有且僅有一個String類,**說明**:繼承和方法重寫在為程序帶來靈活性的同時,也會帶來多個子類行為不一致的問題。為保證所有JDK使用者都使用同一String類的固有實現,不允許自定義修改其中的具體實現,不希望體現個體差異性,故使用final終結器,防止String類被繼承后進行方法重寫。
>[info] ## String 字符串存儲和不可修改原因
1.` String`字符串實際上底層是以**字符數組value實現存儲的**
* java8
~~~
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final char[] value;
...
}
~~~
java 11
~~~
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
...
}
~~~
2. **value 是一個final類型**, 不可以修改,即**value不能指向新的地址,但是單個字符內容是可以變化**
~~~
final char[] value = { 'a', 'b', 'c' };
char[] v2 = { 't', 'o', 'm' };
value[0] = 'H'; // final 不改變地址情況下可以修改內容
// value = v2; 不可以修改 value地址
~~~
3. ` String`,訪問控制修飾符為**private**,類外無法直接訪問,且String類并未為value數組提供相應public權限的getter和setter方法,故外部無法訪問此屬性,無法對此字符數組進行修改,即無法對此字**符串進行修改**
>[info] ## 創建字符串
1. **字符串字面值** 就是使用雙引號包裹,創建形式
~~~
// 使用直接賦值的方式獲取一個字符串對象
String name = "123"
~~~
2. 通過`String` 類創建形式,常見的構造方法
| 方法聲明| 功能介紹|
| --- | --- |
|String() |使用無參方式構造對象得到空字符序列|
|String(byte[] bytes, int offset, int length)|使用bytes數組中下標從offset位置開始的length個字節來構造對象|
|String(byte[] bytes) |使用bytes數組中的所有內容構造對象|
|String(char[] value, int offset, int count)|使用value數組中下標從offset位置開始的count個字符來構造對象|
|String(char[] value) |使用value數組中的所有內容構造對象|
|String(String original) |根據參數指定的字符串內容來構造對象,新創建對象為參數對象的副本|
>[danger] ##### 常用構造創建 -- 使用案例
~~~
public class TestStr {
public static void main(String[] args) {
// 1. 使用無參方式構造對象打印
String str1 = new String();
// 打印結果為""表示空字符串對象
System.out.println("str1:" + str1);
// 2. 使用byte 數組來創建字符串
byte[] bArr = { 97, 98, 99, 100, 101 };
/*
* bytes[] 要解碼為字符的字節數組
* int offset 要解碼的第一個字節的索引 offset大于 bytes.length - length 和 offset 不能為負數
* int length 要解碼的字節數 不能為負數
*/
String str2 = new String(bArr, 1, 3);
// 打印結果bcd ,將每個整數對應他的ascii轉換對應字符串
System.out.println("str1:" + str2);
// 3. 使用整個字節數組來構造字符串對象
String str3 = new String(bArr);
// 打印結果 abcde
System.out.println("str3:" + str3);
// 4. 使用字符數組
char[] charArr = { 97, 98, 'h', 'm' };
String str4 = new String(charArr);
// 打印結果abhm 將整個字符數組拼接
System.out.println("str4:" + str4);
// 5. 使用指定字符數組 位置拼接
/*
* char[] 要解碼為字符數組
* int offset 要解碼的第一個字節的索引 offset大于 bytes.length - length 和 offset 不能為負數
* int length 要解碼的字節數 不能為負數
*/
String str5 = new String(charArr, 1, 3);
// 打印結果 bhm
System.out.println("str5:" + str5);
// 6 使用字符串來構造
String str6 = new String("你好");
// 打印結果 你好
System.out.println("str6:" + str6);
}
}
~~~
>[danger] ##### 常用的構造方法應用場景
1. `char[]` 字符數組作為 **String 構造參數**時候,因為字符串是不能被更改的,因此我們可以去更改`char[]`數組重新生成我們想要的字符串例如`abc --> {'a','b','c'} --> {'Q','b','c'} --> "Qbc"`
2. 當在網絡當中傳輸的數據其實都是字節信息,我們一般要把字節信息根據ascii 碼進行轉換,轉成字符串,就用到`byte[]` 字節數組作為 **String 構造參數**
3. `String str = "abc";`
相當于:
~~~
char data[] = {'a', 'b', 'c'};
String str = new String(data);
~~~
>[danger] ##### 創建字符串對象兩種方式的區別
1. **直接賦值方式創建**,? 以`""`方式給出的字符串,只要字符序列相同(順序和大小寫),系統會檢查該字符串在串池中是否存在,**不存在:創建新的**,存在無論在程序代碼中出現幾次,JVM 都只會**建立一個 String 對象**,并在字符串池中維護
* 在JDK6.0及之前版本,字符串常量池是放在Perm Gen區(**也就是方法區**)中,在JDK7.0版本,字符串常量池被移到了**堆中**了,下圖中說明s1 和 s2 **內存空間地址相同**

2. **通過構造方法創建**,通過 new 創建的字符串對象,每一次 new 都會申請一個內存空間,雖然**內容相同**,但是**地址值不同**,但如圖在作為構造參數的"abc" 是被雙引號包裹,被包裹的其實會在常量池創建,但如果使用的是`char[]`字符數組作為**String 構造參數**時候,并不會有字符串常量池的概念

* **總結**:字符串字面量創建字符時,代碼中出現了相同字符串內容則直接使用池中已有的字符串對象而無需申請內存及創建對象,從而提高了性能
>[danger] ##### 字符串創建地址比較
1. **==號的作用**, 比較**基本數據類型**:比較的是具體的值, 比**較引用數據類型**:比較的是對象地址值
~~~
public class TestStr {
public static void main(String[] args) {
// 1.請問下面的代碼會創建幾個對象?分別存放在什么地方?
// String str1 = "hello"; // 1個對象 存放在常量池中
// String str1 = new String("helo"); // 2個對象 1個在常量池中,1個在堆區
// 2.常量池和堆區對象的比較
String str1 = "hello"; // 常量池
String str2 = "hello"; // 常量池
String str3 = new String("hello"); // 堆區
String str4 = new String("hello"); // 堆區
System.out.println(str1 == str2); // 比較地址 true
System.out.println(str1.equals(str2)); // 比較內容 true
System.out.println(str3 == str4); // 比較地址 false
System.out.println(str3.equals(str4)); // 比較內容 true
System.out.println(str2 == str4); // 比較地址 false
System.out.println(str2.equals(str4)); // 比較內容 true
System.out.println("------------------------------------------------------------");
// 3.常量有優化機制,變量沒有(Java語言為字符串連接運算符(+)提供特殊支持,并為其他對象轉換為字符串)
String str5 = "abcd";
String str6 = "ab" + "cd"; // 常量優化機制 "abcd",
System.out.println(str5 == str6); // 比較地址 true
String str7 = "ab";
String str8 = str7 + "cd"; // 沒有常量優化
System.out.println(str5 == str8); // 比較地址 false
}
}
~~~
* 案例二,`intern` 方法返回是常量池中地址
~~~
public class TestStr {
public static void main(String[] args) {
String str = "abc";
String str2 = "abc1";
String str1 = new String("abc"); // 在堆中str1
System.out.println(str == str1); // false
System.out.println(str == str1.intern()); // true
System.out.println(str1 == str1.intern()); // false
System.out.println(str == str2 ); // false 他們常量池中的地址不同
}
}
~~~
- 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 -- 用法