java 知識(shí)合集

String、StringBufer、StringBuilder

String 它是典型的Immutable(不可改變)類,被聲明成為fnal class,所有屬性也都是fnal的。也由于它的不可變性,類似拼接、裁剪字符串等動(dòng)作,都會(huì)產(chǎn)生新的String對象。在循環(huán)場景下不建議使用這些功能。

StringBufer是為解決上面提到拼接產(chǎn)生太多中間對象的問題而提供的一個(gè)類,可以用append或者add方法,把字符串添加到已有序列的末尾或者指定位置。StringBufer本質(zhì)是一個(gè)線程安全的可修改字符序列,它保證了線程安全(簡單粗暴各種修改數(shù)據(jù)的方法都加上synchronized關(guān)鍵字實(shí)現(xiàn)的),也隨之帶來了額外的性能開銷,所以除非有線程安全的需要,不然還是推薦使用它的后繼者,也就是StringBuilder。

StringBuilder 在能力上和StringBufer沒有本質(zhì)區(qū)別,但是它去掉了線程安全的部分,有效減小了開銷,是絕大部分情況下進(jìn)行字符串拼接的首選。從 builder 命名來看這個(gè)類專門就是為創(chuàng)建而生。

  • Immutable對象在拷貝時(shí)不需要額外復(fù)制數(shù)據(jù)。

字符串的內(nèi)部數(shù)組

  • 為了實(shí)現(xiàn)修改字符序列的目的,StringBufer和StringBuilder底層都是利用可修改的char數(shù)組(JDK 9以后是byte),都繼承了AbstractStringBuilder。

  • 這個(gè)內(nèi)部數(shù)組應(yīng)該創(chuàng)建成多大的呢?如果太小,拼接的時(shí)候可能要重新創(chuàng)建足夠大的數(shù)組;如果太大,又會(huì)浪費(fèi)空間。目前的實(shí)現(xiàn)是,構(gòu)建時(shí)初始字符串長度加16(這意味著,如果沒有構(gòu)建對象時(shí)輸入最初的字符串,那么初始值就是16)。我們?nèi)绻_定拼接會(huì)發(fā)生非常多次,而且大概是可預(yù)計(jì)的,那么就可以指定合適的大小,避免很多次擴(kuò)容的開銷。擴(kuò)容會(huì)產(chǎn)生多重開銷,因?yàn)橐獟仐壴袛?shù)組,創(chuàng)建新的(可以簡單認(rèn)為是倍數(shù))數(shù)組,還要進(jìn)行arraycopy。

字符串拼接

  • 如果在代碼拼接說 String 拼接效率低下,但是在 jdk8即使你使用 String 拼接編譯器還是會(huì)把你優(yōu)化成 StringBuilder,jdk9優(yōu)化成StringConcatFactory(暫時(shí)沒用了解過)

字符串緩存、內(nèi)存

  • String在jdk6以后提供了intern()方法,目的是提示JVM把相應(yīng)字符串緩存起來,以備重復(fù)使用。但是不建議使用。

  • jdk6被緩存的字符串是存在所謂PermGen里的,也就是臭名昭著的“永久代”,這個(gè)空間是很有限的,也基本不會(huì)被FullGC之外的垃圾收集照顧到。所以,如果使用不當(dāng),OOM就會(huì)光顧。

  • jdk7存放在堆中。

  • jdk8出現(xiàn)了MetaSpace(元數(shù)據(jù)區(qū)),就存在元空間中。

  • intern是一種顯式地排重機(jī)制,但是它也有一定的副作用,因?yàn)樾枰_發(fā)者寫代碼時(shí)明確調(diào)用,一是不方便,每一個(gè)都顯式調(diào)用是非常麻煩的;另外就是我們很難保證效率,應(yīng)用開發(fā)階段很難清楚地預(yù)計(jì)字符串的重復(fù)情況,有人認(rèn)為這是一種污染代碼的實(shí)踐。
    幸好在Oracle JDK 8u20之后,推出了一個(gè)新的特性,也就是G1 GC下的字符串排重。它是通過將相同數(shù)據(jù)的字符串指向同一份數(shù)據(jù)來做到的,是JVM底層的改變,并不需要Java類庫做什么修改。
    開啟 G1 GC 后可加入這個(gè)參數(shù)開啟-XX:+UseStringDeduplication

