Java常用類——String
1、什麼是String
在Java中String類是一個使用非常頻繁的類,它不是java八個基本數據類型的其中之一,而是Java提供的在java.lang包下的用於創建和操作字符串的類。本文章通過蒐集的幾個常見面試題來學習String類的簡單使用。
2、String的特點
- 使用final修飾:不可被繼承,並且內部一些方法也被final修飾。
public final class String
- **不可變性:**String的不可變性主要有兩方面決定,首先是String類使用fianl來修飾,決定了String不能被繼承,另外一個原因是因爲String用於存儲字符串值得char行數組value[]也是使用final修飾的。
private final char value[];
- **常量池優化:**常量池的出現是爲了解決對象快速創建問題。當一個String對象創建之後,會在字符串常量池中進行緩存,等到下次創建相同對象的時候將直接返回已緩存對象的引用,從而優化對象的創建過程。
3、String對象的創建方式
-
使用常量賦值的形式實例化對象
String str = "java";
-
使用構造方法的形式實例化對象
String str = new String("java");
這兩種實例化方式到底有什麼區別呢?我們先從存放的位置來分析。
兩種實例化方式都會在棧中創建變量str。
第一種方式實例化對象,虛擬機首先會在字符串常量池中通過String的equals方法來查找是否存在“java”常量,如果存在,虛擬機會將該常量的引用地址直接複製給str變量,如果不存在,會將字符串常量“java”存放在字符串常量池中,並將其引用地址複製給str變量。
第二種方式創建的對象會在堆中開闢內存空間,這裏需要注意的是,jvm並不是將字符串變量存放到了堆內存中,它只是在堆中開闢了一個內存塊,用於存放指向內存本身的引用地址。它的字符串常量“java”也會存放到字符串常量池中,所以使用第二種方式創建對象的時候具體流程如下:
虛擬機首先會在字符串常量池中通過String的equals方法來查找是否存在“java”常量,無論常量池中是否存在“java”字符串虛擬機都會在堆空間開闢內存空間存放新對象,如果存在就直接使用,如果不存在,會將字符串常量“java”存放在字符串常量池中,並將其引用地址複製給堆中新開闢的地址。
總結:
- 常量賦值的形式實例化,字符常量內容存於常量池,變量存於棧中,直接指向常量池。
- 構造方法實例化,會先在堆中創建實例對象,引用對象存於棧中,然後再去常量 區尋找需要的字符常量,如果找到了,直接使用,沒找到則開闢新的空間並存儲內容。
4、“==”與equals的區別
在解釋上述結果之前需要先明確一個概念,那就是“==”符號到底比較的是什麼?這個分爲兩種情況
- 基本數據類型
對於基本數據類型來講,"=="是判斷左右兩邊的值是否相等
- 引用類型
對於引用類型的變量,"=="是判斷左右兩邊所指向的地址是否一致
//Object的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
從Object的equals方法源碼可知,Object的equals方法其實也是直接比較對象的地址,和“==”沒有任何區別。那我們怎麼去比較兩個對象的內容而不是對象地址是否一致呢?現在就需要我們去重寫equals方法,下面以String的equals方法爲例。
//String的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中equals方法源碼,從中我們可以看出調用String的equals方法對兩個變量進行比較時,首先是判斷兩個對象的地址是否相等,如果不相等還會對兩個string變量進行逐字符比較,如果相同那麼就返回true,否則返回false。當然對於我們自定義的類我們也可以根據需求自定義重寫他們的equals方法。
5、String常用方法
方法名稱 | 描述 |
---|---|
public String (char[] value,int offset,int count) | 將組部分字符數組變爲String類 |
public String (char[] value) | 將組部分字符數組變爲String類 |
public char cahrAt(int index) | 返回指定所索引對應的字符信息 |
public char[] toCharArrary() | 將字符串以字符數組的形式返回 |
public boolean equals(String anObject) | 進行相等判斷,區分大小寫 |
Public boolean equalsIgnoreCase(String anotherString) | 進行相等判斷,不區分大小寫 |
public int compareTo(String anoterString) | 判斷兩個字符串的大小(按照字符編碼比較) =0:表示要比較的兩個字符內容相等 >0:表示大於的結果 <0:表示小於的結果 |
public boolean contains(String s) | 判斷指定的內容是否內存 |
public int indexOf(String str) | 由前向後查找指定字符串的位置, 如果查找到了則返回(第一個字母)位置的索引, 如果找不到返回-1 |
public int indexOf(String str,int fromIndex) | 從指定位置由前向後查找指定字符串的位置,找不到返回-1 |
public boolean starsWith(String perfix) | 判斷是否以指定的字符串開頭,如果是返回true,否則返回false |
public boolean endsWith(String suffix) | 判斷是否以指定的字符串結尾 |
public String repaceAll(String regex(正則),String replacement) | 用新的內容替換掉全的舊的 |
public String substring(int beginIndex) | 從指定索引截取到結尾 |
public String substring(int beginIndex,int endIndex) | 截取部分字符串的數據 |
public String split(String regex) | 按照指定的字符串進行全部拆分 |
public String concat(String str) | 追加字符 |
public String toLowerCase() | 轉小寫 |
public String toUpperCase() | 轉大寫 |
public String trim() | 去掉字符串左右兩邊的空格,中間空格保留 |
public intern() | 數據入池 |
public boolean isEmpty() | 判斷是否是空字符串(不是null,而是””長度是0) |
6、面試題目解析
-
考察String的實例化方式
String str1 = "java"; String str2 = "java"; String str3 = new String("java"); String str4 = new String("java"); System.out.println(str1==str2); System.out.println(str3==str4); System.out.println(str3.equals(str3)); 輸出結果:true false true
str1與str2是通過賦值的方式進行的實例化,引用地址相同,所以結果爲true
str3與str4是通過構造方法來進行的實例化,引用地址不同,所以結果爲false
第三個結果爲true是因爲String的equals方法並不是直接比較兩者指向的地址,還會判斷內容是否相同。
-
String不可變性
String str3 = new String("java"); String str4 = str3; System.out.println(str3==str4); str3 = "dart"; System.out.println(str3==str4); 輸出結果:true false
因爲String對象創建後,它會將字符串值保存在final修飾的一個字符數組裏面,所以String是不可變的
/** The value is used for character storage. */ private final char value[];
一旦字符串的值發生改變,那麼該對象就變成了一個新的對象,其地址也會發生改變,所以最終結果應該爲true和false。
-
String字符串拼接
String str1 = "java dart"; String str2 = "java "; String str3 = "dart"; String str4 = str2+str3; String str5 = "java "+"dart"; System.out.println(str1==str4); System.out.println(str1==str5); 輸出結果:false true
拼接後得到的str4與str5字符串內容是一樣的,但是輸出結果爲什麼不同呢?
str2與str3均爲變量,變量與變量進行拼接那麼str4就會在堆中開闢內存地址,而str1則是保存在常量池中,所以str4與str5內存地址一致。
“java ”與“dart”均爲常量,拼接後的結果將保存在常量池中,所以str5與str1內存地址一致
-
final修飾符的使用
String str1 = "java dart"; final String str2 = "java "; final String str3 = new String("java "); String str4 = str2+"dart"; String str5 = str3+"dart"; System.out.println(str1==str4); System.out.println(str1==str5); 輸出結果:true false
這個結果似乎有點意外,爲什麼3中str1==str4爲false,這裏就變成了true?因爲這裏的str2使用final修飾,如果在編譯期都可以知道確切值(定義變量的時候就初始化),那麼在編譯器會將其當做常量使用,所有用到該變量的地方就相當於直接使用該常量,所以常量與常量拼接還將保存在常量池中,而str3在編譯期並不知道確切值,所以輸出結果爲false