部落格首頁:KC老衲愛尼姑的部落格首頁
部落客的github,平常所寫代碼皆在于此
刷題求職神器
共勉:talk is cheap, show me the code
作者是爪哇島的新手,水準很有限,如果發現錯誤,一定要及時告知作者哦!感謝感謝!
刷題求職神器
在下給諸位推薦一款巨好用的刷題求職神器,如果還有小夥伴沒有注冊該網站,可以點選下方連結直接注冊,注冊完後就可以立即刷題了。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CO5YjN2QGNiN2Y5QWNzkDMzYzXzMjNwUDM3IzLchDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
傳送門:牛客網
文章目錄
- 1.String概述
- 2.String類常用的構造方法
- 3.字元串方法
- 4.什麼是池?
- 4.1字元串常量池
- 4.2再談String對象建立
- 4.3intern方法
- 5.字元串的不可變性
- 6字元串修改
- 7.StringBuilder和StringBuffffe
- 8.面試題
1.String概述
String是Java中的引用類型,位于java.lang下,該類所定義的變量可用于指向字元串對象,然後來操作該字元串。
String既然是一個類,那麼可以從該類的屬性以及構造方法出發,去認識該類。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
通過源碼可知,String實作了三個接口,首先java.io.Serializable是一個空接口,作用就是辨別該類,說明此類可以被序列化,Comparable接口是用于比較大小的接口,最後一個CharSequence接口,該接口是char值的可讀序列, 該接口為其實作類提供統一的,隻讀通路許多不同類型的char序列。
2.String類常用的構造方法
String類提供了許多的構造方法,但是最常用有以下幾種。
@Test
public void testString(){
//使用常量串構造
String s = "hello";
System.out.println(s);
//newString對象
String s2 = new String("world");
System.out.println(s2);
//使用字元數組進行構造
char [] arr ={'a','b','c'};
String s3 = new String(arr);
System.out.println(s3);
}
通過源碼可以看到String的底層是一個被private以及final修飾的字元數組
private final char value[];
private int hash; // Default to 0
通過調試也能證明底層确實是一個數組,隻是它的組成部分還有hash。
1.直接使用常量串構造詳解
2.newString詳解
3.使用字元數組進行構造詳解
當傳入字元數組時 ,底層會拷貝一份字元數組并将拷貝後數組的引用給字元串對象的value。
傳入字元數組時String的構造方法
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
3.字元串方法
String對象的比較
字元串的對象的比較分為以下4中
1.== 比較是否引用同一個對象
@Test
public void testString2(){
String s1 = new String("hmr");
String s2 = new String("hmr");
String s3 = new String("yzq");
String s4 = s1;
System.out.println(s1==s3);//fasle
System.out.println(s1 == s2);//false
System.out.println(s4==s1);//true
}
2.boolean equals(Object anObject) 方法:按照字典序比較
String 重寫了Object中的equals方法,因為Object中的equals方法預設按照==來比較,String類重寫後會按照字典序來比較
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;
}
示例
@Test
public void testString3(){
String s1 = new String("hmr");
String s2 = new String("hmr");
String s3 = new String("yzq");
String s4 = s1;
System.out.println(s1.equals(s3));//false
System.out.println(s1 .equals(s2));//true
System.out.println(s4.equals(s1));//true
}
3.int compareTo(String s) 方法 按照字典序進行比較
與equals不同的是,equals傳回的是boolean類型,而compareTo傳回的是int類型。具體比較方式:
- 先按照字典次序大小比較,如果出現不等的字元,直接傳回這兩個字元的大小內插補點
- 如果前k個字元相等(k為兩個字元長度最小值),傳回值兩個字元串長度內插補點
示例
@Test
public void testString4(){
String s1 = new String("hmr");
String s2 = new String("hmr");
String s3 = new String("yzq");
String s4 = s1;
System.out.println(s1.compareTo(s2));//0
System.out.println(s2 .compareTo(s3));//-17
System.out.println(s4.compareTo(s1));//0
}
4.int compareToIgnoreCase(String str) 方法:與compareTo方式相同,但是忽略大小寫比較。
示例
@Test
public void testString5(){
String s1 = new String("HMR");
String s2 = new String("hmr");
String s3 = new String("YZQ");
String s5 = new String("yzq");
String s4 = s1;
System.out.println(s1.compareToIgnoreCase(s2));//0
System.out.println(s2 .compareToIgnoreCase(s3));//-17
System.out.println(s4.compareToIgnoreCase(s1));//0
System.out.println(s5.compareToIgnoreCase(s3));//0
}
4.什麼是池?
由于我們經常對這些字元串常量(常用資源)進行操作,而每次使用時都會開辟相應的記憶體,為了是程式運作的速度加快,就以空間來換時間,即事先将要頻繁使用的資源放入空間中,當我們需要操作時直接從空間來拿使用就行了,這個空間就是池。這就好比張三家裡沒有冰箱,那麼想吃冰棒得去小賣部,張三每天都出去覺得太麻煩了,于是自己買了個冰箱,在向冰箱裡屯了許多冰棒,以後向吃就可以随時吃,就節約了大量的時間。
4.1字元串常量池
字元串常量池在JVM中是StringTable類,實際是一個固定大小的HashTable(數組+連結清單(val為字元串對象))。不同版本的jdk下的字元串常量池的位置和大小的是不同的再次讨論的是jdk8下的字元串常量池,jdk8中該池位于堆記憶體中,池的大小可以設定,其最小值是1009。
4.2再談String對象建立
1.直接使用字元串常量進行指派
@Test
public void testString6(){
String s1 = "hello";
String s2 = "hello";
System.out.println(s1==s2);//true
}
2.通過new建立String類對象
@Test
public void testString6(){
String s3 = new String("hello");
String s4 =new String ("hello");
System.out.println(s3==s4);//false
}
當直接使用字元串常量進行指派時,在加載位元組碼檔案時,“hello”在字元串中已經建立好并儲存在字元串常量池中,當代碼走到String s1 = “hello”;建立對象時,會優先在字元串常量池中查找是否有該字元串,當找到了該字元串則将該字元串的引用指派給s1,如果沒有則建立新的字元串對象并入池。通過new建立的字元串類對象,首先會在堆記憶體開辟一個String對象,然後向字元串常量池中查找該字元是否存在,如若存在則将字元數組的引用指派給字元串對象的value,反之則直接建立新的字元串對象。
通過上述,可以得出new的對象是唯一,并且使用常量串建立的String類型對象的效率更高,更節約空間。
4.3intern方法
intern 是一個native方法(Native方法指:底層使用C++實作的,看不到其實作的源代碼),該方法的作用是手動将建立的String對象添加到常量池中。
代碼示例
@Test
public void testString7(){
char [] arr = new char[]{'a','b','c'};
String s1 = new String(arr);
String s2 = "abc";
System.out.println(s1==s2);//false
}
@Test
public void testString7(){
char [] arr = new char[]{'a','b','c'};
String s1 = new String(arr);
s1.intern();
String s2 = "abc";
System.out.println(s1==s2);//true
}
當s1調用ntern()方法後,會将s1對象放入到字元串常量池,故s1==s2。
5.字元串的不可變性
String是一種不可變對象. 字元串中的内容是不可改變。字元串不可被修改,是因為:
- String類在設計時就是不可變的,
- String類中的字元實際是在内部的value字元數組中,通過源碼可知String是被final修飾,不能被繼承,同時把value被final以及private修飾表明value本身的值是不能修改的,也是就是不能引用其它數組,但是對于一個數組是可以通過下标通路修改其數組對應的值,而此時在String類外壓根拿不到value故字元串不可變。
- 是以涉及到可能修飾字元串内容的操作都是建立一個新的對象,改變的新的對象,源碼如下。
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
6字元串修改
錯誤的使用String進行拼接
public class Test {
public static void main(String[] args) {
String s = "";
for (int i = 0;i<10_000;i++) {
s+= i;
}
System.out.println(s);
}
}
使用String進行字元串拼接效率極其低下,之是以速度慢,可以通過檢視Test的彙編代碼來究其本質
通過彙編得知每次進行字元串拼接時都會new一個StringBuilder對象,這也意味着會程式的運作速度是非常低下的,是以盡量不使用String直接拼接字元串,可以使用StringBuilder或者StringBuffer。
7.StringBuilder和StringBuffffe
由于String類型的字元串不可更改,為了可以高效的進行字元串修改,Java提供了StringBuffer和StringBuilder類,這兩個類大同小異,這裡就介紹幾個常用的API更多的方法,更多需求自行檢視 StringBuildre線上文檔。
1.使用StringBuilder進行拼接字元串
@Test
public void testString9(){
long start = System.currentTimeMillis();
StringBuffer s = new StringBuffer("");
for (int i = 0;i<10_0000;i++) {
s.append(i);
}
long end = System.currentTimeMillis();
System.out.println(end-start);//18
}
@Test
public void testString8(){
long start = System.currentTimeMillis();
String s = "";
for (int i = 0;i<10_0000;i++) {
s+= i;
}
long end = System.currentTimeMillis();//32139
System.out.println(end-start);
}
從上述代碼的運作時間上來看,StringBulder比String速度快了n倍。
2.反轉一個字元串
@Test
public void testStringBuilder(){
StringBuilder s= new StringBuilder("hello world");
System.out.println(s.reverse().toString());
}
注意:String和StringBuilder類不能直接轉換。如果需要轉換可以采用一下方式:
- String轉StringBuilder:利用StringBuilder的構造方法或者append()方法。
- StringBuilder變成Sring:調用toString()方法。
8.面試題
1,String、StringBuffffer、StringBuilder的差別
- String的内容不可修改,StringBuffffer與StringBuilder的内容可以修改。
-
StringBuffffer采用同步處理,屬于線程安全操作;而StringBuilder未采用同步處理,屬于線程不安全操
作。
- 以下總共建立了多少個String對象【前提不考慮常量池之前是否存在】
1.String str = “hello”;
隻會開辟一塊堆記憶體空間,儲存在字元串常量池中,然後str共享常量池中的String對象(1個)
2.String str = new String(“hello”)
會開辟兩塊堆記憶體空間,字元串"hello"儲存在字元串常量池中,然後用常量池中的String對象給新開辟
的String對象指派。(2個)
3.String str = new String(new char[]{‘h’, ‘e’, ‘l’, ‘l’, ‘o’})
先在堆上建立一個String對象,然後利用copyof将重新開辟數組空間,将參數字元串數組中内容拷貝到
String對象中(三個)