String三大核心機制
- 不變性:是一個
immutable
模式的對象,不變模式的主要作用是當一個對象需要被多線程共享並頻繁訪問時,可以保證數據的一致性 - 常量池優化:String對象創建後,會在字符串常量池進行緩存,下次創建同樣的對象時,會直接返回緩存的引用
- final:String類不可繼承,提高了系統的安全性
常見的考點
String類型不是基本數據類型,String底層實現爲char類型的數組
// 部分源碼
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
String實例化有兩種方式:
- 直接賦值
- 通過構造函數:可以以字符串的形式傳入,也可以傳入一個
char
類型的數組
public static void main(String[] args) {
char[] arr = {'你', '好'};
String str1 = new String(arr);
String str2 = new String("Hello World");
String str3 = "Hello World";
}
直接賦值和通過構造函數創建的主要區別在於儲存的區域不同,直接賦值的儲存在字符串常量池中
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // true
}
通過構造函數創建的儲存在堆內存中
public static void main(String[] args) {
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false
}
String的equals方法:String的equals方法重寫了Object類equals的方法,將String類型轉換爲char類型的數組,然後依次比較數組的每一位。
// equals源碼
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;
}
String不可變性:String的值一旦修改就會是一個新的對象,不是再原來的對象了
public static void main(String[] args) {
String str1 = "abc";
String str2 = str1;
str1 += "def";
System.out.println(str1 == str2); // false
}
String的intern()方法
當調用某個字符串對象的intern()方法時,會先去字符串常量池中尋找,如果已經存在一個值相等的字符串對象的話,則直接返回該對象的引用;如果不存在,則在字符串常量池中創建該對象,並返回。
public static void main(String[] args) {
String str1 = "Hello World";
String str2 = new String("Hello World");
System.out.println(str1 == str2); // false
String str3 = str2.intern();
System.out.println(str1 == str3); // true
}
String 常用方法
1、字符串截取
public String substring(int beginIndex)
:從beginIndex
開始截取,直到字符串結尾public String substring(int beginIndex,int endIndex)
:從beginIndex
開始截取,直到endIndex
結束,不包含endIndex
注意:截取之後原字符串不變
public static void main(String[] args) {
String str1 = "HelloWorld";
String substr1 = str1.substring(4);
String substr2 = str1.substring(4, 7);
System.out.println(str1); // HelloWorld
System.out.println(substr1); // oWorld
System.out.println(substr2); // oWo
}
2、字符串分割
public String[] split(String regex)
:將字符串按照regex
分割成數組,同時支持正則表達式
public static void main(String[] args) {
String str = "python java;golang,javascript";
String[] arr = str.split("[,|;| ]");
System.out.println(Arrays.toString(arr)); // [python, java, golang, javascript]
}
面試真題
要弄懂String類型,要先明白常量和變量的區別。常量是在編譯時期就知道值是多少的;而變量是在運行時期才能確定值的
public static void main(String[] args) {
String str1 = "helloWorld";
String str2 = "hello" + "World";
System.out.println(str1 == str2); // true
}
字符串hello
和字符串World
都是常量,拼接之後的值還是保存在字符串常量池中的,所以地址相同
public static void main(String[] args) {
String str1 = "helloWorld";
String str2 = "hello";
str2 += "World";
System.out.println(str1 == str2); // false
}
看似和上一題差不多,但實際上上一題的str2
是有兩個常量相加得到的,所以str2
儲存的是常量池中地址;而這一道題的str2
是一個變量,變量儲存在堆之中,同樣一個變量加一個常量還是儲存在堆中的,所以地址是不同的
public static void main(String[] args) {
String str1 = "helloWorld";
String str2 = "hello";
String str3 = str2 + "World";
System.out.println(str1 == str3); // false
}
同理,str1
在常量池中,str3
在堆中,所以他們的引用一定不相等
public static void main(String[] args) {
String str1 = "helloWorld";
final String str2 = "hello";
String str3 = str2 + "World";
System.out.println(str1 == str3); // true
}
這裏str2
加上final
關鍵字之後稱爲常量,保存在常量池中,在加上常量World
之後返回的值還是常量,所以str1
和str3
是相等的
public static void main(String[] args) {
String str1 = "helloWorld";
final String str2 = new String("hello");
String str3 = str2 + "World";
System.out.println(str1 == str3); // false
}
這裏雖然加上了final
但是通過構造函數創建的對象本身就是儲存在堆中的
public static void main(String[] args) {
String str1 = "helloWorld";
String str2 = "hello";
String str3 = "World";
String str4 = str2 + str3;
System.out.println(str1 == str4); // false
System.out.println(str1 == str4.intern()); // true
}
參考資料:https://blog.csdn.net/qq_34490018/article/details/82110578