Java知識復(fù)習(xí)解答
1.synchronize 和 volitale的區(qū)別;什么是可見性和原子性
可見性:當(dāng)一個線程修改了線程共享變量的值掂为,其他線程能夠立即得知這個修改
原子性:一系列的操作要么全部執(zhí)行完,要么都不執(zhí)行
有序性:如果在本線程內(nèi)觀察勇哗,所有操作都是有序的;如果在一個線程中觀察另一個線程抄谐,所有操作都是無序的瞧栗。前半句是指“線程內(nèi)表現(xiàn)為串行語義”,后半句是指“指令重排序”現(xiàn)象和“工作內(nèi)存中主內(nèi)存同步延遲”現(xiàn)象迹恐。
synchronized具有原子性,可見性憎茂,有序性锤岸,同一時刻只能有一個線程進行獲取拳氢;會造成線程阻塞募逞;標記的變量會被編譯器優(yōu)化放接;可以使用在變量留特,方法,類級別中
volatile具有可見性蜕青,有序性,不能保證原子性慧脱,(需要保證原子性蒙兰,1:運算結(jié)果不依賴變量的當(dāng)前值,或者只有一個線程修改變量的值搜变;2:該變量沒有包含在具有其他變量的不變式中);不會造成線程阻塞扳抽;標記的變量不會被編譯器優(yōu)化(防止指令重排)殖侵;只能使用在變量級別
2.Java的集合類
Java的集合類是放在java.util包下;集合類存放的是對象的引用拢军,而非對象的本身;集合類主要分為Set(集)固蛾,List(列表)度陆,Map(映射)。
List和Set是Collection(接口)的子類懂傀。
Set:不能包含重復(fù)的元素,沒有順序
List:
-
ArrayList:是List的子類恃泪,底層是動態(tài)擴容的數(shù)組結(jié)構(gòu)∥虮茫可以存放重復(fù)的元素(包括null);有序糕非,按照元素的添加順序朽肥,多用于查詢多增刪操作少的情況持钉,ArrayList是線程異步的,是不安全每强。
擴容機制:每次擴容的大小是原來的1.5倍;擴容的過程其實就是一個將原來元素拷貝到一個擴容后數(shù)組大小的長度新數(shù)組中浪箭,所以ArrayList的擴容其實是相對來說是比較消耗性能的辨绊。經(jīng)常出現(xiàn)的一個異常:ConcurrentModificationException,在foreach循環(huán)的時候宣鄙,進行了刪除操作,集合的長度產(chǎn)生的變化冻晤,
LinkedList:LinkedList是一種可以在任何位置進行高效地插入和刪除操作的有序序列绸吸,多用于增刪操作多的情況下。底層實現(xiàn)的數(shù)據(jù)結(jié)構(gòu)是雙向鏈表惯裕。也是不安全的
Map是util包下另一個集合類,是以key-value形式撑刺,鍵不能重復(fù)握玛,值可以重復(fù)甫菠。對Map集合遍歷時先得到鍵的set集合冕屯,再對set集合進行遍歷,得到相應(yīng)的值痰洒。
HashMap:
- 存儲數(shù)據(jù)是根據(jù)鍵值對存儲數(shù)據(jù)的浴韭,并且存儲多個數(shù)據(jù)時,數(shù)據(jù)的鍵不能相同念颈,如果相同該鍵之前對應(yīng)的值將被覆蓋。
- HashMap最多只允許一條存儲數(shù)據(jù)的鍵為null榴芳,可允許多條數(shù)據(jù)的值為null。
- HashMap存儲數(shù)據(jù)的順序是不確定的窟感,并且可能會因為擴容導(dǎo)致元素存儲位置改變讨彼。因此遍歷順序是不確定的肌括。
- HashMap是線程不安全的,使用ConcurrentHashMap具有線程安全谍夭。
底層存儲結(jié)構(gòu):
JDK1.7之前的存儲結(jié)構(gòu):
拉鏈法,專業(yè)點就叫鏈地址法紧索。就是數(shù)組加鏈表的結(jié)合袁辈。每一個數(shù)組元素上存儲的都是一個鏈表珠漂。新添加進來的元素總是放在數(shù)組對應(yīng)的角標位置,而原來處于該角標的位置的節(jié)點作為next節(jié)點放到新節(jié)點的后邊媳危。
JDK1.8中的數(shù)據(jù)結(jié)構(gòu):
如果單單是數(shù)組加鏈表的話荞彼,當(dāng)處理hash值沖突較多的情況下待笑,鏈表的長度會越來越長,查找的效率會越來越低了。所以在1.8中當(dāng)新增節(jié)點導(dǎo)致鏈表的長度超過8的時候寞缝,就會在添加元素的同事將單鏈表轉(zhuǎn)化為紅黑樹。紅黑樹是一種易于增刪改查的二叉樹荆陆,這樣HashMap中的元素操作起來就會更高效。
3. Java中的引用
- StrongReference(強引用):從不回收被啼,對象一直存在帜消,垃圾回收器絕對不會回收它趟据;當(dāng)JVM停止的時候才被終止
- SoftReference(軟引用):可以和引用隊列(ReferenceQueue)聯(lián)合使用;內(nèi)存足夠汹碱,不會回收,內(nèi)存不夠就會回收咳促;
- WeakReference(弱引用):可以和引用隊列(ReferenceQueue)聯(lián)合使用,當(dāng)內(nèi)存不足時勘伺,觸發(fā)GC后被終止。多用于內(nèi)存泄漏的解決飞醉;可以通過手動GC進行清除冲茸。
- PhantomReference(虛引用):必須和引用隊列(ReferenceQueue)聯(lián)合使用缅帘,隨時會被回收,觸發(fā)GC后被終止
4. Java GC回收機制
GC機制:是Java虛擬機垃圾回收器提供的一種用于在空閑時間不定時回收無任何對象引用的對象占據(jù)的內(nèi)存空間的一種機制钦无。
JVM把內(nèi)存劃分成了下面幾個區(qū)域:
-
程序計數(shù)器
每一個線程都有它自己的程序計數(shù)器,并且任何時間一個線程都只有一個方法在執(zhí)行失暂,也就是所謂的當(dāng)前方法彼宠。會存儲當(dāng)前線程正在執(zhí)行的Java方法的JVM指令地址弟塞;如果是本地方法,則是未指定值(undefined)决记。
-
方法區(qū)
用于存儲所謂的元數(shù)據(jù)摧冀,例如類的信息(名稱,修飾符等)惭适、運行時常量池、類中的字段和方法等楼镐。方法區(qū)GC,條件比較苛刻
運行時常量池:常量池可以存放各種常量信息框产,不管是編譯器生成的各種字面量,還是需要在運行時決定的符號引用秉宿。
-
堆區(qū)
是Java內(nèi)存管理的核心區(qū)域戒突,用來放置Java對象實例描睦,幾乎所有創(chuàng)建的Java對象實例都是被直接分配在堆上的。所以堆區(qū)是GC最頻繁的
-
Java虛擬機棧
每個線程在創(chuàng)建時都會創(chuàng)建一個虛擬機棧忱叭,線程私有隔崎,生命周期和線程一樣韵丑,每一個方法被調(diào)用時產(chǎn)生一個棧幀。JVM直接對Java棧的操作只有兩個撵彻,就是對棧幀的壓棧和出棧钓株。
棧幀中存儲著局部變量表陌僵、操作數(shù)、動態(tài)鏈接拾弃、方法出口。
-
本地方法棧
和Java虛擬機棧非常相似豪椿,支持對本地方法的調(diào)用奔坟,也是每個線程都會創(chuàng)建一個搭盾。
方法區(qū)和堆歸所有線程共享
在上面介紹的五個內(nèi)存區(qū)域中,有3個是不需要進行垃圾回收的:本地方法棧鸯隅、程序計數(shù)器澜建、虛擬機棧。因為他們的生命周期是和線程同步的炕舵,隨著線程的銷毀何之,他們占用的內(nèi)存會自動釋放咽筋。所以,只有方法區(qū)和堆區(qū)需要進行垃圾回收奸攻,回收的對象就是那些不存在任何引用的對象蒜危。
GC算法:
經(jīng)典的引用計數(shù)算法睹耐,很難處理循環(huán)引用關(guān)系,所以Java并沒有采用這種方法硝训。而是采用追蹤性垃圾收集,就是根搜索算法窖梁。
常見的垃圾收集算法:
- 復(fù)制算法:高效晃酒,但是需要提前預(yù)留空間窄绒,有一定浪費崔兴。
- 標記-清除:先進行標記,標記處所有要回收的對象敲茄,然后進行清除位谋。有碎片化的問題堰燎,不適合特別大的堆。
- 標記-整理:類似清除秆剪,但是為了避免內(nèi)存碎片化赊淑,在清理過程中將對象移動仅讽,以確保移動后的對象占用連續(xù)的內(nèi)存空間。
5.Java的內(nèi)存模型
Java內(nèi)存模型(Java Memory Model ,JMM)就是一種符合內(nèi)存模型規(guī)范的洁灵,屏蔽了各種硬件和操作系統(tǒng)的訪問差異的饱岸,保證了Java程序在各種平臺下對內(nèi)存的訪問都能保證效果一致的機制及規(guī)范。
6.多進程和多線程
進程是資源分配的最小單位汤锨,線程是CPU調(diào)度的最小單位
7. 線程的休眠方式,sleep和wait哪個會釋放鎖百框?
sleep是Thread類的方法,不會釋放對象鎖琅翻。
wait是Objec類的方法位仁,線程會釋放對象鎖方椎,可以用notify()或者notifyAll()喚醒。
8. String棠众、StringBuffer琳疏、StringBuilder的區(qū)別
都是被final修飾的闸拿,都不允許被繼承。
String長度固定新荤,StringBuffer和StringBuilder長度是可以改變的;StringBuffer是線程安全的苛骨,StringBuilder不是線程安全的篱瞎。StringBuffer多用于多線程痒芝,操作大量數(shù)據(jù),StringBuilder用于單線程操作大量數(shù)據(jù)严衬,效率優(yōu)于StringBuffer澄者;拼接字符串不建議使用+请琳,因為內(nèi)部也是額外創(chuàng)建StringBuffer來完成的。
9. 抽象類和接口有什么區(qū)別单起?
共同點:是上層的抽象層抱怔。 都不能被實例化嘀倒。 都能包含抽象的方法局冰,這些抽象的方法用于描述類具備的功能,但是不會提供具體的實現(xiàn)灌危。
區(qū)別:抽象類里面可以寫非抽象方法,接口中只能有抽象方法勇蝙;類只能繼承一個類,可以是抽象類味混。類可以實現(xiàn)多個接口产雹。抽象類抽象方法可以用所有修飾符修飾翁锡,接口里面的方法都是public,在JDK1.8允許一個靜態(tài)方法和多個Default方法馆衔。抽象類可以有構(gòu)造方法瘟判,接口不能有構(gòu)造器角溃。
10.單例模式,雙判空懶漢單例模式减细,為什么變量用volitale修飾匆瓜,new不是原子操作
用volitale修飾未蝌,是禁止指令重排。new不是原子操作树埠,有多個步驟。