亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

java面試題2020搶先看(夠全)

  發(fā)布時(shí)間:2020-01-02 15:40:42   作者:JAVA高級(jí)架構(gòu)   我要評(píng)論
這篇文章主要介紹了java面試題2020搶先看(夠全),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

Java 面試隨著時(shí)間的改變而改變。在過(guò)去的日子里,當(dāng)你知道 String 和 StringBuilder 的區(qū)別(String 類型和 StringBuffer 類型的主要性能區(qū)別其實(shí)在于 String 是不可變的對(duì)象。因此在每次對(duì) String 類型進(jìn)行改變的時(shí)候其實(shí)都等同于生成了一個(gè)新的 String 對(duì)象,然后將指針指向新的 String 對(duì)象,所以經(jīng)常改變內(nèi)容的字符串最好不要用 String ,因?yàn)槊看紊蓪?duì)象都會(huì)對(duì)系統(tǒng)性能產(chǎn)生影響,特別當(dāng)內(nèi)存中無(wú)引用對(duì)象多了以后,JVM 的 GC 就會(huì)開(kāi)始工作,影響性能,可以考慮使用可變字符序列StringBuilder)就能讓你直接進(jìn)入第二輪面試,但是現(xiàn)在問(wèn)題變得越來(lái)越高級(jí),面試官問(wèn)的問(wèn)題也更深入。在我初入職場(chǎng)的時(shí)候,類似于 Vector 與 Array 的區(qū)別、HashMap 與 Hashtable 的區(qū)別是最流行的問(wèn)題,只需要記住它們,就能在面試中獲得更好的機(jī)會(huì),但這種情形已經(jīng)不復(fù)存在。如今,你將會(huì)被問(wèn)到許多 Java 程序員都沒(méi)有看過(guò)的領(lǐng)域,如 NIO,設(shè)計(jì)模式,成熟的單元測(cè)試,或者那些很難掌握的知識(shí),如并發(fā)、算法、數(shù)據(jù)結(jié)構(gòu)及編碼。

由于我喜歡研究面試題,因此我已經(jīng)收集了許多的面試問(wèn)題,包括許多許多不同的主題。我已經(jīng)為這眾多的問(wèn)題準(zhǔn)備一段時(shí)間了,現(xiàn)在我將它們分享給你們。這里面不但包含經(jīng)典的面試問(wèn)題,如線程、集合、equals 和 hashcode、socket,而且還包含了 NIO、數(shù)組、字符串、Java 8 等主題。

該列表包含了入門級(jí) Java 程序員和多年經(jīng)驗(yàn)的高級(jí)開(kāi)發(fā)者的問(wèn)題。無(wú)論你是 1、2、3、4、5、6、7、8、9 還是 10 年經(jīng)驗(yàn)的開(kāi)發(fā)者,你都能在其中找到一些有趣的問(wèn)題。這里包含了一些超級(jí)容易回答的問(wèn)題,同時(shí)包含經(jīng)驗(yàn)豐富的 Java 程序員也會(huì)棘手的問(wèn)題。

當(dāng)然你們也是非常幸運(yùn)的,當(dāng)今有許多好的書來(lái)幫助你準(zhǔn)備 Java 面試,其中有一本我覺(jué)得特別有用和有趣的是 Markham 的 Java 程序面試揭秘(Java Programming Interview Exposed)。這本書會(huì)告訴你一些 Java 和 JEE 面試中最重要的主題,即使你不是準(zhǔn)備 Java 面試,也值得一讀。

該問(wèn)題列表特別長(zhǎng),我們有各個(gè)地方的問(wèn)題,所以,答案必須要短小、簡(jiǎn)潔、干脆,不拖泥帶水。因此,除了這一個(gè)段落,你只會(huì)聽(tīng)到問(wèn)題與答案,再無(wú)其他內(nèi)容,沒(méi)有反饋,也沒(méi)有評(píng)價(jià)。為此,我已經(jīng)寫好了一些博文,在這些文章中你可以找到我對(duì)某些問(wèn)題的觀點(diǎn),如我為什么喜歡這個(gè)問(wèn)題,這個(gè)問(wèn)題的挑戰(zhàn)是什么?期望從面試者那獲取到什么樣的答案?

這個(gè)列表有一點(diǎn)不同,我鼓勵(lì)你采用類似的方式去分享問(wèn)題和答案,這樣容易溫習(xí)。我希望這個(gè)列表對(duì)面試官和候選人都有很好的用處,面試官可以對(duì)這些問(wèn)題上做一些改變以獲取新奇和令人驚奇的元素,這對(duì)一次好的面試來(lái)說(shuō)非常重要。而候選者,可以擴(kuò)展和測(cè)試 Java 程序語(yǔ)言和平臺(tái)關(guān)鍵領(lǐng)域的知識(shí)。2015 年,會(huì)更多的關(guān)注并發(fā)概念,JVM 內(nèi)部,32 位 JVM 和 64 JVM的區(qū)別,單元測(cè)試及整潔的代碼。我確信,如果你讀過(guò)這個(gè)龐大的 Java 面試問(wèn)題列表,無(wú)論是電話面試還是面對(duì)面的面試,你都能有很好的表現(xiàn)。

Java 面試中的重要話題

除了你看到的驚人的問(wèn)題數(shù)量,我也盡量保證質(zhì)量。我不止一次分享各個(gè)重要主題中的問(wèn)題,也確保包含所謂的高級(jí)話題,這些話題很多程序員不喜歡準(zhǔn)備或者直接放棄,因?yàn)樗麄兊墓ぷ鞑粫?huì)涉及到這些。Java NIO 和 JVM 底層就是最好的例子。你也可以將設(shè)計(jì)模式劃分到這一類中,但是越來(lái)越多有經(jīng)驗(yàn)的程序員了解 GOF 設(shè)計(jì)模式并應(yīng)用這些模式。我也盡量在這個(gè)列表中包含 2015 年最新的面試問(wèn)題,這些問(wèn)題可能是來(lái)年關(guān)注的核心。為了給你一個(gè)大致的了解,下面列出這份 Java 面試問(wèn)題列表包含的主題:

  • 多線程,并發(fā)及線程基礎(chǔ)
  • 數(shù)據(jù)類型轉(zhuǎn)換的基本原則
  • 垃圾回收(GC)
  • Java 集合框架
  • 數(shù)組
  • 字符串
  • GOF 設(shè)計(jì)模式
  • SOLID (單一功能、開(kāi)閉原則、里氏替換、接口隔離以及依賴反轉(zhuǎn))設(shè)計(jì)原則
  • 抽象類與接口
  • Java 基礎(chǔ),如 equals 和 hashcode
  • 泛型與枚舉
  • Java IO 與 NIO
  • 常用網(wǎng)絡(luò)協(xié)議
  • Java 中的數(shù)據(jù)結(jié)構(gòu)和算法
  • 正則表達(dá)式
  • JVM 底層
  • Java 最佳實(shí)踐
  • JDBC
  • Date, Time 與 Calendar
  • Java 處理 XML
  • JUnit
  • 編程

120 大 Java 面試題及答案

現(xiàn)在是時(shí)候給你展示我近 5 年從各種面試中收集來(lái)的 120 個(gè)問(wèn)題了。我確定你在自己的面試中見(jiàn)過(guò)很多這些問(wèn)題,很多問(wèn)題你也能正確回答。

多線程、并發(fā)及線程的基礎(chǔ)問(wèn)題

1)Java 中能創(chuàng)建 volatile 數(shù)組嗎?

能,Java 中可以創(chuàng)建 volatile 類型數(shù)組,不過(guò)只是一個(gè)指向數(shù)組的引用,而不是整個(gè)數(shù)組。我的意思是,如果改變引用指向的數(shù)組,將會(huì)受到 volatile 的保護(hù),但是如果多個(gè)線程同時(shí)改變數(shù)組的元素,volatile 標(biāo)示符就不能起到之前的保護(hù)作用了。

2)volatile 能使得一個(gè)非原子操作變成原子操作嗎?

一個(gè)典型的例子是在類中有一個(gè) long 類型的成員變量。如果你知道該成員變量會(huì)被多個(gè)線程訪問(wèn),如計(jì)數(shù)器、價(jià)格等,你最好是將其設(shè)置為 volatile。為什么?因?yàn)?Java 中讀取 long 類型變量不是原子的,需要分成兩步,如果一個(gè)線程正在修改該 long 變量的值,另一個(gè)線程可能只能看到該值的一半(前 32 位)。但是對(duì)一個(gè) volatile 型的 long 或 double 變量的讀寫是原子。

3)volatile 修飾符的有過(guò)什么實(shí)踐?

一種實(shí)踐是用 volatile 修飾 long 和 double 變量,使其能按原子類型來(lái)讀寫。double 和 long 都是64位寬,因此對(duì)這兩種類型的讀是分為兩部分的,第一次讀取第一個(gè) 32 位,然后再讀剩下的 32 位,這個(gè)過(guò)程不是原子的,但 Java 中 volatile 型的 long 或 double 變量的讀寫是原子的。volatile 修復(fù)符的另一個(gè)作用是提供內(nèi)存屏障(memory barrier),例如在分布式框架中的應(yīng)用。簡(jiǎn)單的說(shuō),就是當(dāng)你寫一個(gè) volatile 變量之前,Java 內(nèi)存模型會(huì)插入一個(gè)寫屏障(write barrier),讀一個(gè) volatile 變量之前,會(huì)插入一個(gè)讀屏障(read barrier)。意思就是說(shuō),在你寫一個(gè) volatile 域時(shí),能保證任何線程都能看到你寫的值,同時(shí),在寫之前,也能保證任何數(shù)值的更新對(duì)所有線程是可見(jiàn)的,因?yàn)閮?nèi)存屏障會(huì)將其他所有寫的值更新到緩存。

4)volatile 類型變量提供什么保證?

volatile 變量提供順序和可見(jiàn)性保證,例如,JVM 或者 JIT為了獲得更好的性能會(huì)對(duì)語(yǔ)句重排序,但是 volatile 類型變量即使在沒(méi)有同步塊的情況下賦值也不會(huì)與其他語(yǔ)句重排序。volatile 提供 happens-before 的保證,確保一個(gè)線程的修改能對(duì)其他線程是可見(jiàn)的。某些情況下,volatile 還能提供原子性,如讀 64 位數(shù)據(jù)類型,像 long 和 double 都不是原子的,但 volatile 類型的 double 和 long 就是原子的。

5) 10 個(gè)線程和 2 個(gè)線程的同步代碼,哪個(gè)更容易寫?

從寫代碼的角度來(lái)說(shuō),兩者的復(fù)雜度是相同的,因?yàn)橥酱a與線程數(shù)量是相互獨(dú)立的。但是同步策略的選擇依賴于線程的數(shù)量,因?yàn)樵蕉嗟木€程意味著更大的競(jìng)爭(zhēng),所以你需要利用同步技術(shù),如鎖分離,這要求更復(fù)雜的代碼和專業(yè)知識(shí)。

6)你是如何調(diào)用 wait()方法的?使用 if 塊還是循環(huán)?為什么?

wait() 方法應(yīng)該在循環(huán)調(diào)用,因?yàn)楫?dāng)線程獲取到 CPU 開(kāi)始執(zhí)行的時(shí)候,其他條件可能還沒(méi)有滿足,所以在處理前,循環(huán)檢測(cè)條件是否滿足會(huì)更好。下面是一段標(biāo)準(zhǔn)的使用 wait 和 notify 方法的代碼:

// The standard idiom for using the wait
methodsynchronized (obj)
{
    while (condition does not hold)obj.wait(); 
   // (Releases lock, and reacquires on wakeup)...
   // Perform action appropriate to condition
   }

參見(jiàn) Effective Java 第 69 條,獲取更多關(guān)于為什么應(yīng)該在循環(huán)中來(lái)調(diào)用 wait 方法的內(nèi)容。

7)什么是多線程環(huán)境下的偽共享(false sharing)?

偽共享是多線程系統(tǒng)(每個(gè)處理器有自己的局部緩存)中一個(gè)眾所周知的性能問(wèn)題。偽共享發(fā)生在不同處理器的上的線程對(duì)變量的修改依賴于相同的緩存行,如下圖所示:

偽共享問(wèn)題很難被發(fā)現(xiàn),因?yàn)榫€程可能訪問(wèn)完全不同的全局變量,內(nèi)存中卻碰巧在很相近的位置上。如其他諸多的并發(fā)問(wèn)題,避免偽共享的最基本方式是仔細(xì)審查代碼,根據(jù)緩存行來(lái)調(diào)整你的數(shù)據(jù)結(jié)構(gòu)。

有經(jīng)驗(yàn)程序員的 Java 面試題

8)什么是 Busy spin?我們?yōu)槭裁匆褂盟?/strong>

