前言
目前創(chuàng)新互聯(lián)公司已為數(shù)千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬空間、網(wǎng)站托管、企業(yè)網(wǎng)站設(shè)計(jì)、貴南網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。String 是我們實(shí)際開(kāi)發(fā)中使用頻率非常高的類(lèi),Java 可以通過(guò) String 類(lèi)來(lái)創(chuàng)建和操作字符串,使用頻率越高的類(lèi),我們就越容易忽視它,因?yàn)橐?jiàn)的多所以熟悉,因?yàn)槭煜に哉J(rèn)為它很簡(jiǎn)單,其實(shí)只是了解到皮毛,并沒(méi)有真正掌握,而 String 又是面試的高頻考點(diǎn),所以我們有必要將 String 這個(gè)類(lèi)深入研究,徹底搞定,本節(jié)課就為大家詳細(xì)講解 String 的核心機(jī)制以及實(shí)際使用。
String 三大核心:
1、不變性:String 是只讀字符串,是一個(gè)典型的 immutable 對(duì)象,對(duì)它進(jìn)行任何操作,其實(shí)都是創(chuàng)建一個(gè)新的對(duì)象,再把引用指向該對(duì)象。不變模式的主要作用在于當(dāng)一個(gè)對(duì)象需要被多線(xiàn)程共享并頻繁訪問(wèn)時(shí),可以保證數(shù)據(jù)的一致性。
2、常量池優(yōu)化:String 對(duì)象創(chuàng)建之后,會(huì)在字符串常量池中進(jìn)行緩存,如果下次創(chuàng)建同樣的對(duì)象時(shí),會(huì)直接返回緩存的引用。
3、final:使用 final 來(lái)定義 String 類(lèi),表示 String 類(lèi)不能被繼承,提高了系統(tǒng)的安全性。
String 不是基本數(shù)據(jù)類(lèi)型
這是很基礎(chǔ)的東西,但是很多初學(xué)者卻容易忽視,Java 的 8 種基本數(shù)據(jù)類(lèi)型中不包括 String,基本數(shù)據(jù)類(lèi)型中用來(lái)描述文本數(shù)據(jù)的是 char,但是它只能表示單個(gè)字符,比如 'a','好' 之類(lèi)的,如果要描述一段文本,就需要用多個(gè) char 類(lèi)型的變量,也就是一個(gè) char 類(lèi)型數(shù)組,比如“你好” 就是長(zhǎng)度為2的數(shù)組 char[] chars = {'你','好'};
但是使用數(shù)組過(guò)于麻煩,所以就有了 String,String 底層就是一個(gè) char 類(lèi)型的數(shù)組,只是使用的時(shí)候開(kāi)發(fā)者不需要直接操作底層數(shù)組,用更加簡(jiǎn)便的方式即可完成對(duì)字符串的使用。
歡迎大家關(guān)注我的公種浩【程序員追風(fēng)】,文章都會(huì)在里面更新,整理的資料也會(huì)放在里面。
高頻面試題
1、== 和 equals 的區(qū)別?
== 可以理解為是比較棧內(nèi)存中的值,如果變量是基本數(shù)據(jù)類(lèi)型,則棧內(nèi)存中存放的就是具體數(shù)值,如果是引用類(lèi)型,則棧中存放的是引用的內(nèi)存地址。
所以對(duì)于基本數(shù)據(jù)類(lèi)型,== 是比較值是否相等,對(duì)于引用數(shù)據(jù)類(lèi)型,比較的是引用的內(nèi)存地址是否相等。
equals 是 Object 類(lèi)提供的一個(gè)方法,其本質(zhì)就是在用 == 進(jìn)行判斷。
public?boolean?equals(Object?obj)?{ ??return?(this?==?obj); }
同時(shí) Java 中任意一個(gè)類(lèi)都可以對(duì)其進(jìn)行重寫(xiě),根據(jù)具體需求重新定義其判斷邏輯,比如我們自定義一個(gè) Student 類(lèi),如下所示。
public?class?Student?{ ????private?Integer?id; ????private?String?name; ????public?Student(Integer?id,?String?name)?{ ????????this.id?=?id; ????????this.name?=?name; ????} }
創(chuàng)建兩個(gè)成員變量值完全相等的實(shí)例化對(duì)象,并用 equals 方法判斷是否相等。
Student?student1?=?new?Student(1,"張三"); Student?student2?=?new?Student(1,"張三"); System.out.println(student1.equals(student2));
結(jié)果為 false,因?yàn)橛袃蓚€(gè)實(shí)例化對(duì)象,就必然會(huì)在堆內(nèi)存中開(kāi)辟兩塊空間來(lái)存儲(chǔ),引用一定是不相同的。而在現(xiàn)實(shí)的邏輯中,如果兩個(gè)學(xué)生的 id 和 name 都一樣,我們就認(rèn)為他們是同一個(gè)學(xué)生,用程序如何來(lái)實(shí)現(xiàn)呢?通過(guò)重寫(xiě) equals 方法即可,如下所示。
public?class?Student?{ ????private?Integer?id; ????private?String?name; ????public?Student(Integer?id,?String?name)?{ ????????this.id?=?id; ????????this.name?=?name; ????} ????@Override ????public?boolean?equals(Object?obj)?{ ????????Student?student?=?(Student)?obj; ????????if(id.equals(student.id)?&&?name.equals(student.name)){ ????????????return?true; ????????} ????????return?false; ????} }
再次運(yùn)行代碼,返回值為 true。
2、下面代碼的運(yùn)行結(jié)果是?
String?str1?=?"Hello?World"; String?str2?=?"Hello"+"?World"; System.out.println(str1?==?str2);
true,"Hello" 和 " World" 都是字符串字面值,字符串字面值 + 字符串字面值的結(jié)果仍然保存在字符串常量池中,所以 str1 和 str2 相同。
3、下面代碼的運(yùn)行結(jié)果是?
String?str1?=?"Hello?World"; String?str2?=?"Hello"; str2?+=?"?World"; System.out.println(str1?==?str2);
false,這題看似與第 2 題一樣,為什么結(jié)果完全不同呢?因?yàn)?str2 = "Hello"+" World" 是直接創(chuàng)建,str2 = "Hello"; str2 = "Hello"; 是先創(chuàng)建再修改,同時(shí)修改完成之后的字符串是放在堆內(nèi)存中的,為什么呢?因?yàn)?str2 是一個(gè)字符串變量," World" 是字符串字面值,當(dāng)字符串字面值與 String 類(lèi)型變量拼接時(shí),得到的新字符串不再保存在常量池中,而是在堆中開(kāi)辟一塊新的空間來(lái)存儲(chǔ),所以 str1 引用指向字符串常量池,str2 引用指向堆內(nèi)存,肯定不相同。
4、下面代碼的運(yùn)行結(jié)果是?
String?str1?=?"Hello?World"; String?str2?=?"?World"; String?str3?=?"Hello"+str2; System.out.println(str1?==?str3);
false,str2 是變量,"Hello" 是字符串字面值,字符串字面值 + 變量會(huì)在堆內(nèi)存中開(kāi)辟新的空間來(lái)存儲(chǔ),所以 str1 和 str3 不同。
5、下面代碼的運(yùn)行結(jié)果是?
String?str1?=?"Hello?World"; final?String?str2?=?"?World"; String?str3?=?"Hello"+str2; System.out.println(str1?==?str3);
true,"Hello" 是字符串字面值,str2 是常量,字符串字面值+常量的結(jié)果仍然保存在字符串常量池中,所以 str1 和 str3 相同。
6、下面代碼的運(yùn)行結(jié)果是?
String?str1?=?"Hello?World"; final?String?str2?=?new?String("?World"); String?str3?=?"Hello"+str2; System.out.println(str1?==?str3);
false,str2 是常量,但是 new String(" World") 保存在堆內(nèi)存中,所以即使使用 final 進(jìn)行了修飾,str2 仍然保存在堆中,則 str3 也就保存在堆中,所以 str1 和 str3 不同。
7、下面代碼的運(yùn)行結(jié)果是?
String?str1?=?"Hello?World"; String?str2?=?"Hello"; String?str3?=?"?World"; String?str4?=?str2?+?str3; System.out.println(str4.intern()?==?str1);
true,當(dāng)調(diào)用 str4 的 intern 方法時(shí),如果字符串常量池已經(jīng)包含一個(gè)等于 str4 的字符串,則返回該字符串,否則將 str4 添加到字符串常量池中,并返回其引用,所以 str4.intern() 與 str1 相同。
8、什么是字符串常量池?
字符串常量池位于堆內(nèi)存中,專(zhuān)門(mén)用來(lái)存儲(chǔ)字符串常量,可以提高內(nèi)存的使用率,避免開(kāi)辟多塊空間存儲(chǔ)相同的字符串,在創(chuàng)建字符串時(shí) JVM 會(huì)首先檢查字符串常量池,如果該字符串已經(jīng)存在池中,則返回它的引用,如果不存在,則實(shí)例化一個(gè)字符串放到池中,并返回其引用。
9、String 是線(xiàn)程安全的嗎?
String 是不可變類(lèi),一旦創(chuàng)建了String對(duì)象,我們就無(wú)法改變它的值。因此它是線(xiàn)程安全的,同一個(gè)字符串實(shí)例可以被多個(gè)線(xiàn)程共享,保證了多線(xiàn)程的安全性。
10、在使用 HashMap 的時(shí)候,用 String 做 key 有什么好處?
HashMap 內(nèi)部實(shí)現(xiàn)是通過(guò) key 的 hashcode 來(lái)確定 value 的存儲(chǔ)位置,因?yàn)樽址遣豢勺兊模援?dāng)創(chuàng)建字符串時(shí),它的 hashcode 被緩存下來(lái),不需要再次計(jì)算,所以相比于其他對(duì)象更快。
最后
歡迎大家一起交流,喜歡文章記得點(diǎn)個(gè)贊喲,感謝支持!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。