Java知識復習解答
1.synchronize 和 volitale的區(qū)別;什么是可見性和原子性
可見性:當一個線程修改了線程共享變量的值,其他線程能夠立即得知這個修改
原子性:一系列的操作要么全部執(zhí)行完,要么都不執(zhí)行
有序性:如果在本線程內(nèi)觀察,所有操作都是有序的;如果在一個線程中觀察另一個線程,所有操作都是無序的。前半句是指“線程內(nèi)表現(xiàn)為串行語義”,后半句是指“指令重排序”現(xiàn)象和“工作內(nèi)存中主內(nèi)存同步延遲”現(xiàn)象。
synchronized具有原子性,可見性,有序性,同一時刻只能有一個線程進行獲??;會造成線程阻塞;標記的變量會被編譯器優(yōu)化;可以使用在變量,方法,類級別中
volatile具有可見性,有序性,不能保證原子性,(需要保證原子性,1:運算結(jié)果不依賴變量的當前值,或者只有一個線程修改變量的值;2:該變量沒有包含在具有其他變量的不變式中);不會造成線程阻塞;標記的變量不會被編譯器優(yōu)化(防止指令重排);只能使用在變量級別
2.Java的集合類
Java的集合類是放在java.util包下;集合類存放的是對象的引用,而非對象的本身;集合類主要分為Set(集),List(列表),Map(映射)。
List和Set是Collection(接口)的子類。
Set:不能包含重復的元素,沒有順序
List:
-
ArrayList:是List的子類,底層是動態(tài)擴容的數(shù)組結(jié)構(gòu)??梢源娣胖貜偷脑兀ò╪ull);有序,按照元素的添加順序,多用于查詢多增刪操作少的情況,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形式,鍵不能重復,值可以重復。對Map集合遍歷時先得到鍵的set集合,再對set集合進行遍歷,得到相應的值。
HashMap:
- 存儲數(shù)據(jù)是根據(jù)鍵值對存儲數(shù)據(jù)的,并且存儲多個數(shù)據(jù)時,數(shù)據(jù)的鍵不能相同,如果相同該鍵之前對應的值將被覆蓋。
- HashMap最多只允許一條存儲數(shù)據(jù)的鍵為null,可允許多條數(shù)據(jù)的值為null。
- HashMap存儲數(shù)據(jù)的順序是不確定的,并且可能會因為擴容導致元素存儲位置改變。因此遍歷順序是不確定的。
- HashMap是線程不安全的,使用ConcurrentHashMap具有線程安全。
底層存儲結(jié)構(gòu):
JDK1.7之前的存儲結(jié)構(gòu):
拉鏈法,專業(yè)點就叫鏈地址法。就是數(shù)組加鏈表的結(jié)合。每一個數(shù)組元素上存儲的都是一個鏈表。新添加進來的元素總是放在數(shù)組對應的角標位置,而原來處于該角標的位置的節(jié)點作為next節(jié)點放到新節(jié)點的后邊。
JDK1.8中的數(shù)據(jù)結(jié)構(gòu):
如果單單是數(shù)組加鏈表的話,當處理hash值沖突較多的情況下,鏈表的長度會越來越長,查找的效率會越來越低了。所以在1.8中當新增節(jié)點導致鏈表的長度超過8的時候,就會在添加元素的同事將單鏈表轉(zhuǎn)化為紅黑樹。紅黑樹是一種易于增刪改查的二叉樹,這樣HashMap中的元素操作起來就會更高效。
3. Java中的引用
- StrongReference(強引用):從不回收,對象一直存在,垃圾回收器絕對不會回收它;當JVM停止的時候才被終止
- SoftReference(軟引用):可以和引用隊列(ReferenceQueue)聯(lián)合使用;內(nèi)存足夠,不會回收,內(nèi)存不夠就會回收;
- WeakReference(弱引用):可以和引用隊列(ReferenceQueue)聯(lián)合使用,當內(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í)行,也就是所謂的當前方法。會存儲當前線程正在執(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并沒有采用這種方法。而是采用追蹤性垃圾收集,就是根搜索算法。
常見的垃圾收集算法:
- 復制算法:高效,但是需要提前預留空間,有一定浪費。
- 標記-清除:先進行標記,標記處所有要回收的對象,然后進行清除。有碎片化的問題,不適合特別大的堆。
- 標記-整理:類似清除,但是為了避免內(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不是原子操作,有多個步驟。