Busy spin 是一種在不釋放 CPU 的基礎(chǔ)上等待事件的技術(shù)。它經(jīng)常用于避免丟失 CPU 緩存中的數(shù)據(jù)(如果線程先暫停,之后在其他CPU上運(yùn)行就會(huì)丟失)。所以,如果你的工作要求低延遲,并且你的線程目前沒(méi)有任何順序,這樣你就可以通過(guò)循環(huán)檢測(cè)隊(duì)列中的新消息來(lái)代替調(diào)用 sleep() 或 wait() 方法。它唯一的好處就是你只需等待很短的時(shí)間,如幾微秒或幾納秒。LMAX 分布式框架是一個(gè)高性能線程間通信的庫(kù),該庫(kù)有一個(gè) BusySpinWaitStrategy 類就是基于這個(gè)概念實(shí)現(xiàn)的,使用 busy spin 循環(huán) EventProcessors 等待屏障。

9)Java 中怎么獲取一份線程 dump 文件?

在 Linux 下,你可以通過(guò)命令 kill -3 PID (Java 進(jìn)程的進(jìn)程 ID)來(lái)獲取 Java 應(yīng)用的 dump 文件。在 Windows 下,你可以按下 Ctrl + Break 來(lái)獲取。這樣 JVM 就會(huì)將線程的 dump 文件打印到標(biāo)準(zhǔn)輸出或錯(cuò)誤文件中,它可能打印在控制臺(tái)或者日志文件中,具體位置依賴應(yīng)用的配置。如果你使用Tomcat。

10)Swing 是線程安全的?

不是,Swing 不是線程安全的。你不能通過(guò)任何線程來(lái)更新 Swing 組件,如 JTable、JList 或 JPanel,事實(shí)上,它們只能通過(guò) GUI 或 AWT 線程來(lái)更新。這就是為什么 Swing 提供 invokeAndWait() 和 invokeLater() 方法來(lái)獲取其他線程的 GUI 更新請(qǐng)求。這些方法將更新請(qǐng)求放入 AWT 的線程隊(duì)列中,可以一直等待,也可以通過(guò)異步更新直接返回結(jié)果。你也可以在參考答案中查看和學(xué)習(xí)到更詳細(xì)的內(nèi)容。

11)什么是線程局部變量?

當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,每個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本,是線程隔離的。線程隔離的秘密在于ThreadLocalMap類(ThreadLocal的靜態(tài)內(nèi)部類)

線程局部變量是局限于線程內(nèi)部的變量,屬于線程自身所有,不在多個(gè)線程間共享。Java 提供 ThreadLocal 類來(lái)支持線程局部變量,是一種實(shí)現(xiàn)線程安全的方式。但是在管理環(huán)境下(如 web 服務(wù)器)使用線程局部變量的時(shí)候要特別小心,在這種情況下,工作線程的生命周期比任何應(yīng)用變量的生命周期都要長(zhǎng)。任何線程局部變量一旦在工作完成后沒(méi)有釋放,Java 應(yīng)用就存在內(nèi)存泄露的風(fēng)險(xiǎn)。

ThreadLocal的方法:void set(T value)、T get()以及T initialValue()。

ThreadLocal是如何為每個(gè)線程創(chuàng)建變量的副本的:

首先,在每個(gè)線程Thread內(nèi)部有一個(gè)ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個(gè)threadLocals就是用來(lái)存儲(chǔ)實(shí)際的變量副本的,鍵值為當(dāng)前ThreadLocal變量,value為變量副本(即T類型的變量)。初始時(shí),在Thread里面,threadLocals為空,當(dāng)通過(guò)ThreadLocal變量調(diào)用get()方法或者set()方法,就會(huì)對(duì)Thread類中的threadLocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。然后在當(dāng)前線程里面,如果要使用副本變量,就可以通過(guò)get方法在threadLocals里面查找。

總結(jié):

a、實(shí)際的通過(guò)ThreadLocal創(chuàng)建的副本是存儲(chǔ)在每個(gè)線程自己的threadLocals中的

b、為何threadLocals的類型ThreadLocalMap的鍵值為ThreadLocal對(duì)象,因?yàn)槊總€(gè)線程中可有多個(gè)threadLocal變量,就像上面代碼中的longLocal和stringLocal;

c、在進(jìn)行g(shù)et之前,必須先set,否則會(huì)報(bào)空指針異常;如果想在get之前不需要調(diào)用set就能正常訪問(wèn)的話,必須重寫initialValue()方法

12)用 wait-notify 寫一段代碼來(lái)解決生產(chǎn)者-消費(fèi)者問(wèn)題?

請(qǐng)參考答案中的示例代碼。只要記住在同步塊中調(diào)用 wait() 和 notify()方法,如果阻塞,通過(guò)循環(huán)來(lái)測(cè)試等待條件。

13) 用 Java 寫一個(gè)線程安全的單例模式(Singleton)?

請(qǐng)參考答案中的示例代碼,這里面一步一步教你創(chuàng)建一個(gè)線程安全的 Java 單例類。當(dāng)我們說(shuō)線程安全時(shí),意思是即使初始化是在多線程環(huán)境中,仍然能保證單個(gè)實(shí)例。Java 中,使用枚舉作為單例類是最簡(jiǎn)單的方式來(lái)創(chuàng)建線程安全單例模式的方式。

14)Java 中 sleep 方法和 wait 方法的區(qū)別?

雖然兩者都是用來(lái)暫停當(dāng)前運(yùn)行的線程,但是 sleep() 實(shí)際上只是短暫停頓,因?yàn)樗粫?huì)釋放鎖,而 wait() 意味著條件等待,這就是為什么該方法要釋放鎖,因?yàn)橹挥羞@樣,其他等待的線程才能在滿足條件時(shí)獲取到該鎖。

15)什么是不可變對(duì)象(immutable object)?Java 中怎么創(chuàng)建一個(gè)不可變對(duì)象?

不可變對(duì)象指對(duì)象一旦被創(chuàng)建,狀態(tài)就不能再改變。任何修改都會(huì)創(chuàng)建一個(gè)新的對(duì)象,如 String、Integer及其它包裝類。詳情參見(jiàn)答案,一步一步指導(dǎo)你在 Java 中創(chuàng)建一個(gè)不可變的類。

16)我們能創(chuàng)建一個(gè)包含可變對(duì)象的不可變對(duì)象嗎?

是的,我們是可以創(chuàng)建一個(gè)包含可變對(duì)象的不可變對(duì)象的,你只需要謹(jǐn)慎一點(diǎn),不要共享可變對(duì)象的引用就可以了,如果需要變化時(shí),就返回原對(duì)象的一個(gè)拷貝。最常見(jiàn)的例子就是對(duì)象中包含一個(gè)日期對(duì)象的引用。

數(shù)據(jù)類型和 Java 基礎(chǔ)面試問(wèn)題

17)Java 中應(yīng)該使用什么數(shù)據(jù)類型來(lái)代表價(jià)格?

如果不是特別關(guān)心內(nèi)存和性能的話,使用BigDecimal,否則使用預(yù)定義精度的 double 類型。

18)怎么將 byte 轉(zhuǎn)換為 String?

可以使用 String 接收 byte[] 參數(shù)的構(gòu)造器來(lái)進(jìn)行轉(zhuǎn)換,需要注意的點(diǎn)是要使用的正確的編碼,否則會(huì)使用平臺(tái)默認(rèn)編碼,這個(gè)編碼可能跟原來(lái)的編碼相同,也可能不同。

19)Java 中怎樣將 bytes 轉(zhuǎn)換為 long 類型?

String接收bytes的構(gòu)造器轉(zhuǎn)成String,再Long.parseLong

20)我們能將 int 強(qiáng)制轉(zhuǎn)換為 byte 類型的變量嗎?如果該值大于 byte 類型的范圍,將會(huì)出現(xiàn)什么現(xiàn)象?

是的,我們可以做強(qiáng)制轉(zhuǎn)換,但是 Java 中 int 是 32 位的,而 byte 是 8 位的,所以,如果強(qiáng)制轉(zhuǎn)化是,int 類型的高 24 位將會(huì)被丟棄,byte 類型的范圍是從 -128 到 127。

21)存在兩個(gè)類,B 繼承 A,C 繼承 B,我們能將 B 轉(zhuǎn)換為 C 么?如 C = (C) B;

可以,向下轉(zhuǎn)型。但是不建議使用,容易出現(xiàn)類型轉(zhuǎn)型異常.

22)哪個(gè)類包含 clone 方法?是 Cloneable 還是 Object?

java.lang.Cloneable 是一個(gè)標(biāo)示性接口,不包含任何方法,clone 方法在 object 類中定義。并且需要知道 clone() 方法是一個(gè)本地方法,這意味著它是由 c 或 c++ 或 其他本地語(yǔ)言實(shí)現(xiàn)的。

23)Java 中 ++ 操作符是線程安全的嗎?

不是線程安全的操作。它涉及到多個(gè)指令,如讀取變量值,增加,然后存儲(chǔ)回內(nèi)存,這個(gè)過(guò)程可能會(huì)出現(xiàn)多個(gè)線程交差。

24)a = a + b 與 a += b 的區(qū)別

+= 隱式的將加操作的結(jié)果類型強(qiáng)制轉(zhuǎn)換為持有結(jié)果的類型。如果兩這個(gè)整型相加,如 byte、short 或者 int,首先會(huì)將它們提升到 int 類型,然后在執(zhí)行加法操作。

byte a = 127;byte b = 127;b = a + b; // error : cannot convert from int to byteb += a; // ok

(因?yàn)?a+b 操作會(huì)將 a、b 提升為 int 類型,所以將 int 類型賦值給 byte 就會(huì)編譯出錯(cuò))

25)我能在不進(jìn)行強(qiáng)制轉(zhuǎn)換的情況下將一個(gè) double 值賦值給 long 類型的變量嗎?

不行,你不能在沒(méi)有強(qiáng)制類型轉(zhuǎn)換的前提下將一個(gè) double 值賦值給 long 類型的變量,因?yàn)?double 類型的范圍比 long 類型更廣,所以必須要進(jìn)行強(qiáng)制轉(zhuǎn)換。

26)3*0.1 == 0.3 將會(huì)返回什么?true 還是 false?

false,因?yàn)橛行└↑c(diǎn)數(shù)不能完全精確的表示出來(lái)。

27)int 和 Integer 哪個(gè)會(huì)占用更多的內(nèi)存?

Integer 對(duì)象會(huì)占用更多的內(nèi)存。Integer 是一個(gè)對(duì)象,需要存儲(chǔ)對(duì)象的元數(shù)據(jù)。但是 int 是一個(gè)原始類型的數(shù)據(jù),所以占用的空間更少。

28)為什么 Java 中的 String 是不可變的(Immutable)?

Java 中的 String 不可變是因?yàn)?Java 的設(shè)計(jì)者認(rèn)為字符串使用非常頻繁,將字符串設(shè)置為不可變可以允許多個(gè)客戶端之間共享相同的字符串。更詳細(xì)的內(nèi)容參見(jiàn)答案。

29)我們能在 Switch 中使用 String 嗎?

從 Java 7 開(kāi)始,我們可以在 switch case 中使用字符串,但這僅僅是一個(gè)語(yǔ)法糖。內(nèi)部實(shí)現(xiàn)在 switch 中使用字符串的 hash code。

30)Java 中的構(gòu)造器鏈?zhǔn)鞘裁矗?/strong>

當(dāng)你從一個(gè)構(gòu)造器中調(diào)用另一個(gè)構(gòu)造器,就是Java 中的構(gòu)造器鏈。這種情況只在重載了類的構(gòu)造器的時(shí)候才會(huì)出現(xiàn)。

JVM 底層 與 GC(Garbage Collection) 的面試問(wèn)題

31)64 位 JVM 中,int 的長(zhǎng)度是多數(shù)?

Java 中,int 類型變量的長(zhǎng)度是一個(gè)固定值,與平臺(tái)無(wú)關(guān),都是 32 位。意思就是說(shuō),在 32 位 和 64 位 的Java 虛擬機(jī)中,int 類型的長(zhǎng)度是相同的。

32)Serial 與 Parallel GC之間的不同之處?

Serial 與 Parallel 在GC執(zhí)行的時(shí)候都會(huì)引起 stop-the-world。它們之間主要不同 serial 收集器是默認(rèn)的復(fù)制收集器,執(zhí)行 GC 的時(shí)候只有一個(gè)線程,而 parallel 收集器使用多個(gè) GC 線程來(lái)執(zhí)行。

33)32 位和 64 位的 JVM,int 類型變量的長(zhǎng)度是多數(shù)?

32 位和 64 位的 JVM 中,int 類型變量的長(zhǎng)度是相同的,都是 32 位或者 4 個(gè)字節(jié)。

34)Java 中 WeakReference 與 SoftReference的區(qū)別?

Java中一共有四種類型的引用。StrongReference、 SoftReference、 WeakReference 以及 PhantomReference。

StrongReference 是 Java 的默認(rèn)引用實(shí)現(xiàn), 它會(huì)盡可能長(zhǎng)時(shí)間的存活于 JVM 內(nèi),當(dāng)沒(méi)有任何對(duì)象指向它時(shí)將會(huì)被GC回收

WeakReference,顧名思義, 是一個(gè)弱引用, 當(dāng)所引用的對(duì)象在 JVM 內(nèi)不再有強(qiáng)引用時(shí), 將被GC回收