動(dòng)態(tài)代理

深入理解RPC之動(dòng)態(tài)代理篇這篇文章針對集中動(dòng)態(tài)代理有比較詳細(xì)的分析
動(dòng)態(tài)代理的應(yīng)用場景比如:RPC、安全、日志、事務(wù)

靜態(tài)代理

事先寫好代理類,可以手工編寫,也可以用工具生成。缺點(diǎn)是每個(gè)業(yè)務(wù)類都要對應(yīng)一個(gè)代理類,非常不靈活。

動(dòng)態(tài)代理

運(yùn)行時(shí)自動(dòng)生成代理對象。缺點(diǎn)是生成代理代理對象和調(diào)用代理方法都要額外花費(fèi)時(shí)間。

JDK動(dòng)態(tài)代理:基于Java反射機(jī)制實(shí)現(xiàn),必須要實(shí)現(xiàn)了接口的業(yè)務(wù)類才能用這種辦法生成代理對象。新版本也開始結(jié)合ASM機(jī)制。

cglib動(dòng)態(tài)代理:基于ASM機(jī)制實(shí)現(xiàn),通過生成業(yè)務(wù)類的子類作為代理類。

基本數(shù)據(jù)類型和引用數(shù)據(jù)類型

緩存機(jī)制

緩存機(jī)制并不是只有Integer才有,同樣存在于其他的一些包裝類,比如:

  • Boolean,緩存了true/ false對應(yīng)實(shí)例,確切說,只會(huì)返回兩個(gè)常量實(shí)例Boolean.TRUE/ FALSE。
  • Short,同樣是緩存了-128到127之間的數(shù)值。
  • Byte,數(shù)值有限,所以全部都被緩存。
  • Character,緩存范圍'\ u0000' 到 '\ u007F'。
    繼續(xù)深挖緩存,I nteger的緩存范圍雖然默認(rèn)是-128到127,但是在特別的應(yīng)用場景,比如我們明確知道應(yīng)用會(huì)頻繁使用更大的數(shù)值,這時(shí)候應(yīng)該怎么辦呢?
-XX:AutoBoxCacheMax=N

緩存上限值實(shí)際是可以根據(jù)需要調(diào)整的,JVM提供了參數(shù)設(shè)置:

自動(dòng)拆裝箱的注意點(diǎn)

建議避免無意中的裝箱、拆箱行為,尤其是在性能敏感的場合,創(chuàng)建10萬個(gè)Java對象和10萬個(gè)整數(shù)的開銷可不是一個(gè)數(shù)量級(jí)的,不管是內(nèi)存使用還是處理速度,光是對象頭的空間占用就已經(jīng)是數(shù)量級(jí)的差距了。

Integer 內(nèi)存結(jié)構(gòu)組成

  1. Mark Word: 標(biāo)記位 4字節(jié),類似輕量級(jí)鎖標(biāo)記位,偏向鎖標(biāo)記位等。
  2. Class對象指針: 4字節(jié),指向?qū)ο髮?yīng)class對象的內(nèi)存地址。
  3. 對象實(shí)際數(shù)據(jù):對象所有成員變量。
  4. 對齊:對齊填充字節(jié),按照8個(gè)字節(jié)填充。
    Integer占用內(nèi)存大小,4+ 4+ 4+ 4= 16字節(jié)。

集合

狹義的集合框架

Map

Map

LinkedHashMap

LinkedHashMap通常提供的是遍歷順序符合插入順序,它的實(shí)現(xiàn)是通過為條目(鍵值對)維護(hù)一個(gè)雙向鏈表。注意,通過特定構(gòu)造函數(shù),我們可以創(chuàng)建反映訪問順序的實(shí)例,所謂的put、get、compute等,都算作“訪問”。


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容