雖然 WeakReference 與 SoftReference 都有利于提高 GC 和 內(nèi)存的效率,但是 WeakReference ,一旦失去最后一個(gè)強(qiáng)引用,就會(huì)被 GC 回收,而 SoftReference 會(huì)盡可能長(zhǎng)的保留引用直到 JVM 內(nèi)存不足時(shí)才會(huì)被回收(虛擬機(jī)保證), 這一特性使得 SoftReference 非常適合緩存應(yīng)用

35)WeakHashMap 是怎么工作的?

WeakHashMap 的工作與正常的 HashMap 類似,但是使用弱引用作為 key,意思就是當(dāng) key 對(duì)象沒(méi)有任何引用時(shí),key/value 將會(huì)被回收。

36)JVM 選項(xiàng) -XX:+UseCompressedOops 有什么作用?為什么要使用?

當(dāng)你將你的應(yīng)用從 32 位的 JVM 遷移到 64 位的 JVM 時(shí),由于對(duì)象的指針從 32 位增加到了 64 位,因此堆內(nèi)存會(huì)突然增加,差不多要翻倍。這也會(huì)對(duì) CPU 緩存(容量比內(nèi)存小很多)的數(shù)據(jù)產(chǎn)生不利的影響。因?yàn)?,遷移到 64 位的 JVM 主要?jiǎng)訖C(jī)在于可以指定最大堆大小,通過(guò)壓縮 OOP 可以節(jié)省一定的內(nèi)存。通過(guò) -XX:+UseCompressedOops 選項(xiàng),JVM 會(huì)使用 32 位的 OOP,而不是 64 位的 OOP。

37)怎樣通過(guò) Java 程序來(lái)判斷 JVM 是 32 位 還是 64 位?

你可以檢查某些系統(tǒng)屬性如 sun.arch.data.model 或 os.arch 來(lái)獲取該信息。

38)32 位 JVM 和 64 位 JVM 的最大堆內(nèi)存分別是多數(shù)?

理論上說(shuō)上 32 位的 JVM 堆內(nèi)存可以到達(dá) 2^32,即 4GB,但實(shí)際上會(huì)比這個(gè)小很多。不同操作系統(tǒng)之間不同,如 Windows 系統(tǒng)大約 1.5 GB,Solaris 大約 3GB。64 位 JVM允許指定最大的堆內(nèi)存,理論上可以達(dá)到 2^64,這是一個(gè)非常大的數(shù)字,實(shí)際上你可以指定堆內(nèi)存大小到 100GB。甚至有的 JVM,如 Azul,堆內(nèi)存到 1000G 都是可能的。

39)JRE、JDK、JVM 及 JIT 之間有什么不同?

JRE 代表 Java 運(yùn)行時(shí)(Java run-time),是運(yùn)行 Java 應(yīng)用所必須的。JDK 代表 Java 開(kāi)發(fā)工具(Java development kit),是 Java 程序的開(kāi)發(fā)工具,如 Java 編譯器,它也包含 JRE。JVM 代表 Java 虛擬機(jī)(Java virtual machine),它的責(zé)任是運(yùn)行 Java 應(yīng)用。JIT 代表即時(shí)編譯(Just In Time compilation),當(dāng)代碼執(zhí)行的次數(shù)超過(guò)一定的閾值時(shí),會(huì)將 Java 字節(jié)碼轉(zhuǎn)換為本地代碼,如,主要的熱點(diǎn)代碼會(huì)被準(zhǔn)換為本地代碼,這樣有利大幅度提高 Java 應(yīng)用的性能。

3 年工作經(jīng)驗(yàn)的 Java 面試題

40)解釋 Java 堆空間及 GC?

當(dāng)通過(guò) Java 命令啟動(dòng) Java 進(jìn)程的時(shí)候,會(huì)為它分配內(nèi)存。內(nèi)存的一部分用于創(chuàng)建堆空間,當(dāng)程序中創(chuàng)建對(duì)象的時(shí)候,就從對(duì)空間中分配內(nèi)存。GC 是 JVM 內(nèi)部的一個(gè)進(jìn)程,回收無(wú)效對(duì)象的內(nèi)存用于將來(lái)的分配。

JVM 底層面試題及答案

41)你能保證 GC 執(zhí)行嗎?

不能,雖然你可以調(diào)用 System.gc() 或者 Runtime.getRuntime().gc(),但是沒(méi)有辦法保證 GC 的執(zhí)行。

42)怎么獲取 Java 程序使用的內(nèi)存?堆使用的百分比?

可以通過(guò) java.lang.Runtime 類中與內(nèi)存相關(guān)方法來(lái)獲取剩余的內(nèi)存,總內(nèi)存及最大堆內(nèi)存。通過(guò)這些方法你也可以獲取到堆使用的百分比及堆內(nèi)存的剩余空間。Runtime.freeMemory() 方法返回剩余空間的字節(jié)數(shù),Runtime.totalMemory() 方法總內(nèi)存的字節(jié)數(shù),Runtime.maxMemory() 返回最大內(nèi)存的字節(jié)數(shù)。

43)Java 中堆和棧有什么區(qū)別?

JVM 中堆和棧屬于不同的內(nèi)存區(qū)域,使用目的也不同。棧常用于保存方法幀和局部變量,而對(duì)象總是在堆上分配。棧通常都比堆小,也不會(huì)在多個(gè)線程之間共享,而堆被整個(gè) JVM 的所有線程共享。Difference between stack and heap memory in Java

關(guān)于內(nèi)存的的面試問(wèn)題和答案

Java 基本概念面試題

44)“a==b”和”a.equals(b)”有什么區(qū)別?

如果 a 和 b 都是對(duì)象,則 a==b 是比較兩個(gè)對(duì)象的引用,只有當(dāng) a 和 b 指向的是堆中的同一個(gè)對(duì)象才會(huì)返回 true,而 a.equals(b) 是進(jìn)行邏輯比較,所以通常需要重寫該方法來(lái)提供邏輯一致性的比較。例如,String 類重寫 equals() 方法,所以可以用于兩個(gè)不同對(duì)象,但是包含的字母相同的比較。

45)a.hashCode() 有什么用?與 a.equals(b) 有什么關(guān)系?

hashCode() 方法是相應(yīng)對(duì)象整型的 hash 值。它常用于基于 hash 的集合類,如 Hashtable、HashMap、LinkedHashMap等等。它與 equals() 方法關(guān)系特別緊密。根據(jù) Java 規(guī)范,兩個(gè)使用 equal() 方法來(lái)判斷相等的對(duì)象,必須具有相同的 hash code。

46)final、finalize 和 finally 的不同之處?

final 是一個(gè)修飾符,可以修飾變量、方法和類。如果 final 修飾變量,意味著該變量的值在初始化后不能被改變。Java 技術(shù)允許使用 finalize() 方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。這個(gè)方法是由垃圾收集器在確定這個(gè)對(duì)象沒(méi)有被引用時(shí)對(duì)這個(gè)對(duì)象調(diào)用的,但是什么時(shí)候調(diào)用 finalize 沒(méi)有保證。finally 是一個(gè)關(guān)鍵字,與 try 和 catch 一起用于異常的處理。finally 塊一定會(huì)被執(zhí)行,無(wú)論在 try 塊中是否有發(fā)生異常。

47)Java 中的編譯期常量是什么?使用它又什么風(fēng)險(xiǎn)?

公共靜態(tài)不可變(public static final )變量也就是我們所說(shuō)的編譯期常量,這里的 public 可選的。實(shí)際上這些變量在編譯時(shí)會(huì)被替換掉,因?yàn)榫幾g器知道這些變量的值,并且知道這些變量在運(yùn)行時(shí)不能改變。這種方式存在的一個(gè)問(wèn)題是你使用了一個(gè)內(nèi)部的或第三方庫(kù)中的公有編譯時(shí)常量,但是這個(gè)值后面被其他人改變了,但是你的客戶端仍然在使用老的值,甚至你已經(jīng)部署了一個(gè)新的jar。為了避免這種情況,當(dāng)你在更新依賴 JAR 文件時(shí),確保重新編譯你的程序。

Java 集合框架的面試題

這部分也包含數(shù)據(jù)結(jié)構(gòu)、算法及數(shù)組的面試問(wèn)題

48) List、Set、Map 和 Queue 之間的區(qū)別(答案)

List 是一個(gè)有序集合,允許元素重復(fù)。它的某些實(shí)現(xiàn)可以提供基于下標(biāo)值的常量訪問(wèn)時(shí)間,但是這不是 List 接口保證的。Set 是一個(gè)無(wú)序集合。

49)poll() 方法和 remove() 方法的區(qū)別?

poll() 和 remove() 都是從隊(duì)列中取出一個(gè)元素,但是 poll() 在獲取元素失敗的時(shí)候會(huì)返回空,但是 remove() 失敗的時(shí)候會(huì)拋出異常。

50)Java 中 LinkedHashMap 和 PriorityQueue 的區(qū)別是什么?(答案)

PriorityQueue 保證最高或者最低優(yōu)先級(jí)的的元素總是在隊(duì)列頭部,但是 LinkedHashMap 維持的順序是元素插入的順序。當(dāng)遍歷一個(gè) PriorityQueue 時(shí),沒(méi)有任何順序保證,但是 LinkedHashMap 課保證遍歷順序是元素插入的順序。

51)ArrayList 與 LinkedList 的不區(qū)別?(答案)

最明顯的區(qū)別是 ArrrayList 底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,支持隨機(jī)訪問(wèn),而 LinkedList 的底層數(shù)據(jù)結(jié)構(gòu)書鏈表,不支持隨機(jī)訪問(wèn)。使用下標(biāo)訪問(wèn)一個(gè)元素,ArrayList 的時(shí)間復(fù)雜度是 O(1),而 LinkedList 是 O(n)。更多細(xì)節(jié)的討論參見(jiàn)答案。

52)用哪兩種方式來(lái)實(shí)現(xiàn)集合的排序?(答案)

你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有順序的的集合,如 list,然后通過(guò) Collections.sort() 來(lái)排序。

53)Java 中怎么打印數(shù)組?(answer答案)

你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法來(lái)打印數(shù)組。由于數(shù)組沒(méi)有實(shí)現(xiàn) toString() 方法,所以如果將數(shù)組傳遞給 System.out.println() 方法,將無(wú)法打印出數(shù)組的內(nèi)容,但是 Arrays.toString() 可以打印每個(gè)元素。

54)Java 中的 LinkedList 是單向鏈表還是雙向鏈表?(答案)

是雙向鏈表,你可以檢查 JDK 的源碼。在 Eclipse,你可以使用快捷鍵 Ctrl + T,直接在編輯器中打開(kāi)該類。

55)Java 中的 TreeMap 是采用什么樹實(shí)現(xiàn)的?(答案)

Java 中的 TreeMap 是使用紅黑樹實(shí)現(xiàn)的。

56) Hashtable 與 HashMap 有什么不同之處?(答案)

這兩個(gè)類有許多不同的地方,下面列出了一部分:a) Hashtable 是 JDK 1 遺留下來(lái)的類,而 HashMap 是后來(lái)增加的。b)Hashtable 是同步的,比較慢,但 HashMap 沒(méi)有同步策略,所以會(huì)更快。c)Hashtable 不允許有個(gè)空的 key,但是 HashMap 允許出現(xiàn)一個(gè) null key。更多的不同之處參見(jiàn)答案。

57)Java 中的 HashSet,內(nèi)部是如何工作的?(answer答案)

HashSet 的內(nèi)部采用 HashMap來(lái)實(shí)現(xiàn)。由于 Map 需要 key 和 value,所以所有 key 的都有一個(gè)默認(rèn) value。類似于 HashMap,HashSet 不允許重復(fù)的 key,只允許有一個(gè)null key,意思就是 HashSet 中只允許存儲(chǔ)一個(gè) null 對(duì)象。

58)寫一段代碼在遍歷 ArrayList 時(shí)移除一個(gè)元素?(答案)

該問(wèn)題的關(guān)鍵在于面試者使用的是 ArrayList 的 remove() 還是 Iterator 的 remove()方法。這有一段示例代碼,是使用正確的方式來(lái)實(shí)現(xiàn)在遍歷的過(guò)程中移除元素,而不會(huì)出現(xiàn) ConcurrentModificationException 異常的示例代碼。

59)我們能自己寫一個(gè)容器類,然后使用 for-each 循環(huán)嗎?

可以,你可以寫一個(gè)自己的容器類。如果你想使用 Java 中增強(qiáng)的循環(huán)來(lái)遍歷,你只需要實(shí)現(xiàn) Iterable 接口。如果你實(shí)現(xiàn) Collection 接口,默認(rèn)就具有該屬性。

60)ArrayList 和 HashMap 的默認(rèn)大小是多數(shù)?(答案)

在 Java 7 中,ArrayList 的默認(rèn)大小是 10 個(gè)元素,HashMap 的默認(rèn)大小是16個(gè)元素(必須是2的冪)。這就是 Java 7 中 ArrayList 和 HashMap 類的代碼片段:

// from ArrayList.java JDK 1.7
private static final int DEFAULT_CAPACITY = 10;
//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

61)有沒(méi)有可能兩個(gè)不相等的對(duì)象有有相同的 hashcode?

有可能,兩個(gè)不相等的對(duì)象可能會(huì)有相同的 hashcode 值,這就是為什么在 hashmap 中會(huì)有沖突。相等 hashcode 值的規(guī)定只是說(shuō)如果兩個(gè)對(duì)象相等,必須有相同的hashcode 值,但是沒(méi)有關(guān)于不相等對(duì)象的任何規(guī)定。

62)兩個(gè)相同的對(duì)象會(huì)有不同的的 hash code 嗎?

不能,根據(jù) hash code 的規(guī)定,這是不可能的。

63)我們可以在 hashcode() 中使用隨機(jī)數(shù)字嗎?(答案)

不行,因?yàn)閷?duì)象的 hashcode 值必須是相同的。參見(jiàn)答案獲取更多關(guān)于 Java 中重寫 hashCode() 方法的知識(shí)。

64)Java 中,Comparator 與 Comparable 有什么不同?(答案)

Comparable 接口用于定義對(duì)象的自然順序,而 comparator 通常用于定義用戶定制的順序。Comparable 總是只有一個(gè),但是可以有多個(gè) comparator 來(lái)定義對(duì)象的順序。

65)為什么在重寫 equals 方法的時(shí)候需要重寫 hashCode 方法?(答案)

因?yàn)橛袕?qiáng)制的規(guī)范指定需要同時(shí)重寫 hashcode 與 equal 是方法,許多容器類,如 HashMap、HashSet 都依賴于 hashcode 與 equals 的規(guī)定。

Java IO 和 NIO 的面試題

IO 是 Java 面試中一個(gè)非常重要的點(diǎn)。你應(yīng)該很好掌握 Java IO,NIO,NIO2 以及與操作系統(tǒng),磁盤 IO 相關(guān)的基礎(chǔ)知識(shí)。下面是 Java IO 中經(jīng)常問(wèn)的問(wèn)題。

66)在我 Java 程序中,我有三個(gè) socket,我需要多少個(gè)線程來(lái)處理?

67)Java 中怎么創(chuàng)建 ByteBuffer?

68)Java 中,怎么讀寫 ByteBuffer ?

69)Java 采用的是大端還是小端?

70)ByteBuffer 中的字節(jié)序是什么?

71)Java 中,直接緩沖區(qū)與非直接緩沖器有什么區(qū)別?(答案)

72)Java 中的內(nèi)存映射緩存區(qū)是什么?(answer答案)

73)socket 選項(xiàng) TCP NO DELAY 是指什么?

74)TCP 協(xié)議與 UDP 協(xié)議有什么區(qū)別?(answer答案)

75)Java 中,ByteBuffer 與 StringBuffer有什么區(qū)別?(答案)

Java 最佳實(shí)踐的面試問(wèn)題

包含 Java 中各個(gè)部分的最佳實(shí)踐,如集合,字符串,IO,多線程,錯(cuò)誤和異常處理,設(shè)計(jì)模式等等。

76)Java 中,編寫多線程程序的時(shí)候你會(huì)遵循哪些最佳實(shí)踐?(答案)

這是我在寫Java 并發(fā)程序的時(shí)候遵循的一些最佳實(shí)踐:a)給線程命名,這樣可以幫助調(diào)試。b)最小化同步的范圍,而不是將整個(gè)方法同步,只對(duì)關(guān)鍵部分做同步。c)如果可以,更偏向于使用 volatile 而不是 synchronized。d)使用更高層次的并發(fā)工具,而不是使用 wait() 和 notify() 來(lái)實(shí)現(xiàn)線程間通信,如 BlockingQueue,CountDownLatch 及 Semeaphore。e)優(yōu)先使用并發(fā)集合,而不是對(duì)集合進(jìn)行同步。并發(fā)集合提供更好的可擴(kuò)展性。

77)說(shuō)出幾點(diǎn) Java 中使用 Collections 的最佳實(shí)踐(答案)

這是我在使用 Java 中 Collectionc 類的一些最佳實(shí)踐:a)使用正確的集合類,例如,如果不需要同步列表,使用 ArrayList 而不是 Vector。b)優(yōu)先使用并發(fā)集合,而不是對(duì)集合進(jìn)行同步。并發(fā)集合提供更好的可擴(kuò)展性。c)使用接口代表和訪問(wèn)集合,如使用List存儲(chǔ) ArrayList,使用 Map 存儲(chǔ) HashMap 等等。d)使用迭代器來(lái)循環(huán)集合。e)使用集合的時(shí)候使用泛型。

78)說(shuō)出至少 5 點(diǎn)在 Java 中使用線程的最佳實(shí)踐。(答案)

這個(gè)問(wèn)題與之前的問(wèn)題類似,你可以使用上面的答案。對(duì)線程來(lái)說(shuō),你應(yīng)該:a)對(duì)線程命名 b)將線程和任務(wù)分離,使用線程池執(zhí)行器來(lái)執(zhí)行 Runnable 或 Callable。c)使用線程池

79)說(shuō)出 5 條 IO 的最佳實(shí)踐(答案)

IO 對(duì) Java 應(yīng)用的性能非常重要。理想情況下,你不應(yīng)該在你應(yīng)用的關(guān)鍵路徑上避免 IO 操作。下面是一些你應(yīng)該遵循的 Java IO 最佳實(shí)踐:a)使用有緩沖區(qū)的 IO 類,而不要單獨(dú)讀取字節(jié)或字符。b)使用 NIO 和 NIO2 c)在 finally 塊中關(guān)閉流,或者使用 try-with-resource(Java7) 語(yǔ)句。d)使用內(nèi)存映射文件獲取更快的 IO。

80)列出 5 個(gè)應(yīng)該遵循的 JDBC 最佳實(shí)踐(答案)

有很多的最佳實(shí)踐,你可以根據(jù)你的喜好來(lái)例舉。下面是一些更通用的原則:a)使用批量的操作來(lái)插入和更新數(shù)據(jù) b)使用 PreparedStatement 來(lái)避免 SQL 異常,并提高性能。c)使用數(shù)據(jù)庫(kù)連接池 d)通過(guò)列名來(lái)獲取結(jié)果集,不要使用列的下標(biāo)來(lái)獲取。

81)說(shuō)出幾條 Java 中方法重載的最佳實(shí)踐?(答案)

下面有幾條可以遵循的方法重載的最佳實(shí)踐來(lái)避免造成自動(dòng)裝箱的混亂。a)不要重載這樣的方法:一個(gè)方法接收 int 參數(shù),而另個(gè)方法接收 Integer 參數(shù)。b)不要重載參數(shù)數(shù)量一致,而只是參數(shù)順序不同的方法。c)如果重載的方法參數(shù)個(gè)數(shù)多于 5 個(gè),采用可變參數(shù)。

Date、Time 及 Calendar 的面試題

82)在多線程環(huán)境下,SimpleDateFormat 是線程安全的嗎?(答案)

不是,非常不幸,DateFormat 的所有實(shí)現(xiàn),包括 SimpleDateFormat 都不是線程安全的,因此你不應(yīng)該在多線程序中使用,除非是在對(duì)外線程安全的環(huán)境中使用,如 將 SimpleDateFormat 限制在 ThreadLocal 中。如果你不這么做,在解析或者格式化日期的時(shí)候,可能會(huì)獲取到一個(gè)不正確的結(jié)果。因此,從日期、時(shí)間處理的所有實(shí)踐來(lái)說(shuō),我強(qiáng)力推薦 joda-time 庫(kù)。

83)Java 中如何格式化一個(gè)日期?如格式化為 ddMMyyyy 的形式?(答案)

Java 中,可以使用 SimpleDateFormat 類或者 joda-time 庫(kù)來(lái)格式日期。DateFormat 類允許你使用多種流行的格式來(lái)格式化日期。參見(jiàn)答案中的示例代碼,代碼中演示了將日期格式化成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。

84)Java 中,怎么在格式化的日期中顯示時(shí)區(qū)?

pattern中加z yyyy-MM-dd HH:mm:ss.SSS Z

85)Java 中 java.util.Date 與 java.sql.Date 有什么區(qū)別?

java.sql.Date是針對(duì)SQL語(yǔ)句使用的,它只包含日期而沒(méi)有時(shí)間部分,它們都有g(shù)etTime方法返回毫秒數(shù),自然就可以直接構(gòu)建。java.util.Date 是 java.sql.Date 的父類,前者是常用的表示時(shí)間的類,我們通常格式化或者得到當(dāng)前時(shí)間都是用他,后者之后在讀寫數(shù)據(jù)庫(kù)的時(shí)候用他,因?yàn)镻reparedStament的setDate()的第2參數(shù)和ResultSet的getDate()方法的第2個(gè)參數(shù)都是java.sql.Date。

86)Java 中,如何計(jì)算兩個(gè)日期之間的差距?

public static int dateDiff(Date d1, Date d2) throws Exception
{
   long n1 = d1.getTime();long n2 = d2.getTime();
   long diff = Math.abs(n1 - n2);
   diff /= 3600 * 1000 * 24;
   return diff;
}

87)Java 中,如何將字符串 YYYYMMDD 轉(zhuǎn)換為日期?

SimpleDateFormat的parse方法

單元測(cè)試 JUnit 面試題

89)如何測(cè)試靜態(tài)方法?(答案)

可以使用 PowerMock 庫(kù)來(lái)測(cè)試靜態(tài)方法。

90)怎么利用 JUnit 來(lái)測(cè)試一個(gè)方法的異常?(答案)

91)你使用過(guò)哪個(gè)單元測(cè)試庫(kù)來(lái)測(cè)試你的 Java 程序?(答案)

92)@Before 和 @BeforeClass 有什么區(qū)別?(答案)

編程和代碼相關(guān)的面試題

93)怎么檢查一個(gè)字符串只包含數(shù)字?(解決方案)

94)Java 中如何利用泛型寫一個(gè) LRU 緩存?(答案)

95)寫一段 Java 程序?qū)?byte 轉(zhuǎn)換為 long?(答案)

95)在不使用 StringBuffer 的前提下,怎么反轉(zhuǎn)一個(gè)字符串?(解決方案)

97)Java 中,怎么獲取一個(gè)文件中單詞出現(xiàn)的最高頻率?(解決方案)

98)如何檢查出兩個(gè)給定的字符串是反序的?(解決方案)

99)Java 中,怎么打印出一個(gè)字符串的所有排列?(解決方案)

100)Java 中,怎樣才能打印出數(shù)組中的重復(fù)元素?(解決方案)

101)Java 中如何將字符串轉(zhuǎn)換為整數(shù)?(解決方案)

102)在沒(méi)有使用臨時(shí)變量的情況如何交換兩個(gè)整數(shù)變量的值?(解決方案)

交換兩個(gè)值,不用臨時(shí)變量?我們通過(guò)位運(yùn)算中的異或來(lái)實(shí)現(xiàn)。 //測(cè)試代碼為C語(yǔ)言代碼

前置知識(shí):

1.一個(gè)整數(shù)自己跟自己異或,結(jié)果為0   //因?yàn)楫惢虻姆▌t為,相同為0,不同為1,注意這里所說(shuō)的都是二進(jìn)制位。

2.任意一個(gè)整數(shù)跟0異或,結(jié)果為本身。//因?yàn)?異或0得1,0異或0,得0,所以1還是1,0還是0,沒(méi)發(fā)生變化。

------------------------------------------------------------------------------------------------------

測(cè)試代碼如下:

int main(int argc, char* argv[]) 
{    
     int a=575,b=667;   
     a=a^b;   
     b=a^b;    
     a=a^b;   
     printf("a=%d b=%d \n",a,b);  
     getchar();    
     return 0;
 }

//效果截圖如下:

分析下a和b發(fā)生交換的原因:

根據(jù)以上代碼不難得出以下表達(dá)式:

1. b=(a^b)^b=a^b^b=a2. a=(a^b)^[(a^b)^b]=a^b^[a^b^b]=a^b^a^b^b=a^a^b^b^b=b

根據(jù)前面說(shuō)的前置知識(shí),不難明白a和b,為什么發(fā)生交換了。

關(guān)于 OOP 和設(shè)計(jì)模式的面試題

這部分包含 Java 面試過(guò)程中關(guān)于 SOLID 的設(shè)計(jì)原則,OOP 基礎(chǔ),如類,對(duì)象,接口,繼承,多態(tài),封裝,抽象以及更高級(jí)的一些概念,如組合、聚合及關(guān)聯(lián)。也包含了 GOF 設(shè)計(jì)模式的問(wèn)題。

103)接口是什么?為什么要使用接口而不是直接使用具體類?

接口用于定義 API。它定義了類必須得遵循的規(guī)則。同時(shí),它提供了一種抽象,因?yàn)榭蛻舳酥皇褂媒涌?,這樣可以有多重實(shí)現(xiàn),如 List 接口,你可以使用可隨機(jī)訪問(wèn)的 ArrayList,也可以使用方便插入和刪除的 LinkedList。接口中不允許寫代碼,以此來(lái)保證抽象,但是 Java 8 中你可以在接口聲明靜態(tài)的默認(rèn)方法,這種方法是具體的。

104)Java 中,抽象類與接口之間有什么不同?(答案)

Java 中,抽象類和接口有很多不同之處,但是最重要的一個(gè)是 Java 中限制一個(gè)類只能繼承一個(gè)類,但是可以實(shí)現(xiàn)多個(gè)接口。抽象類可以很好的定義一個(gè)家族類的默認(rèn)行為,而接口能更好的定義類型,有助于后面實(shí)現(xiàn)多態(tài)機(jī)制。關(guān)于這個(gè)問(wèn)題的討論請(qǐng)查看答案。

105)除了單例模式,你在生產(chǎn)環(huán)境中還用過(guò)什么設(shè)計(jì)模式?

這需要根據(jù)你的經(jīng)驗(yàn)來(lái)回答。一般情況下,你可以說(shuō)依賴注入,工廠模式,裝飾模式或者觀察者模式,隨意選擇你使用過(guò)的一種即可。不過(guò)你要準(zhǔn)備回答接下的基于你選擇的模式的問(wèn)題。

106)你能解釋一下里氏替換原則嗎?(答案)

107) 什么情況下會(huì)違反迪米特法則?為什么會(huì)有這個(gè)問(wèn)題?(答案)

迪米特法則建議“只和朋友說(shuō)話,不要陌生人說(shuō)話”,以此來(lái)減少類之間的耦合。

108)適配器模式是什么?什么時(shí)候使用?

適配器模式提供對(duì)接口的轉(zhuǎn)換。如果你的客戶端使用某些接口,但是你有另外一些接口,你就可以寫一個(gè)適配去來(lái)連接這些接口。

109)什么是“依賴注入”和“控制反轉(zhuǎn)”?為什么有人使用?(答案)

110)抽象類是什么?它與接口有什么區(qū)別?你為什么要使用過(guò)抽象類?(答案)

抽象方法:由abstract修飾的方法為抽象方法,抽象方法只有方法的定義,沒(méi)有方法的實(shí)現(xiàn)。
抽象類:一個(gè)類中如果包含抽象方法,個(gè)i類應(yīng)該用abstract關(guān)鍵字聲明為抽象類。
抽象類不可以實(shí)例化,即使一個(gè)類中沒(méi)有抽象方法,也可以將其定義為抽象類,同樣,該類不可以實(shí)例化。
抽象類的意義:
 

1,為子類提供一個(gè)公共的類型;

2,封裝子類中重復(fù)內(nèi)容(成員變量和方法);

3,定義有抽象方法,子類雖然有不同的實(shí)現(xiàn),但該方法的定義是一致的。

抽象類和接口的區(qū)別:
抽象類:為了被子類繼承,為子類提供了同一的方法入口;
接口:定義了一個(gè)標(biāo)準(zhǔn)(特殊的抽象類)。

111)構(gòu)造器注入和 setter 依賴注入,那種方式更好?(答案)

每種方式都有它的缺點(diǎn)和優(yōu)點(diǎn)。構(gòu)造器注入保證所有的注入都被初始化,但是 setter 注入提供更好的靈活性來(lái)設(shè)置可選依賴。如果使用 XML 來(lái)描述依賴,Setter 注入的可讀寫會(huì)更強(qiáng)。經(jīng)驗(yàn)法則是強(qiáng)制依賴使用構(gòu)造器注入,可選依賴使用 setter 注入。

112)依賴注入和工廠模式之間有什么不同?(答案)

雖然兩種模式都是將對(duì)象的創(chuàng)建從應(yīng)用的邏輯中分離,但是依賴注入比工程模式更清晰。通過(guò)依賴注入,你的類就是 POJO,它只知道依賴而不關(guān)心它們?cè)趺传@取。使用工廠模式,你的類需要通過(guò)工廠來(lái)獲取依賴。因此,使用 DI 會(huì)比使用工廠模式更容易測(cè)試。關(guān)于這個(gè)話題的更詳細(xì)討論請(qǐng)參見(jiàn)答案。

113)適配器模式和裝飾器模式有什么區(qū)別?(答案)

雖然適配器模式和裝飾器模式的結(jié)構(gòu)類似,但是每種模式的出現(xiàn)意圖不同。適配器模式被用于橋接兩個(gè)接口,而裝飾模式的目的是在不修改類的情況下給類增加新的功能。

114)適配器模式和代理模式之前有什么不同?(答案)

這個(gè)問(wèn)題與前面的類似,適配器模式和代理模式的區(qū)別在于他們的意圖不同。由于適配器模式和代理模式都是封裝真正執(zhí)行動(dòng)作的類,因此結(jié)構(gòu)是一致的,但是適配器模式用于接口之間的轉(zhuǎn)換,而代理模式則是增加一個(gè)額外的中間層,以便支持分配、控制或智能訪問(wèn)。

115)什么是模板方法模式?(答案)

模板方法提供算法的框架,你可以自己去配置或定義步驟。例如,你可以將排序算法看做是一個(gè)模板。它定義了排序的步驟,但是具體的比較,可以使用 Comparable 或者其語(yǔ)言中類似東西,具體策略由你去配置。列出算法概要的方法就是眾所周知的模板方法。

116)什么時(shí)候使用訪問(wèn)者模式?(答案)

訪問(wèn)者模式用于解決在類的繼承層次上增加操作,但是不直接與之關(guān)聯(lián)。這種模式采用雙派發(fā)的形式來(lái)增加中間層。

117)什么時(shí)候使用組合模式?(答案)

組合模式使用樹結(jié)構(gòu)來(lái)展示部分與整體繼承關(guān)系。它允許客戶端采用統(tǒng)一的形式來(lái)對(duì)待單個(gè)對(duì)象和對(duì)象容器。當(dāng)你想要展示對(duì)象這種部分與整體的繼承關(guān)系時(shí)采用組合模式。

118)繼承和組合之間有什么不同?(答案)

雖然兩種都可以實(shí)現(xiàn)代碼復(fù)用,但是組合比繼承共靈活,因?yàn)榻M合允許你在運(yùn)行時(shí)選擇不同的實(shí)現(xiàn)。用組合實(shí)現(xiàn)的代碼也比繼承測(cè)試起來(lái)更加簡(jiǎn)單。

119)描述 Java 中的重載和重寫?(答案)

重載和重寫都允許你用相同的名稱來(lái)實(shí)現(xiàn)不同的功能,但是重載是編譯時(shí)活動(dòng),而重寫是運(yùn)行時(shí)活動(dòng)。你可以在同一個(gè)類中重載方法,但是只能在子類中重寫方法。重寫必須要有繼承。

120)Java 中,嵌套公共靜態(tài)類與頂級(jí)類有什么不同?(答案)

類的內(nèi)部可以有多個(gè)嵌套公共靜態(tài)類,但是一個(gè) Java 源文件只能有一個(gè)頂級(jí)公共類,并且頂級(jí)公共類的名稱與源文件名稱必須一致。

121) OOP 中的 組合、聚合和關(guān)聯(lián)有什么區(qū)別?(答案)

如果兩個(gè)對(duì)象彼此有關(guān)系,就說(shuō)他們是彼此相關(guān)聯(lián)的。組合和聚合是面向?qū)ο笾械膬煞N形式的關(guān)聯(lián)。組合是一種比聚合更強(qiáng)力的關(guān)聯(lián)。組合中,一個(gè)對(duì)象是另一個(gè)的擁有者,而聚合則是指一個(gè)對(duì)象使用另一個(gè)對(duì)象。如果對(duì)象 A 是由對(duì)象 B 組合的,則 A 不存在的話,B一定不存在,但是如果 A 對(duì)象聚合了一個(gè)對(duì)象 B,則即使 A 不存在了,B 也可以單獨(dú)存在。

122)給我一個(gè)符合開(kāi)閉原則的設(shè)計(jì)模式的例子?(答案)

開(kāi)閉原則要求你的代碼對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。這個(gè)意思就是說(shuō),如果你想增加一個(gè)新的功能,你可以很容易的在不改變已測(cè)試過(guò)的代碼的前提下增加新的代碼。有好幾個(gè)設(shè)計(jì)模式是基于開(kāi)閉原則的,如策略模式,如果你需要一個(gè)新的策略,只需要實(shí)現(xiàn)接口,增加配置,不需要改變核心邏輯。一個(gè)正在工作的例子是 Collections.sort() 方法,這就是基于策略模式,遵循開(kāi)閉原則的,你不需為新的對(duì)象修改 sort() 方法,你需要做的僅僅是實(shí)現(xiàn)你自己的 Comparator 接口。

123)抽象工廠模式和原型模式之間的區(qū)別?(答案)

124)什么時(shí)候使用享元模式?(答案)

享元模式通過(guò)共享對(duì)象來(lái)避免創(chuàng)建太多的對(duì)象。為了使用享元模式,你需要確保你的對(duì)象是不可變的,這樣你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。

Java 面試中其他各式各樣的問(wèn)題

這部分包含 Java 中關(guān)于 XML 的面試題,JDBC 面試題,正則表達(dá)式面試題,Java 錯(cuò)誤和異常及序列化面試題

125)嵌套靜態(tài)類與頂級(jí)類有什么區(qū)別?(答案)

一個(gè)公共的頂級(jí)類的源文件名稱與類名相同,而嵌套靜態(tài)類沒(méi)有這個(gè)要求。一個(gè)嵌套類位于頂級(jí)類內(nèi)部,需要使用頂級(jí)類的名稱來(lái)引用嵌套靜態(tài)類,如 HashMap.Entry 是一個(gè)嵌套靜態(tài)類,HashMap 是一個(gè)頂級(jí)類,Entry是一個(gè)嵌套靜態(tài)類。

126)你能寫出一個(gè)正則表達(dá)式來(lái)判斷一個(gè)字符串是否是一個(gè)數(shù)字嗎?(解決方案)

一個(gè)數(shù)字字符串,只能包含數(shù)字,如 0 到 9 以及 +、- 開(kāi)頭,通過(guò)這個(gè)信息,你可以下一個(gè)如下的正則表達(dá)式來(lái)判斷給定的字符串是不是數(shù)字。

127)Java 中,受檢查異常 和 不受檢查異常的區(qū)別?(答案)

受檢查異常編譯器在編譯期間檢查。對(duì)于這種異常,方法強(qiáng)制處理或者通過(guò) throws 子句聲明。其中一種情況是 Exception 的子類但不是 RuntimeException 的子類。非受檢查是 RuntimeException 的子類,在編譯階段不受編譯器的檢查。

128)Java 中,throw 和 throws 有什么區(qū)別?(答案)

throw 用于拋出 java.lang.Throwable 類的一個(gè)實(shí)例化對(duì)象,意思是說(shuō)你可以通過(guò)關(guān)鍵字 throw 拋出一個(gè) Error 或者 一個(gè)Exception,如:throw new IllegalArgumentException(“size must be multiple of 2″) 而throws 的作用是作為方法聲明和簽名的一部分,方法被拋出相應(yīng)的異常以便調(diào)用者能處理。Java 中,任何未處理的受檢查異常強(qiáng)制在 throws 子句中聲明。

129)Java 中,Serializable 與 Externalizable 的區(qū)別?(答案)

Serializable 接口是一個(gè)序列化 Java 類的接口,以便于它們可以在網(wǎng)絡(luò)上傳輸或者可以將它們的狀態(tài)保存在磁盤上,是 JVM 內(nèi)嵌的默認(rèn)序列化方式,成本高、脆弱而且不安全。Externalizable 允許你控制整個(gè)序列化過(guò)程,指定特定的二進(jìn)制格式,增加安全機(jī)制。

130)Java 中,DOM 和 SAX 解析器有什么不同?(答案)

DOM 解析器將整個(gè) XML 文檔加載到內(nèi)存來(lái)創(chuàng)建一棵 DOM 模型樹,這樣可以更快的查找節(jié)點(diǎn)和修改 XML 結(jié)構(gòu),而 SAX 解析器是一個(gè)基于事件的解析器,不會(huì)將整個(gè) XML 文檔加載到內(nèi)存。由于這個(gè)原因,DOM 比 SAX 更快,也要求更多的內(nèi)存,不適合于解析大 XML 文件。

131)說(shuō)出 JDK 1.7 中的三個(gè)新特性?(答案)

雖然 JDK 1.7 不像 JDK 5 和 8 一樣的大版本,但是,還是有很多新的特性,如 try-with-resource 語(yǔ)句,這樣你在使用流或者資源的時(shí)候,就不需要手動(dòng)關(guān)閉,Java 會(huì)自動(dòng)關(guān)閉。Fork-Join 池某種程度上實(shí)現(xiàn) Java 版的 Map-reduce。允許 Switch 中有 String 變量和文本。菱形操作符(<>)用于類型推斷,不再需要在變量聲明的右邊申明泛型,因此可以寫出可讀寫更強(qiáng)、更簡(jiǎn)潔的代碼。另一個(gè)值得一提的特性是改善異常處理,如允許在同一個(gè) catch 塊中捕獲多個(gè)異常。

132)說(shuō)出 5 個(gè) JDK 1.8 引入的新特性?(答案)

Java 8 在 Java 歷史上是一個(gè)開(kāi)創(chuàng)新的版本,下面 JDK 8 中 5 個(gè)主要的特性:Lambda 表達(dá)式,允許像對(duì)象一樣傳遞匿名函數(shù) Stream API,充分利用現(xiàn)代多核 CPU,可以寫出很簡(jiǎn)潔的代碼 Date 與 Time API,最終,有一個(gè)穩(wěn)定、簡(jiǎn)單的日期和時(shí)間庫(kù)可供你使用 擴(kuò)展方法,現(xiàn)在,接口中可以有靜態(tài)、默認(rèn)方法。重復(fù)注解,現(xiàn)在你可以將相同的注解在同一類型上使用多次。

133)Java 中,Maven 和 ANT 有什么區(qū)別?(答案)

雖然兩者都是構(gòu)建工具,都用于創(chuàng)建 Java 應(yīng)用,但是 Maven 做的事情更多,在基于“約定優(yōu)于配置”的概念下,提供標(biāo)準(zhǔn)的Java 項(xiàng)目結(jié)構(gòu),同時(shí)能為應(yīng)用自動(dòng)管理依賴(應(yīng)用中所依賴的 JAR 文件),Maven 與 ANT 工具更多的不同之處請(qǐng)參見(jiàn)答案。

這就是所有的面試題,如此之多,是不是?我可以保證,如果你能回答列表中的所有問(wèn)題,你就可以很輕松的應(yīng)付任何核心 Java 或者高級(jí) Java 面試。雖然,這里沒(méi)有涵蓋 Servlet、JSP、JSF、JPA,JMS,EJB 及其它 Java EE 技術(shù),也沒(méi)有包含主流的框架如 Spring MVC,Struts 2.0,Hibernate,也沒(méi)有包含 SOAP 和 RESTful web service,但是這份列表對(duì)做 Java 開(kāi)發(fā)的、準(zhǔn)備應(yīng)聘 Java web 開(kāi)發(fā)職位的人還是同樣有用的,因?yàn)樗械?Java 面試,開(kāi)始的問(wèn)題都是 Java 基礎(chǔ)和 JDK API 相關(guān)的。如果你認(rèn)為我這里有任何應(yīng)該在這份列表中而被我遺漏了的 Java 流行的問(wèn)題,你可以自由的給我建議。我的目的是從最近的面試中創(chuàng)建一份最新的、最優(yōu)的 Java 面試問(wèn)題列表。

50道Java線程面試題分析及答案

下面是Java線程相關(guān)的熱門面試題,你可以用它來(lái)好好準(zhǔn)備面試。

**1) 什么是線程?
**線程是操作系統(tǒng)能夠進(jìn)行運(yùn)算調(diào)度的最小單位,它被包含在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位。程序員可以通過(guò)它進(jìn)行多處理器編程,你可以使用多線程對(duì)運(yùn)算密集型任務(wù)提速。比如,如果一個(gè)線程完成一個(gè)任務(wù)要100毫秒,那么用十個(gè)線程完成改任務(wù)只需10毫秒。Java在語(yǔ)言層面對(duì)多線程提供了卓越的支持,它也是一個(gè)很好的賣點(diǎn)。

**2) 線程和進(jìn)程有什么區(qū)別?
**線程是進(jìn)程的子集,一個(gè)進(jìn)程可以有很多線程,每條線程并行執(zhí)行不同的任務(wù)。不同的進(jìn)程使用不同的內(nèi)存空間,而所有的線程共享一片相同的內(nèi)存空間。別把它和棧內(nèi)存搞混,每個(gè)線程都擁有單獨(dú)的棧內(nèi)存用來(lái)存儲(chǔ)本地?cái)?shù)據(jù)。

**3) 如何在Java中實(shí)現(xiàn)線程?
**在語(yǔ)言層面有兩種方式。java.lang.Thread 類的實(shí)例就是一個(gè)線程但是它需要調(diào)用java.lang.Runnable接口來(lái)執(zhí)行,由于線程類本身就是調(diào)用的Runnable接口所以你可以繼承java.lang.Thread 類或者直接調(diào)用Runnable接口來(lái)重寫run()方法實(shí)現(xiàn)線程。

**4) 用Runnable還是Thread?
**這個(gè)問(wèn)題是上題的后續(xù),大家都知道我們可以通過(guò)繼承Thread類或者調(diào)用Runnable接口來(lái)實(shí)現(xiàn)線程,問(wèn)題是,那個(gè)方法更好呢?什么情況下使用它?這個(gè)問(wèn)題很容易回答,如果你知道Java不支持類的多重繼承,但允許你調(diào)用多個(gè)接口。所以如果你要繼承其他類,當(dāng)然是調(diào)用Runnable接口好了。

**5) Thread 類中的start() 和 run() 方法有什么區(qū)別?
**這個(gè)問(wèn)題經(jīng)常被問(wèn)到,但還是能從此區(qū)分出面試者對(duì)Java線程模型的理解程度。start()方法被用來(lái)啟動(dòng)新創(chuàng)建的線程,而且start()內(nèi)部調(diào)用了run()方法,這和直接調(diào)用run()方法的效果不一樣。當(dāng)你調(diào)用run()方法的時(shí)候,只會(huì)是在原來(lái)的線程中調(diào)用,沒(méi)有新的線程啟動(dòng),start()方法才會(huì)啟動(dòng)新線程。

**6) Java中Runnable和Callable有什么不同?
**Runnable和Callable都代表那些要在不同的線程中執(zhí)行的任務(wù)。Runnable從JDK1.0開(kāi)始就有了,Callable是在JDK1.5增加的。它們的主要區(qū)別是Callable的 call() 方法可以返回值和拋出異常,而Runnable的run()方法沒(méi)有這些功能。Callable可以返回裝載有計(jì)算結(jié)果的Future對(duì)象。

**7)如何強(qiáng)制啟動(dòng)一個(gè)線程?
**這個(gè)問(wèn)題就像是如何強(qiáng)制進(jìn)行Java垃圾回收,目前還沒(méi)有覺(jué)得方法,雖然你可以使用System.gc()來(lái)進(jìn)行垃圾回收,但是不保證能成功。在Java里面沒(méi)有辦法強(qiáng)制啟動(dòng)一個(gè)線程,它是被線程調(diào)度器控制著且Java沒(méi)有公布相關(guān)的API。

**8) Java中CyclicBarrier 和 CountDownLatch有什么不同?
**CyclicBarrier 和 CountDownLatch 都可以用來(lái)讓一組線程等待其它線程。與 CyclicBarrier 不同的是,CountdownLatch 不能重新使用。

**9) Java內(nèi)存模型是什么?
**Java內(nèi)存模型規(guī)定和指引Java程序在不同的內(nèi)存架構(gòu)、CPU和操作系統(tǒng)間有確定性地行為。它在多線程的情況下尤其重要。Java內(nèi)存模型對(duì)一個(gè)線程所做的變動(dòng)能被其它線程可見(jiàn)提供了保證,它們之間是先行發(fā)生關(guān)系。這個(gè)關(guān)系定義了一些規(guī)則讓程序員在并發(fā)編程時(shí)思路更清晰。比如,先行發(fā)生關(guān)系確保了:
線程內(nèi)的代碼能夠按先后順序執(zhí)行,這被稱為程序次序規(guī)則。
對(duì)于同一個(gè)鎖,一個(gè)解鎖操作一定要發(fā)生在時(shí)間上后發(fā)生的另一個(gè)鎖定操作之前,也叫做管程鎖定規(guī)則。
前一個(gè)對(duì)volatile的寫操作在后一個(gè)volatile的讀操作之前,也叫volatile變量規(guī)則。
一個(gè)線程內(nèi)的任何操作必需在這個(gè)線程的start()調(diào)用之后,也叫作線程啟動(dòng)規(guī)則。
一個(gè)線程的所有操作都會(huì)在線程終止之前,線程終止規(guī)則。
一個(gè)對(duì)象的終結(jié)操作必需在這個(gè)對(duì)象構(gòu)造完成之后,也叫對(duì)象終結(jié)規(guī)則。
可傳遞性

10) Java中的volatile 變量是什么?
volatile是一個(gè)特殊的修飾符,只有成員變量才能使用它。在Java并發(fā)程序缺少同步類的情況下,多線程對(duì)成員變量的操作對(duì)其它線程是透明的。volatile變量可以保證下一個(gè)讀取操作會(huì)在前一個(gè)寫操作之后發(fā)生,就是上一題的volatile變量規(guī)則。點(diǎn)擊這里查看更多volatile的相關(guān)內(nèi)容。

**11) 什么是線程安全?Vector是一個(gè)線程安全類嗎? 
**如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。一個(gè)線程安全的計(jì)數(shù)器類的同一個(gè)實(shí)例對(duì)象在被多個(gè)線程使用的情況下也不會(huì)出現(xiàn)計(jì)算失誤。很顯然你可以將集合類分成兩組,線程安全和非線程安全的。Vector 是用同步方法來(lái)實(shí)現(xiàn)線程安全的, 而和它相似的ArrayList不是線程安全的。

**12) Java中什么是競(jìng)態(tài)條件?舉個(gè)例子說(shuō)明。
**競(jìng)態(tài)條件會(huì)導(dǎo)致程序在并發(fā)情況下出現(xiàn)一些bugs。多線程對(duì)一些資源的競(jìng)爭(zhēng)的時(shí)候就會(huì)產(chǎn)生競(jìng)態(tài)條件,如果首先要執(zhí)行的程序競(jìng)爭(zhēng)失敗排到后面執(zhí)行了,那么整個(gè)程序就會(huì)出現(xiàn)一些不確定的bugs。這種bugs很難發(fā)現(xiàn)而且會(huì)重復(fù)出現(xiàn),因?yàn)榫€程間的隨機(jī)競(jìng)爭(zhēng)。

**13) Java中如何停止一個(gè)線程?
**Java提供了很豐富的API但沒(méi)有為停止線程提供API。JDK 1.0本來(lái)有一些像stop(), suspend() 和 resume()的控制方法但是由于潛在的死鎖威脅因此在后續(xù)的JDK版本中他們被棄用了,之后Java API的設(shè)計(jì)者就沒(méi)有提供一個(gè)兼容且線程安全的方法來(lái)停止一個(gè)線程。當(dāng)run() 或者 call() 方法執(zhí)行完的時(shí)候線程會(huì)自動(dòng)結(jié)束,如果要手動(dòng)結(jié)束一個(gè)線程,你可以用volatile 布爾變量來(lái)退出run()方法的循環(huán)或者是取消任務(wù)來(lái)中斷線程。

**14) 一個(gè)線程運(yùn)行時(shí)發(fā)生異常會(huì)怎樣?
**這是我在一次面試中遇到的一個(gè)很刁鉆的Java面試題, 簡(jiǎn)單的說(shuō),如果異常沒(méi)有被捕獲該線程將會(huì)停止執(zhí)行。Thread.UncaughtExceptionHandler是用于處理未捕獲異常造成線程突然中斷情況的一個(gè)內(nèi)嵌接口。當(dāng)一個(gè)未捕獲異常將造成線程中斷的時(shí)候JVM會(huì)使用Thread.getUncaughtExceptionHandler()來(lái)查詢線程的UncaughtExceptionHandler并將線程和異常作為參數(shù)傳遞給handler的uncaughtException()方法進(jìn)行處理。

**15) 如何在兩個(gè)線程間共享數(shù)據(jù)?
**你可以通過(guò)共享對(duì)象來(lái)實(shí)現(xiàn)這個(gè)目的,或者是使用像阻塞隊(duì)列這樣并發(fā)的數(shù)據(jù)結(jié)構(gòu)。這篇教程《Java線程間通信》(涉及到在兩個(gè)線程間共享對(duì)象)用wait和notify方法實(shí)現(xiàn)了生產(chǎn)者消費(fèi)者模型。

**16) Java中notify 和 notifyAll有什么區(qū)別?
**這又是一個(gè)刁鉆的問(wèn)題,因?yàn)槎嗑€程可以等待單監(jiān)控鎖,Java API 的設(shè)計(jì)人員提供了一些方法當(dāng)?shù)却龡l件改變的時(shí)候通知它們,但是這些方法沒(méi)有完全實(shí)現(xiàn)。notify()方法不能喚醒某個(gè)具體的線程,所以只有一個(gè)線程在等待的時(shí)候它才有用武之地。而notifyAll()喚醒所有線程并允許他們爭(zhēng)奪鎖確保了至少有一個(gè)線程能繼續(xù)運(yùn)行。

**17) 為什么wait, notify 和 notifyAll這些方法不在thread類里面?
**這是個(gè)設(shè)計(jì)相關(guān)的問(wèn)題,它考察的是面試者對(duì)現(xiàn)有系統(tǒng)和一些普遍存在但看起來(lái)不合理的事物的看法。回答這些問(wèn)題的時(shí)候,你要說(shuō)明為什么把這些方法放在Object類里是有意義的,還有不把它放在Thread類里的原因。一個(gè)很明顯的原因是JAVA提供的鎖是對(duì)象級(jí)的而不是線程級(jí)的,每個(gè)對(duì)象都有鎖,通過(guò)線程獲得。如果線程需要等待某些鎖那么調(diào)用對(duì)象中的wait()方法就有意義了。如果wait()方法定義在Thread類中,線程正在等待的是哪個(gè)鎖就不明顯了。簡(jiǎn)單的說(shuō),由于wait,notify和notifyAll都是鎖級(jí)別的操作,所以把他們定義在Object類中因?yàn)殒i屬于對(duì)象你也可以查看這篇文章了解更多。

18) 什么是ThreadLocal變量?
ThreadLocal是Java里一種特殊的變量。每個(gè)線程都有一個(gè)ThreadLocal就是每個(gè)線程都擁有了自己獨(dú)立的一個(gè)變量,競(jìng)爭(zhēng)條件被徹底消除了。它是為創(chuàng)建代價(jià)高昂的對(duì)象獲取線程安全的好方法,比如你可以用ThreadLocal讓SimpleDateFormat變成線程安全的,因?yàn)槟莻€(gè)類創(chuàng)建代價(jià)高昂且每次調(diào)用都需要?jiǎng)?chuàng)建不同的實(shí)例所以不值得在局部范圍使用它,如果為每個(gè)線程提供一個(gè)自己獨(dú)有的變量拷貝,將大大提高效率。首先,通過(guò)復(fù)用減少了代價(jià)高昂的對(duì)象的創(chuàng)建個(gè)數(shù)。其次,你在沒(méi)有使用高代價(jià)的同步或者不變性的情況下獲得了線程安全。線程局部變量的另一個(gè)不錯(cuò)的例子是ThreadLocalRandom類,它在多線程環(huán)境中減少了創(chuàng)建代價(jià)高昂的Random對(duì)象的個(gè)數(shù)。

**19) 什么是FutureTask?
**在Java并發(fā)程序中FutureTask表示一個(gè)可以取消的異步運(yùn)算。它有啟動(dòng)和取消運(yùn)算、查詢運(yùn)算是否完成和取回運(yùn)算結(jié)果等方法。只有當(dāng)運(yùn)算完成的時(shí)候結(jié)果才能取回,如果運(yùn)算尚未完成get方法將會(huì)阻塞。一個(gè)FutureTask對(duì)象可以對(duì)調(diào)用了Callable和Runnable的對(duì)象進(jìn)行包裝,由于FutureTask也是調(diào)用了Runnable接口所以它可以提交給Executor來(lái)執(zhí)行。

**20) Java中interrupted 和 isInterruptedd方法的區(qū)別?
**interrupted() 和 isInterrupted()的主要區(qū)別是前者會(huì)將中斷狀態(tài)清除而后者不會(huì)。Java多線程的中斷機(jī)制是用內(nèi)部標(biāo)識(shí)來(lái)實(shí)現(xiàn)的,調(diào)用Thread.interrupt()來(lái)中斷一個(gè)線程就會(huì)設(shè)置中斷標(biāo)識(shí)為true。當(dāng)中斷線程調(diào)用靜態(tài)方法Thread.interrupted()來(lái)檢查中斷狀態(tài)時(shí),中斷狀態(tài)會(huì)被清零。而非靜態(tài)方法isInterrupted()用來(lái)查詢其它線程的中斷狀態(tài)且不會(huì)改變中斷狀態(tài)標(biāo)識(shí)。簡(jiǎn)單的說(shuō)就是任何拋出InterruptedException異常的方法都會(huì)將中斷狀態(tài)清零。無(wú)論如何,一個(gè)線程的中斷狀態(tài)有有可能被其它線程調(diào)用中斷來(lái)改變。

**21) 為什么wait和notify方法要在同步塊中調(diào)用?
**主要是因?yàn)镴ava API強(qiáng)制要求這樣做,如果你不這么做,你的代碼會(huì)拋出IllegalMonitorStateException異常。還有一個(gè)原因是為了避免wait和notify之間產(chǎn)生競(jìng)態(tài)條件。

**22) 為什么你應(yīng)該在循環(huán)中檢查等待條件?
**處于等待狀態(tài)的線程可能會(huì)收到錯(cuò)誤警報(bào)和偽喚醒,如果不在循環(huán)中檢查等待條件,程序就會(huì)在沒(méi)有滿足結(jié)束條件的情況下退出。因此,當(dāng)一個(gè)等待線程醒來(lái)時(shí),不能認(rèn)為它原來(lái)的等待狀態(tài)仍然是有效的,在notify()方法調(diào)用之后和等待線程醒來(lái)之前這段時(shí)間它可能會(huì)改變。這就是在循環(huán)中使用wait()方法效果更好的原因,你可以在Eclipse中創(chuàng)建模板調(diào)用wait和notify試一試。如果你想了解更多關(guān)于這個(gè)問(wèn)題的內(nèi)容,我推薦你閱讀《Effective Java》這本書中的線程和同步章節(jié)。

**23) Java中的同步集合與并發(fā)集合有什么區(qū)別?
**同步集合與并發(fā)集合都為多線程和并發(fā)提供了合適的線程安全的集合,不過(guò)并發(fā)集合的可擴(kuò)展性更高。在Java1.5之前程序員們只有同步集合來(lái)用且在多線程并發(fā)的時(shí)候會(huì)導(dǎo)致?tīng)?zhēng)用,阻礙了系統(tǒng)的擴(kuò)展性。Java5介紹了并發(fā)集合像ConcurrentHashMap,不僅提供線程安全還用鎖分離和內(nèi)部分區(qū)等現(xiàn)代技術(shù)提高了可擴(kuò)展性。

**24) Java中堆和棧有什么不同?
**為什么把這個(gè)問(wèn)題歸類在多線程和并發(fā)面試題里?因?yàn)闂J且粔K和線程緊密相關(guān)的內(nèi)存區(qū)域。每個(gè)線程都有自己的棧內(nèi)存,用于存儲(chǔ)本地變量,方法參數(shù)和棧調(diào)用,一個(gè)線程中存儲(chǔ)的變量對(duì)其它線程是不可見(jiàn)的。而堆是所有線程共享的一片公用內(nèi)存區(qū)域。對(duì)象都在堆里創(chuàng)建,為了提升效率線程會(huì)從堆中弄一個(gè)緩存到自己的棧,如果多個(gè)線程使用該變量就可能引發(fā)問(wèn)題,這時(shí)volatile 變量就可以發(fā)揮作用了,它要求線程從主存中讀取變量的值。

25) 什么是線程池?為什么要使用它?
創(chuàng)建線程要花費(fèi)昂貴的資源和時(shí)間,如果任務(wù)來(lái)了才創(chuàng)建線程那么響應(yīng)時(shí)間會(huì)變長(zhǎng),而且一個(gè)進(jìn)程能創(chuàng)建的線程數(shù)有限。為了避免這些問(wèn)題,在程序啟動(dòng)的時(shí)候就創(chuàng)建若干線程來(lái)響應(yīng)處理,它們被稱為線程池,里面的線程叫工作線程。從JDK1.5開(kāi)始,Java API提供了Executor框架讓你可以創(chuàng)建不同的線程池。比如單線程池,每次處理一個(gè)任務(wù);數(shù)目固定的線程池或者是緩存線程池(一個(gè)適合很多生存期短的任務(wù)的程序的可擴(kuò)展線程池)。

**26) 如何寫代碼來(lái)解決生產(chǎn)者消費(fèi)者問(wèn)題?
**在現(xiàn)實(shí)中你解決的許多線程問(wèn)題都屬于生產(chǎn)者消費(fèi)者模型,就是一個(gè)線程生產(chǎn)任務(wù)供其它線程進(jìn)行消費(fèi),你必須知道怎么進(jìn)行線程間通信來(lái)解決這個(gè)問(wèn)題。比較低級(jí)的辦法是用wait和notify來(lái)解決這個(gè)問(wèn)題,比較贊的辦法是用Semaphore 或者 BlockingQueue來(lái)實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模型,這篇教程有實(shí)現(xiàn)它。

**27) 如何避免死鎖?
**死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。這是一個(gè)嚴(yán)重的問(wèn)題,因?yàn)樗梨i會(huì)讓你的程序掛起無(wú)法完成任務(wù),死鎖的發(fā)生必須滿足以下四個(gè)條件:
互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用。
請(qǐng)求與保持條件:一個(gè)進(jìn)程因請(qǐng)求資源而阻塞時(shí),對(duì)已獲得的資源保持不放。
不剝奪條件:進(jìn)程已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪。
循環(huán)等待條件:若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。
最簡(jiǎn)單的方法就是阻止循環(huán)等待條件,將系統(tǒng)中所有的資源設(shè)置標(biāo)志位、排序,規(guī)定所有的進(jìn)程申請(qǐng)資源必須以一定的順序(升序或降序)做操作來(lái)避免死鎖。這篇教程有代碼示例和避免死鎖的討論細(xì)節(jié)。

**28) Java中活鎖和死鎖有什么區(qū)別
**這是上題的擴(kuò)展,活鎖和死鎖類似,不同之處在于處于活鎖的線程或進(jìn)程的狀態(tài)是不斷改變的,活鎖可以認(rèn)為是一種特殊的饑餓。一個(gè)現(xiàn)實(shí)的活鎖例子是兩個(gè)人在狹小的走廊碰到,兩個(gè)人都試著避讓對(duì)方好讓彼此通過(guò),但是因?yàn)楸茏尩姆较蚨家粯訉?dǎo)致最后誰(shuí)都不能通過(guò)走廊。簡(jiǎn)單的說(shuō)就是,活鎖和死鎖的主要區(qū)別是前者進(jìn)程的狀態(tài)可以改變但是卻不能繼續(xù)執(zhí)行。

**29) 怎么檢測(cè)一個(gè)線程是否擁有鎖?
**我一直不知道我們竟然可以檢測(cè)一個(gè)線程是否擁有鎖,直到我參加了一次電話面試。在java.lang.Thread中有一個(gè)方法叫holdsLock(),它返回true如果當(dāng)且僅當(dāng)當(dāng)前線程擁有某個(gè)具體對(duì)象的鎖。

**30) 你如何在Java中獲取線程堆棧?
**對(duì)于不同的操作系統(tǒng),有多種方法來(lái)獲得Java進(jìn)程的線程堆棧。當(dāng)你獲取線程堆棧時(shí),JVM會(huì)把所有線程的狀態(tài)存到日志文件或者輸出到控制臺(tái)。在Windows你可以使用Ctrl + Break組合鍵來(lái)獲取線程堆棧,Linux下用kill -3命令。你也可以用jstack這個(gè)工具來(lái)獲取,它對(duì)線程id進(jìn)行操作,你可以用jps這個(gè)工具找到id。

**31) JVM中哪個(gè)參數(shù)是用來(lái)控制線程的棧堆棧小的
**這個(gè)問(wèn)題很簡(jiǎn)單, -Xss參數(shù)用來(lái)控制線程的堆棧大小。你可以查看JVM配置列表來(lái)了解這個(gè)參數(shù)的更多信息。

**32) Java中synchronized 和 ReentrantLock 有什么不同?
**Java在過(guò)去很長(zhǎng)一段時(shí)間只能通過(guò)synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)互斥,它有一些缺點(diǎn)。比如你不能擴(kuò)展鎖之外的方法或者塊邊界,嘗試獲取鎖時(shí)不能中途取消等。Java 5 通過(guò)Lock接口提供了更復(fù)雜的控制來(lái)解決這些問(wèn)題。ReentrantLock 類實(shí)現(xiàn)了 Lock,它擁有與 synchronized 相同的并發(fā)性和內(nèi)存語(yǔ)義且它還具有可擴(kuò)展性。

33) 有三個(gè)線程T1,T2,T3,怎么確保它們按順序執(zhí)行?
在多線程中有多種方法讓線程按特定順序執(zhí)行,你可以用線程類的join()方法在一個(gè)線程中啟動(dòng)另一個(gè)線程,另外一個(gè)線程完成該線程繼續(xù)執(zhí)行。為了確保三個(gè)線程的順序你應(yīng)該先啟動(dòng)最后一個(gè)(T3調(diào)用T2,T2調(diào)用T1),這樣T1就會(huì)先完成而T3最后完成。

**34) Thread類中的yield方法有什么作用
**Yield方法可以暫停當(dāng)前正在執(zhí)行的線程對(duì)象,讓其它有相同優(yōu)先級(jí)的線程執(zhí)行。它是一個(gè)靜態(tài)方法而且只保證當(dāng)前線程放棄CPU占用而不能保證使其它線程一定能占用CPU,執(zhí)行yield()的線程有可能在進(jìn)入到暫停狀態(tài)后馬上又被執(zhí)行。

**35) Java中ConcurrentHashMap的并發(fā)度是什么?
**ConcurrentHashMap把實(shí)際map劃分成若干部分來(lái)實(shí)現(xiàn)它的可擴(kuò)展性和線程安全。這種劃分是使用并發(fā)度獲得的,它是ConcurrentHashMap類構(gòu)造函數(shù)的一個(gè)可選參數(shù),默認(rèn)值為16,這樣在多線程情況下就能避免爭(zhēng)用。

36) Java中Semaphore是什么?
Java中的Semaphore是一種新的同步類,它是一個(gè)計(jì)數(shù)信號(hào)。從概念上講,從概念上講,信號(hào)量維護(hù)了一個(gè)許可集合。如有必要,在許可可用前會(huì)阻塞每一個(gè) acquire(),然后再獲取該許可。每個(gè) release()添加一個(gè)許可,從而可能釋放一個(gè)正在阻塞的獲取者。但是,不使用實(shí)際的許可對(duì)象,Semaphore只對(duì)可用許可的號(hào)碼進(jìn)行計(jì)數(shù),并采取相應(yīng)的行動(dòng)。

**37)如果你提交任務(wù)時(shí),線程池隊(duì)列已滿。會(huì)時(shí)發(fā)會(huì)生什么?
**這個(gè)問(wèn)題問(wèn)得很狡猾,許多程序員會(huì)認(rèn)為該任務(wù)會(huì)阻塞直到線程池隊(duì)列有空位。事實(shí)上如果一個(gè)任務(wù)不能被調(diào)度執(zhí)行那么ThreadPoolExecutor’s submit()方法將會(huì)拋出一個(gè)RejectedExecutionException異常。

**38) Java線程池中submit() 和 execute()方法有什么區(qū)別?
**兩個(gè)方法都可以向線程池提交任務(wù),execute()方法的返回類型是void,它定義在Executor接口中, 而submit()方法可以返回持有計(jì)算結(jié)果的Future對(duì)象,它定義在ExecutorService接口中,它擴(kuò)展了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。

**39) 什么是阻塞式方法?
**阻塞式方法是指程序會(huì)一直等待該方法完成期間不做其他事情,ServerSocket的accept()方法就是一直等待客戶端連接。這里的阻塞是指調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起,直到得到結(jié)果之后才會(huì)返回。此外,還有異步和非阻塞式方法在任務(wù)完成前就返回。

**40) Swing是線程安全的嗎?為什么?
**你可以很肯定的給出回答,Swing不是線程安全的,但是你應(yīng)該解釋這么回答的原因即便面試官?zèng)]有問(wèn)你為什么。當(dāng)我們說(shuō)swing不是線程安全的常常提到它的組件,這些組件不能在多線程中進(jìn)行修改,所有對(duì)GUI組件的更新都要在AWT線程中完成,而Swing提供了同步和異步兩種回調(diào)方法來(lái)進(jìn)行更新

**41) Java中invokeAndWait 和 invokeLater有什么區(qū)別?
**這兩個(gè)方法是Swing API 提供給Java開(kāi)發(fā)者用來(lái)從當(dāng)前線程而不是事件派發(fā)線程更新GUI組件用的。InvokeAndWait()同步更新GUI組件,比如一個(gè)進(jìn)度條,一旦進(jìn)度更新了,進(jìn)度條也要做出相應(yīng)改變。如果進(jìn)度被多個(gè)線程跟蹤,那么就調(diào)用invokeAndWait()方法請(qǐng)求事件派發(fā)線程對(duì)組件進(jìn)行相應(yīng)更新。而invokeLater()方法是異步調(diào)用更新組件的。

**42) Swing API中那些方法是線程安全的?
**這個(gè)問(wèn)題又提到了swing和線程安全,雖然組件不是線程安全的但是有一些方法是可以被多線程安全調(diào)用的,比如repaint(), revalidate()。JTextComponent的setText()方法和JTextArea的insert() 和 append() 方法也是線程安全的。

**43) 如何在Java中創(chuàng)建Immutable對(duì)象?
**這個(gè)問(wèn)題看起來(lái)和多線程沒(méi)什么關(guān)系, 但不變性有助于簡(jiǎn)化已經(jīng)很復(fù)雜的并發(fā)程序。Immutable對(duì)象可以在沒(méi)有同步的情況下共享,降低了對(duì)該對(duì)象進(jìn)行并發(fā)訪問(wèn)時(shí)的同步化開(kāi)銷??墒荍ava沒(méi)有@Immutable這個(gè)注解符,要?jiǎng)?chuàng)建不可變類,要實(shí)現(xiàn)下面幾個(gè)步驟:通過(guò)構(gòu)造方法初始化所有成員、對(duì)變量不要提供setter方法、將所有的成員聲明為私有的,這樣就不允許直接訪問(wèn)這些成員、在getter方法中,不要直接返回對(duì)象本身,而是克隆對(duì)象,并返回對(duì)象的拷貝。

**44) Java中的ReadWriteLock是什么?
**一般而言,讀寫鎖是用來(lái)提升并發(fā)程序性能的鎖分離技術(shù)的成果。Java中的ReadWriteLock是Java 5 中新增的一個(gè)接口,一個(gè)ReadWriteLock維護(hù)一對(duì)關(guān)聯(lián)的鎖,一個(gè)用于只讀操作一個(gè)用于寫。在沒(méi)有寫線程的情況下一個(gè)讀鎖可能會(huì)同時(shí)被多個(gè)讀線程持有。寫鎖是獨(dú)占的,你可以使用JDK中的ReentrantReadWriteLock來(lái)實(shí)現(xiàn)這個(gè)規(guī)則,它最多支持65535個(gè)寫鎖和65535個(gè)讀鎖。

**45) 多線程中的忙循環(huán)是什么?
**忙循環(huán)就是程序員用循環(huán)讓一個(gè)線程等待,不像傳統(tǒng)方法wait(), sleep() 或 yield() 它們都放棄了CPU控制,而忙循環(huán)不會(huì)放棄CPU,它就是在運(yùn)行一個(gè)空循環(huán)。這么做的目的是為了保留CPU緩存,在多核系統(tǒng)中,一個(gè)等待線程醒來(lái)的時(shí)候可能會(huì)在另一個(gè)內(nèi)核運(yùn)行,這樣會(huì)重建緩存。為了避免重建緩存和減少等待重建的時(shí)間就可以使用它了。

**46)volatile 變量和 atomic 變量有什么不同?
**這是個(gè)有趣的問(wèn)題。首先,volatile 變量和 atomic 變量看起來(lái)很像,但功能卻不一樣。Volatile變量可以確保先行關(guān)系,即寫操作會(huì)發(fā)生在后續(xù)的讀操作之前, 但它并不能保證原子性。例如用volatile修飾count變量那么 count++ 操作就不是原子性的。而AtomicInteger類提供的atomic方法可以讓這種操作具有原子性如getAndIncrement()方法會(huì)原子性的進(jìn)行增量操作把當(dāng)前值加一,其它數(shù)據(jù)類型和引用變量也可以進(jìn)行相似操作。

**47) 如果同步塊內(nèi)的線程拋出異常會(huì)發(fā)生什么?
**這個(gè)問(wèn)題坑了很多Java程序員,若你能想到鎖是否釋放這條線索來(lái)回答還有點(diǎn)希望答對(duì)。無(wú)論你的同步塊是正常還是異常退出的,里面的線程都會(huì)釋放鎖,所以對(duì)比鎖接口我更喜歡同步塊,因?yàn)樗挥梦一ㄙM(fèi)精力去釋放鎖,該功能可以在finally block里釋放鎖實(shí)現(xiàn)。

**48) 單例模式的雙檢鎖是什么?
**這個(gè)問(wèn)題在Java面試中經(jīng)常被問(wèn)到,但是面試官對(duì)回答此問(wèn)題的滿意度僅為50%。一半的人寫不出雙檢鎖還有一半的人說(shuō)不出它的隱患和Java1.5是如何對(duì)它修正的。它其實(shí)是一個(gè)用來(lái)創(chuàng)建線程安全的單例的老方法,當(dāng)單例實(shí)例第一次被創(chuàng)建時(shí)它試圖用單個(gè)鎖進(jìn)行性能優(yōu)化,但是由于太過(guò)于復(fù)雜在JDK1.4中它是失敗的,我個(gè)人也不喜歡它。無(wú)論如何,即便你也不喜歡它但是還是要了解一下,因?yàn)樗?jīng)常被問(wèn)到。

**49) 如何在Java中創(chuàng)建線程安全的Singleton?
**這是上面那個(gè)問(wèn)題的后續(xù),如果你不喜歡雙檢鎖而面試官問(wèn)了創(chuàng)建Singleton類的替代方法,你可以利用JVM的類加載和靜態(tài)變量初始化特征來(lái)創(chuàng)建Singleton實(shí)例,或者是利用枚舉類型來(lái)創(chuàng)建Singleton,我很喜歡用這種方法。

**50) 寫出3條你遵循的多線程最佳實(shí)踐
**這種問(wèn)題我最喜歡了,我相信你在寫并發(fā)代碼來(lái)提升性能的時(shí)候也會(huì)遵循某些最佳實(shí)踐。以下三條最佳實(shí)踐我覺(jué)得大多數(shù)Java程序員都應(yīng)該遵循:
給你的線程起個(gè)有意義的名字。
這樣可以方便找bug或追蹤。OrderProcessor, QuoteProcessor or TradeProcessor 這種名字比 Thread-1. Thread-2 and Thread-3 好多了,給線程起一個(gè)和它要完成的任務(wù)相關(guān)的名字,所有的主要框架甚至JDK都遵循這個(gè)最佳實(shí)踐。
避免鎖定和縮小同步的范圍
鎖花費(fèi)的代價(jià)高昂且上下文切換更耗費(fèi)時(shí)間空間,試試最低限度的使用同步和鎖,縮小臨界區(qū)。因此相對(duì)于同步方法我更喜歡同步塊,它給我擁有對(duì)鎖的絕對(duì)控制權(quán)。
多用同步類少用wait 和 notify
首先,CountDownLatch, Semaphore, CyclicBarrier 和 Exchanger 這些同步類簡(jiǎn)化了編碼操作,而用wait和notify很難實(shí)現(xiàn)對(duì)復(fù)雜控制流的控制。其次,這些類是由最好的企業(yè)編寫和維護(hù)在后續(xù)的JDK中它們還會(huì)不斷優(yōu)化和完善,使用這些更高等級(jí)的同步工具你的程序可以不費(fèi)吹灰之力獲得優(yōu)化。
多用并發(fā)集合少用同步集合
這是另外一個(gè)容易遵循且受益巨大的最佳實(shí)踐,并發(fā)集合比同步集合的可擴(kuò)展性更好,所以在并發(fā)編程時(shí)使用并發(fā)集合效果更好。如果下一次你需要用到map,你應(yīng)該首先想到用ConcurrentHashMap。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 三年經(jīng)驗(yàn)網(wǎng)易、滴滴、點(diǎn)我Java崗面試經(jīng)驗(yàn)匯總

    這篇文章主要介紹了三年經(jīng)驗(yàn)網(wǎng)易、滴滴、點(diǎn)我Java崗面試經(jīng)驗(yàn)匯總,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-06-12
  • java基礎(chǔ)面試題整理小結(jié)

    本文是小編給大家收藏整理的關(guān)于java基礎(chǔ)面試題小結(jié),在面試中經(jīng)常會(huì)被問(wèn)題,今天小編特此整理把內(nèi)容分享到腳本之家平臺(tái),需要的朋友參考下吧
    2020-05-19
  • 史上最全阿里Java面試題目大匯總(強(qiáng)烈建議收藏)

    這篇文章主要介紹了史上最全阿里Java面試題目大匯總,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-05-08
  • 最全Java面試208題,涵蓋大廠必考范圍

    這篇文章主要介紹了最全Java面試208題,涵蓋大廠必考范圍,熟悉本文中列出的知識(shí)點(diǎn)會(huì)大大增加通過(guò)前兩輪技術(shù)面試的幾率,感興趣的可以了解一下
    2020-05-07
  • 2020年最新版Java面試題大全

    這篇文章主要介紹了2020年最新版Java面試題大全,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-04-16
  • 100+經(jīng)典Java面試題及答案解析

    這篇文章主要介紹了100+經(jīng)典Java面試題及答案解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)
    2020-04-09
  • 面試百度、阿里、騰訊,這134道Java面試題你會(huì)多少

    這篇文章主要介紹了面試百度、阿里、騰訊,這134道Java面試題你會(huì)多少,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-03-26
  • 85道Java微服務(wù)面試題整理,助力2020面試

    這篇文章主要介紹了85道Java微服務(wù)面試題整理,助力2020面試 ,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-03-13
  • 最新115道華為、京東、滴滴、美團(tuán)精選Java面試題整理

    這篇文章主要介紹了最新115道華為、京東、滴滴、美團(tuán)精選Java面試題整理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-03-04
  • 2萬(wàn)字Java并發(fā)編程面試題整理(含答案,建議收藏)

    這篇文章主要介紹了2萬(wàn)字Java并發(fā)編程面試題整理(含答案,建議收藏),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-13

最新評(píng)論