Java面試寶典

相關概念

面向對象的三個特徵

封裝,繼承,多態.這個應該是人人皆知.有時候也會加上抽象.html

多態的好處

容許不一樣類對象對同一消息作出響應,即同一消息能夠根據發送對象的不一樣而採用多種不一樣的行爲方式(發送消息就是函數調用).主要有如下優勢:java

  1. 可替換性:多態對已存在代碼具備可替換性.
  2. 可擴充性:增長新的子類不影響已經存在的類結構.
  3. 接口性:多態是超累經過方法簽名,想子類提供一個公共接口,由子類來完善或者重寫它來實現的.
  4. 靈活性:
  5. 簡化性:

代碼中如何實現多態

實現多態主要有如下三種方式: 1. 接口實現 2. 繼承父類重寫方法 3. 同一類中進行方法重載c++

虛擬機是如何實現多態的

動態綁定技術(dynamic binding),執行期間判斷所引用對象的實際類型,根據實際類型調用對應的方法.git

接口的意義

接口的意義用三個詞就能夠歸納:規範,擴展,回調.github

抽象類的意義

抽象類的意義能夠用三句話來歸納:web

  1. 爲其餘子類提供一個公共的類型
  2. 封裝子類中重複定義的內容
  3. 定義抽象方法,子類雖然有不一樣的實現,可是定義時一致的 ## 接口和抽象類的區別
比較 抽象類 接口
默認方法 抽象類能夠有默認的方法實現 ,java 8以前,接口中不存在方法的實現.
實現方式 子類使用extends關鍵字來繼承抽象類.若是子類不是抽象類,子類須要提供抽象類中所聲明方法的實現. 子類使用implements來實現接口,須要提供接口中全部聲明的實現.
構造器 抽象類中能夠有構造器, 接口中不能
和正常類區別 抽象類不能被實例化 接口則是徹底不一樣的類型
訪問修飾符 抽象方法能夠有public,protected和default等修飾 接口默認是public,不能使用其餘修飾符
多繼承 一個子類只能存在一個父類 一個子類能夠存在多個接口
添加新方法 想抽象類中添加新方法,能夠提供默認的實現,所以能夠不修改子類現有的代碼 若是往接口中添加新方法,則子類中須要實現該方法.
     

父類的靜態方法可否被子類重寫

不能.子類繼承父類後,有相同的靜態方法和非靜態,這是非靜態方法覆蓋父類中的方法(即方法重寫),父類的該靜態方法被隱藏(若是對象是父類則調用該隱藏的方法),另外子類可集成父類的靜態與非靜態方法,至於方法重載我以爲它其中一要素就是在同一類中,不能說父類中的什麼方法與子類裏的什麼方法是方法重載的體現.面試

什麼是不可變對象

不可變對象指對象一旦被建立,狀態就不能再改變。任何修改都會建立一個新的對象,如 String、Integer及其它包裝類。算法

可否建立一個包含可變對象的不可變對象?

固然能夠建立一個包含可變對象的不可變對象的,你只須要謹慎一點,不要共享可變對象的引用就能夠了,若是須要變化時,就返回原對象的一個拷貝。最多見的例子就是對象中包含一個日期對象的引用.spring

java 建立對象的幾種方式

  1. 採用new
  2. 經過反射
  3. 採用clone
  4. 經過序列化機制

前2者都須要顯式地調用構造方法. 形成耦合性最高的剛好是第一種,所以你發現不管什麼框架,只要涉及到解耦必先減小new的使用.編程

switch中可否使用string作參數

在idk 1.7以前,switch只能支持byte,short,char,int或者其對應的封裝類以及Enum類型。從idk 1.7以後switch開始支持String.

Object中有哪些公共方法?

  1. equals()
  2. clone()
  3. getClass()
  4. notify(),notifyAll(),wait()

java當中的四種引用

強引用,軟引用,弱引用,虛引用.不一樣的引用類型主要體如今GC上:

  1. 強引用:若是一個對象具備強引用,它就不會被垃圾回收器回收。即便當前內存空間不足,JVM也不會回收它,而是拋出 OutOfMemoryError 錯誤,使程序異常終止。若是想中斷強引用和某個對象之間的關聯,能夠顯式地將引用賦值爲null,這樣一來的話,JVM在合適的時間就會回收該對象
  2. 軟引用:在使用軟引用時,若是內存的空間足夠,軟引用就能繼續被使用,而不會被垃圾回收器回收,只有在內存不足時,軟引用纔會被垃圾回收器回收。
  3. 弱引用:具備弱引用的對象擁有的生命週期更短暫。由於當 JVM 進行垃圾回收,一旦發現弱引用對象,不管當前內存空間是否充足,都會將弱引用回收。不過因爲垃圾回收器是一個優先級較低的線程,因此並不必定能迅速發現弱引用對象
  4. 虛引用:顧名思義,就是形同虛設,若是一個對象僅持有虛引用,那麼它至關於沒有引用,在任什麼時候候均可能被垃圾回收器回收。

更多瞭解參見深刻對象引用

WeakReference與SoftReference的區別?

這點在四種引用類型中已經作了解釋,這裏簡單說明一下便可: 雖然 WeakReference 與 SoftReference 都有利於提升 GC 和 內存的效率,可是 WeakReference ,一旦失去最後一個強引用,就會被 GC 回收,而軟引用雖然不能阻止被回收,可是能夠延遲到 JVM 內存不足的時候。

爲何要有不一樣的引用類型

不像C語言,咱們能夠控制內存的申請和釋放,在Java中有時候咱們須要適當的控制對象被回收的時機,所以就誕生了不一樣的引用類型,能夠說不一樣的引用類型實則是對GC回收時機不可控的妥協.有如下幾個使用場景能夠充分的說明:

  1. 利用軟引用和弱引用解決OOM問題:用一個HashMap來保存圖片的路徑和相應圖片對象關聯的軟引用之間的映射關係,在內存不足時,JVM會自動回收這些緩存圖片對象所佔用的空間,從而有效地避免了OOM的問題.
  2. 經過軟引用實現Java對象的高速緩存:好比咱們建立了一Person的類,若是每次須要查詢一我的的信息,哪怕是幾秒中以前剛剛查詢過的,都要從新構建一個實例,這將引發大量Person對象的消耗,而且因爲這些對象的生命週期相對較短,會引發屢次GC影響性能。此時,經過軟引用和 HashMap 的結合能夠構建高速緩存,提供性能.

java中==和eqauls()的區別,equals()和`hashcode的區別

==是運算符,用於比較兩個變量是否相等,而equals是Object類的方法,用於比較兩個對象是否相等.默認Object類的equals方法是比較兩個對象的地址,此時和==的結果同樣.換句話說:基本類型比較用==,比較的是他們的值.默認下,對象用==比較時,比較的是內存地址,若是須要比較對象內容,須要重寫equal方法

equals()hashcode()的聯繫

hashCode()是Object類的一個方法,返回一個哈希值.若是兩個對象根據equal()方法比較相等,那麼調用這兩個對象中任意一個對象的hashCode()方法必須產生相同的哈希值. 若是兩個對象根據eqaul()方法比較不相等,那麼產生的哈希值不必定相等(碰撞的狀況下仍是會相等的.)

a.hashCode()有什麼用?與a.equals(b)有什麼關係

hashCode() 方法是相應對象整型的 hash 值。它經常使用於基於 hash 的集合類,如 Hashtable、HashMap、LinkedHashMap等等。它與 equals() 方法關係特別緊密。根據 Java 規範,兩個使用 equal() 方法來判斷相等的對象,必須具備相同的 hashcode。

將對象放入到集合中時,首先判斷要放入對象的hashcode是否已經在集合中存在,不存在則直接放入集合.若是hashcode相等,而後經過equal()方法判斷要放入對象與集合中的任意對象是否相等:若是equal()判斷不相等,直接將該元素放入集合中,不然不放入.

有沒有可能兩個不相等的對象有相同的hashcode

有可能,兩個不相等的對象可能會有相同的 hashcode 值,這就是爲何在 hashmap 中會有衝突。相等 hashcode 值的規定只是說若是兩個對象相等,必須有相同的hashcode 值,可是沒有關於不相等對象的任何規定。

能夠在hashcode中使用隨機數字嗎?

不行,由於同一對象的 hashcode 值必須是相同的

「a==b」與a.equals(b)有什麼區別

若是a 和b 都是對象,則 a==b 是比較兩個對象的引用,只有當 a 和 b 指向的是堆中的同一個對象纔會返回 true,而 a.equals(b) 是進行邏輯比較,因此一般須要重寫該方法來提供邏輯一致性的比較。例如,String 類重寫 equals() 方法,因此能夠用於兩個不一樣對象,可是包含的字母相同的比較。

3*0.1==0.3返回值是什麼

false,由於有些浮點數不能徹底精確的表示出來。

a=a+b與a+=b有什麼區別嗎?

隱式的將加操做的結果類型強制轉換爲持有結果的類型。若是兩這個整型相加,如 byte、short 或者 int,首先會將它們提高到 int 類型,而後在執行加法操做。若是加法操做的結果比 a 的最大值要大,則 a+b 會出現編譯錯誤,可是 a += b 沒問題,以下: byte a = 127; byte b = 127; b = a + b; // error : cannot convert from int to byte b += a; // ok (譯者注:這個地方應該表述的有誤,其實不管 a+b 的值爲多少,編譯器都會報錯,由於 a+b 操做會將 a、b 提高爲 int 類型,因此將 int 類型賦值給 byte 就會編譯出錯)

內部類的做用

內部類能夠用多個實例,每一個實例都有本身的狀態信息,而且與其餘外圍對象的信息相互獨立.在單個外圍類當中,可讓多個內部類以不一樣的方式實現同一接口,或者繼承同一個類.建立內部類對象的時刻病不依賴於外部類對象的建立.內部類並無使人疑惑的」is-a」關係,它就像是一個獨立的實體.

內部類提供了更好的封裝,除了該外圍類,其餘類都不能訪問

final,finalize和finally的不一樣之處

final 是一個修飾符,能夠修飾變量、方法和類。若是 final 修飾變量,意味着該變量的值在初始化後不能被改變。finalize 方法是在對象被回收以前調用的方法,給對象本身最後一個復活的機會,可是何時調用 finalize 沒有保證。finally 是一個關鍵字,與 try 和 catch 一塊兒用於異常的處理。finally 塊必定會被執行,不管在 try 塊中是否有發生異常。

clone()是哪一個類型的方法?

java.lang.Cloneable 是一個標示性接口,不包含任何方法,clone 方法在 object 類中定義。而且須要知道 clone() 方法是一個本地方法,這意味着它是由 c 或 c++ 或 其餘本地語言實現的。

深拷貝和淺拷貝的區別是什麼?

淺拷貝:被複制對象的全部變量都含有與原來的對象相同的值,而全部的對其餘對象的引用仍然指向原來的對象。換言之,淺拷貝僅僅複製所考慮的對象,而不復制它所引用的對象。

深拷貝:被複制對象的全部變量都含有與原來的對象相同的值,而那些引用其餘對象的變量將指向被複制過的新對象,而再也不是原有的那些被引用的對象。換言之,深拷貝把要複製的對象所引用的對象都複製了一遍。

static都有哪些用法?

幾乎全部的人都知道static關鍵字這兩個基本的用法:靜態變量和靜態方法.也就是被static所修飾的變量/方法都屬於類的靜態資源,類實例所共享.

除了靜態變量和靜態方法以外,static也用於靜態塊,多用於初始化操做:

public calss PreCache{ static{ //執行相關操做 } }

此外static也多用於修飾內部類,此時稱之爲靜態內部類.

最後一種用法就是靜態導包,即import static.import static是在JDK 1.5以後引入的新特性,能夠用來指定導入某個類中的靜態資源,而且不須要使用類名.資源名,能夠直接使用資源名,好比:

import static java.lang.Math.*; public class Test{ public static void main(String[] args){ //System.out.println(Math.sin(20));傳統作法 System.out.println(sin(20)); } } 

final有哪些用法

final也是不少面試喜歡問的地方,能回答下如下三點就不錯了: 1.被final修飾的類不能夠被繼承 2.被final修飾的方法不能夠被重寫 3.被final修飾的變量不能夠被改變.若是修飾的引用,那麼表示引用不可變,引用指向的內容可變. 4.被final修飾的方法,JVM會嘗試將其內聯,以提升運行效率 5.被final修飾的常量,在編譯階段會存入常量池中.

當前回答出編譯器對final域要遵照的兩個重排序規則更好: 1.在構造函數內對一個final域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變量,這兩個操做之間不能重排序. 2.初次讀一個包含final域的對象的引用,與隨後初次讀這個final域,這兩個操做之間不能重排序.


數據類型相關

java中int char,long各佔多少字節?

|類型|位數|字節數| |-|-|-| |short|2|16| |int|4|32| |long|8|64| |float|4|32 |double|8|64| |char|2|16|

64位的JVM當中,int的長度是多少?

Java 中,int 類型變量的長度是一個固定值,與平臺無關,都是 32 位。意思就是說,在 32 位 和 64 位 的Java 虛擬機中,int 類型的長度是相同的。

java int和Integer的區別

Integer是int的包裝類型,在拆箱和裝箱中,而知自動轉換.int是基本類型,直接存數值,而integer是對象,用一個引用指向這個對象.

int 和Integer誰佔用的內存更多?

Integer 對象會佔用更多的內存。Integer是一個對象,須要存儲對象的元數據。可是 int 是一個原始類型的數據,因此佔用的空間更少。

String,StringBuffer和StringBuilder區別

String是字符串常量,final修飾;StringBuffer字符串變量(線程安全); StringBuilder 字符串變量(線程不安全).

String和StringBuffer

String和StringBuffer主要區別是性能:String是不可變對象,每次對String類型進行操做都等同於產生了一個新的String對象,而後指向新的String對象.因此儘可能不在對String進行大量的拼接操做,不然會產生不少臨時對象,致使GC開始工做,影響系統性能.

StringBuffer是對對象自己操做,而不是產生新的對象,所以在一般在有大量拼接的狀況下咱們建議使用StringBuffer.

可是須要注意如今JVM會對String拼接作必定的優化: String s=「This is only 」+」simple」+」test」會被虛擬機直接優化成String s=「This is only simple test」,此時就不存在拼接過程.

StringBuffer和StringBuilder

StringBuffer是線程安全的可變字符串,其內部實現是可變數組.StringBuilder是java 5.0新增的,其功能和StringBuffer相似,可是非線程安全.所以,在沒有多線程問題的前提下,使用StringBuilder會取得更好的性能.

什麼是編譯器常量?使用它有什麼風險?

公共靜態不可變(public static final )變量也就是咱們所說的編譯期常量,這裏的 public 可選的。實際上這些變量在編譯時會被替換掉,由於編譯器知道這些變量的值,而且知道這些變量在運行時不能改變。這種方式存在的一個問題是你使用了一個內部的或第三方庫中的公有編譯時常量,可是這個值後面被其餘人改變了,可是你的客戶端仍然在使用老的值,甚至你已經部署了一個新的jar。爲了不這種狀況,當你在更新依賴 JAR 文件時,確保從新編譯你的程序。

java當中使用什麼類型表示價格比較好?

若是不是特別關心內存和性能的話,使用BigDecimal,不然使用預約義精度的 double 類型。

如何將byte轉爲String

可使用 String 接收 byte[] 參數的構造器來進行轉換,須要注意的點是要使用的正確的編碼,不然會使用平臺默認編碼,這個編碼可能跟原來的編碼相同,也可能不一樣。

能夠將int強轉爲byte類型麼?會產生什麼問題?

咱們能夠作強制轉換,可是Java中int是32位的而byte是8 位的,因此,若是強制轉化int類型的高24位將會被丟棄,byte 類型的範圍是從-128.到128


關於垃圾回收

你知道哪些垃圾回收算法?

垃圾回收從理論上很是容易理解,具體的方法有如下幾種:

  1. 標記-清除
  2. 標記-複製
  3. 標記-整理
  4. 分代回收 更詳細的內容參見深刻理解垃圾回收算法

如何判斷一個對象是否應該被回收

這就是所謂的對象存活性判斷,經常使用的方法有兩種:1.引用計數法;2:對象可達性分析.因爲引用計數法存在互相引用致使沒法進行GC的問題,因此目前JVM虛擬機多使用對象可達性分析算法.

簡單的解釋一下垃圾回收

Java 垃圾回收機制最基本的作法是分代回收。內存中的區域被劃分紅不一樣的世代,對象根據其存活的時間被保存在對應世代的區域中。通常的實現是劃分紅3個世代:年輕、年老和永久。內存的分配是發生在年輕世代中的。當一個對象存活時間足夠長的時候,它就會被複制到年老世代中。對於不一樣的世代可使用不一樣的垃圾回收算法。進行世代劃分的出發點是對應用中對象存活時間進行研究以後得出的統計規律。通常來講,一個應用中的大部分對象的存活時間都很短。好比局部變量的存活時間就只在方法的執行過程當中。基於這一點,對於年輕世代的垃圾回收算法就能夠頗有針對性.

調用System.gc()會發生什麼?

通知GC開始工做,可是GC真正開始的時間不肯定.


進程,線程相關

說說進程,線程,協程之間的區別

簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程.進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存資源,減小切換次數,從而效率更高.線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位.同一進程中的多個線程之間能夠併發執行.

你瞭解守護線程嗎?它和非守護線程有什麼區別

程序運行完畢,jvm會等待非守護線程完成後關閉,可是jvm不會等待守護線程.守護線程最典型的例子就是GC線程

什麼是多線程上下文切換

多線程的上下文切換是指CPU控制權由一個已經正在運行的線程切換到另一個就緒並等待獲取CPU執行權的線程的過程。

建立兩種線程的方式?他們有什麼區別?

經過實現java.lang.Runnable或者經過擴展java.lang.Thread類.相比擴展Thread,實現Runnable接口可能更優.緣由有二:

  1. Java不支持多繼承.所以擴展Thread類就表明這個子類不能擴展其餘類.而實現Runnable接口的類還可能擴展另外一個類.
  2. 類可能只要求可執行便可,所以集成整個Thread類的開銷過大.

Runnable和Callable的區別

Runnable接口中的run()方法的返回值是void,它作的事情只是純粹地去執行run()方法中的代碼而已;Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合能夠用來獲取異步執行的結果。 這實際上是頗有用的一個特性,由於多線程相比單線程更難、更復雜的一個重要緣由就是由於多線程充滿着未知性,某條線程是否執行了?某條線程執行了多久?某條線程執行的時候咱們指望的數據是否已經賦值完畢?沒法得知,咱們能作的只是等待這條多線程的任務執行完畢而已。而Callable+Future/FutureTask卻能夠獲取多線程運行的結果,能夠在等待時間太長沒獲取到須要的數據的狀況下取消該線程的任務,真的是很是有用。

什麼致使線程阻塞

阻塞指的是暫停一個線程的執行以等待某個條件發生(如某資源就緒),學過操做系統的同窗對它必定已經很熟悉了。Java 提供了大量方法來支持阻塞,下面讓咱們逐一分析。

方法 說明
sleep() sleep() 容許 指定以毫秒爲單位的一段時間做爲參數,它使得線程在指定的時間內進入阻塞狀態,不能獲得CPU 時間,指定的時間一過,線程從新進入可執行狀態。 典型地,sleep() 被用在等待某個資源就緒的情形:測試發現條件不知足後,讓線程阻塞一段時間後從新測試,直到條件知足爲止
suspend() 和 resume() 兩個方法配套使用,suspend()使得線程進入阻塞狀態,而且不會自動恢復,必須其對應的resume() 被調用,才能使得線程從新進入可執行狀態。典型地,suspend() 和 resume() 被用在等待另外一個線程產生的結果的情形:測試發現結果尚未產生後,讓線程阻塞,另外一個線程產生告終果後,調用 resume() 使其恢復。
yield() yield() 使得線程放棄當前分得的 CPU 時間,可是不使線程阻塞,即線程仍處於可執行狀態,隨時可能再次分得 CPU 時間。調用 yield() 的效果等價於調度程序認爲該線程已執行了足夠的時間從而轉到另外一個線程
wait() 和 notify() 兩個方法配套使用,wait() 使得線程進入阻塞狀態,它有兩種形式,一種容許 指定以毫秒爲單位的一段時間做爲參數,另外一種沒有參數,前者當對應的 notify() 被調用或者超出指定時間時線程從新進入可執行狀態,後者則必須對應的 notify() 被調用.

wait(),notify()和suspend(),resume()之間的區別

初看起來它們與 suspend() 和 resume() 方法對沒有什麼分別,可是事實上它們是大相徑庭的。區別的核心在於,前面敘述的全部方法,阻塞時都不會釋放佔用的鎖(若是佔用了的話),而這一對方法則相反。上述的核心區別致使了一系列的細節上的區別。

首先,前面敘述的全部方法都隸屬於 Thread 類,可是這一對卻直接隸屬於 Object 類,也就是說,全部對象都擁有這一對方法。初看起來這十分難以想象,可是實際上倒是很天然的,由於這一對方法阻塞時要釋放佔用的鎖,而鎖是任何對象都具備的,調用任意對象的 wait() 方法致使線程阻塞,而且該對象上的鎖被釋放。而調用 任意對象的notify()方法則致使因調用該對象的 wait() 方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到得到鎖後才真正可執行)。

其次,前面敘述的全部方法均可在任何位置調用,可是這一對方法卻必須在 synchronized 方法或塊中調用,理由也很簡單,只有在synchronized 方法或塊中當前線程才佔有鎖,纔有鎖能夠釋放。一樣的道理,調用這一對方法的對象上的鎖必須爲當前線程所擁有,這樣纔有鎖能夠釋放。所以,這一對方法調用必須放置在這樣的 synchronized 方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不知足這一條件,則程序雖然仍能編譯,但在運行時會出現IllegalMonitorStateException 異常。

wait() 和 notify() 方法的上述特性決定了它們常常和synchronized 方法或塊一塊兒使用,將它們和操做系統的進程間通訊機制做一個比較就會發現它們的類似性:synchronized方法或塊提供了相似於操做系統原語的功能,它們的執行不會受到多線程機制的干擾,而這一對方法則至關於 block 和wakeup 原語(這一對方法均聲明爲 synchronized)。它們的結合使得咱們能夠實現操做系統上一系列精妙的進程間通訊的算法(如信號量算法),並用於解決各類複雜的線程間通訊問題。

關於 wait() 和 notify() 方法最後再說明兩點: 第一:調用 notify() 方法致使解除阻塞的線程是從因調用該對象的 wait() 方法而阻塞的線程中隨機選取的,咱們沒法預料哪個線程將會被選擇,因此編程時要特別當心,避免因這種不肯定性而產生問題。

第二:除了 notify(),還有一個方法 notifyAll() 也可起到相似做用,惟一的區別在於,調用 notifyAll() 方法將把因調用該對象的 wait() 方法而阻塞的全部線程一次性所有解除阻塞。固然,只有得到鎖的那一個線程才能進入可執行狀態。

談到阻塞,就不能不談一談死鎖,略一分析就能發現,suspend() 方法和不指定超時期限的 wait() 方法的調用均可能產生死鎖。遺憾的是,Java 並不在語言級別上支持死鎖的避免,咱們在編程中必須當心地避免死鎖。

以上咱們對 Java 中實現線程阻塞的各類方法做了一番分析,咱們重點分析了 wait() 和 notify() 方法,由於它們的功能最強大,使用也最靈活,可是這也致使了它們的效率較低,較容易出錯。實際使用中咱們應該靈活使用各類方法,以便更好地達到咱們的目的。

爲何wait()方法和notify()/notifyAll()方法要在同步塊中被調用

這是JDK強制的,wait()方法和notify()/notifyAll()方法在調用前都必須先得到對象的鎖

wait()方法和notify()/notifyAll()方法在放棄對象監視器時有什麼區別

wait()方法和notify()/notifyAll()方法在放棄對象監視器的時候的區別在於:wait()方法當即釋放對象監視器,notify()/notifyAll()方法則會等待線程剩餘代碼執行完畢纔會放棄對象監視器。

wait()與sleep()的區別

關於這二者已經在上面進行詳細的說明,這裏就作個歸納好了:

  • sleep()來自Thread類,和wait()來自Object類.調用sleep()方法的過程當中,線程不會釋放對象鎖。而 調用 wait 方法線程會釋放對象鎖
  • sleep()睡眠後不出讓系統資源,wait讓其餘線程能夠佔用CPU
  • sleep(milliseconds)須要指定一個睡眠時間,時間一到會自動喚醒.而wait()須要配合notify()或者notifyAll()使用

synchronized和ReentrantLock的區別

synchronized是和if、else、for、while同樣的關鍵字,ReentrantLock是類,這是兩者的本質區別。既然ReentrantLock是類,那麼它就提供了比synchronized更多更靈活的特性,能夠被繼承、能夠有方法、能夠有各類各樣的類變量,ReentrantLock比synchronized的擴展性體如今幾點上: (1)ReentrantLock能夠對獲取鎖的等待時間進行設置,這樣就避免了死鎖 (2)ReentrantLock能夠獲取各類鎖的信息 (3)ReentrantLock能夠靈活地實現多路通知 另外,兩者的鎖機制其實也是不同的:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操做的應該是對象頭中mark word.

FutureTask是什麼

這個其實前面有提到過,FutureTask表示一個異步運算的任務。FutureTask裏面能夠傳入一個Callable的具體實現類,能夠對這個異步運算的任務的結果進行等待獲取、判斷是否已經完成、取消任務等操做。固然,因爲FutureTask也是Runnable接口的實現類,因此FutureTask也能夠放入線程池中。

一個線程若是出現了運行時異常怎麼辦?

若是這個異常沒有被捕獲的話,這個線程就中止執行了。另外重要的一點是:若是這個線程持有某個某個對象的監視器,那麼這個對象監視器會被當即釋放

如何在兩個線程間共享數據

經過在線程之間共享對象就能夠了,而後經過wait/notify/notifyAll、await/signal/signalAll進行喚起和等待,比方說阻塞隊列BlockingQueue就是爲線程之間共享數據而設計的

如何正確的使用wait()?使用if仍是while?

wait() 方法應該在循環調用,由於當線程獲取到 CPU 開始執行的時候,其餘條件可能尚未知足,因此在處理前,循環檢測條件是否知足會更好。下面是一段標準的使用 wait 和 notify 方法的代碼:

synchronized (obj) {
    while (condition does not hold)
      obj.wait(); // (Releases lock, and reacquires on wakeup)
      ... // Perform action appropriate to condition
 }

什麼是線程局部變量

線程局部變量是侷限於線程內部的變量,屬於線程自身全部,不在多個線程間共享。Java提供ThreadLocal類來支持線程局部變量,是一種實現線程安全的方式。可是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別當心,在這種狀況下,工做線程的生命週期比任何應用變量的生命週期都要長。任何線程局部變量一旦在工做完成後沒有釋放,Java 應用就存在內存泄露的風險。

ThreadLoal的做用是什麼?

簡單說ThreadLocal就是一種以空間換時間的作法在每一個Thread裏面維護了一個ThreadLocal.ThreadLocalMap把數據進行隔離,數據不共享,天然就沒有線程安全方面的問題了.

生產者消費者模型的做用是什麼?

(1)經過平衡生產者的生產能力和消費者的消費能力來提高整個系統的運行效率,這是生產者消費者模型最重要的做用 (2)解耦,這是生產者消費者模型附帶的做用,解耦意味着生產者和消費者之間的聯繫少,聯繫越少越能夠獨自發展而不須要收到相互的制約

寫一個生產者-消費者隊列

能夠經過阻塞隊列實現,也能夠經過wait-notify來實現.

使用阻塞隊列來實現

//消費者
public class Producer implements Runnable{
    private final BlockingQueue<Integer> queue;

    public Producer(BlockingQueue q){
        this.queue=q;
    }

    @Override
    public void run() {
        try {
            while (true){
                Thread.sleep(1000);//模擬耗時
                queue.put(produce());
            }
        }catch (InterruptedException e){

        }
    }

    private int produce() {
        int n=new Random().nextInt(10000);
        System.out.println("Thread:" + Thread.currentThread().getId() + " produce:" + n);
        return n;
    }
}
//消費者
public class Consumer implements Runnable {
    private final BlockingQueue<Integer> queue;

    public Consumer(BlockingQueue q){
        this.queue=q;
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(2000);//模擬耗時
                consume(queue.take());
            }catch (InterruptedException e){

            }

        }
    }

    private void consume(Integer n) {
        System.out.println("Thread:" + Thread.currentThread().getId() + " consume:" + n);

    }
}
//測試
public class Main {

    public static void main(String[] args) {
        BlockingQueue<Integer> queue=new ArrayBlockingQueue<Integer>(100);
        Producer p=new Producer(queue);
        Consumer c1=new Consumer(queue);
        Consumer c2=new Consumer(queue);

        new Thread(p).start();
        new Thread(c1).start();
        new Thread(c2).start();
    }
}

使用wait-notify來實現

該種方式應該最經典,這裏就不作說明了

ConcurrentHashMap的併發度是什麼?

ConcurrentHashMap的併發度就是segment的大小,默認爲16,這意味着最多同時能夠有16條線程操做ConcurrentHashMap,這也是ConcurrentHashMap對Hashtable的最大優點,任何狀況下,Hashtable能同時有兩條線程獲取Hashtable中的數據嗎?

CyclicBarrier和CountDownLatch區別

這兩個類很是相似,都在java.util.concurrent下,均可以用來表示代碼運行到某個點上,兩者的區別在於:

  • CyclicBarrier的某個線程運行到某個點上以後,該線程即中止運行,直到全部的線程都到達了這個點,全部線程才從新運行;CountDownLatch則不是,某線程運行到某個點上以後,只是給某個數值-1而已,該線程繼續運行
  • CyclicBarrier只能喚起一個任務,CountDownLatch能夠喚起多個任務
  • CyclicBarrier可重用,CountDownLatch不可重用,計數值爲0該CountDownLatch就不可再用了

java中的++操做符線程安全麼?

不是線程安全的操做。它涉及到多個指令,如讀取變量值,增長,而後存儲回內存,這個過程可能會出現多個線程交差

你有哪些多線程開發良好的實踐?

  1. 給線程命名
  2. 最小化同步範圍
  3. 優先使用volatile
  4. 儘量使用更高層次的併發工具而非wait和notify()來實現線程通訊,如BlockingQueue,Semeaphore
  5. 優先使用併發容器而非同步容器.
  6. 考慮使用線程池

關於volatile關鍵字

能夠建立Volatile數組嗎?

Java 中能夠建立 volatile類型數組,不過只是一個指向數組的引用,而不是整個數組。若是改變引用指向的數組,將會受到volatile 的保護,可是若是多個線程同時改變數組的元素,volatile標示符就不能起到以前的保護做用了

volatile能使得一個非原子操做變成原子操做嗎?

一個典型的例子是在類中有一個 long 類型的成員變量。若是你知道該成員變量會被多個線程訪問,如計數器、價格等,你最好是將其設置爲 volatile。爲何?由於 Java 中讀取 long 類型變量不是原子的,須要分紅兩步,若是一個線程正在修改該 long 變量的值,另外一個線程可能只能看到該值的一半(前 32 位)。可是對一個 volatile 型的 long 或 double 變量的讀寫是原子。

一種實踐是用 volatile 修飾 long 和 double 變量,使其能按原子類型來讀寫。double 和 long 都是64位寬,所以對這兩種類型的讀是分爲兩部分的,第一次讀取第一個 32 位,而後再讀剩下的 32 位,這個過程不是原子的,但 Java 中 volatile 型的 long 或 double 變量的讀寫是原子的。volatile 修復符的另外一個做用是提供內存屏障(memory barrier),例如在分佈式框架中的應用。簡單的說,就是當你寫一個 volatile 變量以前,Java 內存模型會插入一個寫屏障(write barrier),讀一個 volatile 變量以前,會插入一個讀屏障(read barrier)。意思就是說,在你寫一個 volatile 域時,能保證任何線程都能看到你寫的值,同時,在寫以前,也能保證任何數值的更新對全部線程是可見的,由於內存屏障會將其餘全部寫的值更新到緩存。

volatile類型變量提供什麼保證?

volatile 主要有兩方面的做用:1.避免指令重排2.可見性保證.例如,JVM 或者 JIT爲了得到更好的性能會對語句重排序,可是 volatile 類型變量即便在沒有同步塊的狀況下賦值也不會與其餘語句重排序。 volatile 提供 happens-before 的保證,確保一個線程的修改能對其餘線程是可見的。某些狀況下,volatile 還能提供原子性,如讀 64 位數據類型,像 long 和 double 都不是原子的(低32位和高32位),但 volatile 類型的 double 和 long 就是原子的.


關於集合

Java中的集合及其繼承關係

關於集合的體系是每一個人都應該爛熟於心的,尤爲是對咱們常用的List,Map的原理更該如此.這裏咱們看這張圖便可: 

更多內容可見集合類總結

poll()方法和remove()方法區別?

poll() 和 remove() 都是從隊列中取出一個元素,可是 poll() 在獲取元素失敗的時候會返回空,可是 remove() 失敗的時候會拋出異常。

LinkedHashMap和PriorityQueue的區別

PriorityQueue 是一個優先級隊列,保證最高或者最低優先級的的元素老是在隊列頭部,可是 LinkedHashMap 維持的順序是元素插入的順序。當遍歷一個 PriorityQueue 時,沒有任何順序保證,可是 LinkedHashMap 課保證遍歷順序是元素插入的順序。

WeakHashMap與HashMap的區別是什麼?

WeakHashMap 的工做與正常的 HashMap 相似,可是使用弱引用做爲 key,意思就是當 key 對象沒有任何引用時,key/value 將會被回收。

ArrayList和LinkedList的區別?

最明顯的區別是 ArrrayList底層的數據結構是數組,支持隨機訪問,而 LinkedList 的底層數據結構是雙向循環鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間複雜度是 O(1),而 LinkedList 是 O(n)。

ArrayList和HashMap默認大小?

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

private static final int DEFAULT_CAPACITY = 10;

 //from HashMap.java JDK 7
 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

Comparator和Comparable的區別?

Comparable 接口用於定義對象的天然順序,而 comparator 一般用於定義用戶定製的順序。Comparable 老是隻有一個,可是能夠有多個 comparator 來定義對象的順序。

如何實現集合排序?

你可使用有序集合,如 TreeSet 或 TreeMap,你也可使用有順序的的集合,如 list,而後經過 Collections.sort() 來排序。

如何打印數組內容

你可使用 Arrays.toString() 和 Arrays.deepToString() 方法來打印數組。因爲數組沒有實現 toString() 方法,因此若是將數組傳遞給 System.out.println() 方法,將沒法打印出數組的內容,可是 Arrays.toString() 能夠打印每一個元素。

LinkedList的是單向鏈表仍是雙向?

雙向循環列表,具體實現自行查閱源碼.

TreeMap是實現原理

採用紅黑樹實現,具體實現自行查閱源碼.

遍歷ArrayList時如何正確移除一個元素

該問題的關鍵在於面試者使用的是 ArrayList 的 remove() 仍是 Iterator 的 remove()方法。這有一段示例代碼,是使用正確的方式來實如今遍歷的過程當中移除元素,而不會出現 ConcurrentModificationException 異常的示例代碼。

什麼是ArrayMap?它和HashMap有什麼區別?

ArrayMap是Android SDK中提供的,非Android開發者能夠略過. ArrayMap是用兩個數組來模擬map,更少的內存佔用空間,更高的效率. 具體參考這篇文章:ArrayMap VS HashMap

HashMap的實現原理

1 HashMap概述: HashMap是基於哈希表的Map接口的非同步實現。此實現提供全部可選的映射操做,並容許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。 2 HashMap的數據結構: 在java編程語言中,最基本的結構就是兩種,一個是數組,另一個是模擬指針(引用),全部的數據結構均可以用這兩個基本結構來構造的,HashMap也不例外。HashMap其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體。

當咱們往Hashmap中put元素時,首先根據key的hashcode從新計算hash值,根絕hash值獲得這個元素在數組中的位置(下標),若是該數組在該位置上已經存放了其餘元素,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放入鏈尾.若是數組中該位置沒有元素,就直接將該元素放到數組的該位置上.

你瞭解Fail-Fast機制嗎

Fail-Fast即咱們常說的快速失敗,更多內容參看fail-fast機制


關於日期

SimpleDateFormat是線程安全的嗎?

很是不幸,DateFormat 的全部實現,包括 SimpleDateFormat 都不是線程安全的,所以你不該該在多線程序中使用,除非是在對外線程安全的環境中使用,如 將 SimpleDateFormat 限制在 ThreadLocal 中。若是你不這麼作,在解析或者格式化日期的時候,可能會獲取到一個不正確的結果。所以,從日期、時間處理的全部實踐來講,我強力推薦 joda-time 庫。

如何格式化日期?

Java 中,可使用 SimpleDateFormat 類或者 joda-time 庫來格式日期。DateFormat 類容許你使用多種流行的格式來格式化日期。參見答案中的示例代碼,代碼中演示了將日期格式化成不一樣的格式,如 dd-MM-yyyy 或 ddMMyyyy。


關於異常

簡單描述java異常體系

相比沒有人不瞭解異常體系,關於異常體系的更多信息能夠見:白話異常機制

什麼是異常鏈

詳情直接參見白話異常機制,不作解釋了.

throw和throws的區別

throw用於主動拋出java.lang.Throwable 類的一個實例化對象,意思是說你能夠經過關鍵字 throw 拋出一個 Error 或者 一個Exception,如:throw new IllegalArgumentException(「size must be multiple of 2″), 而throws 的做用是做爲方法聲明和簽名的一部分,方法被拋出相應的異常以便調用者能處理。Java 中,任何未處理的受檢查異常強制在 throws 子句中聲明。


關於序列化

Java 中,Serializable 與 Externalizable 的區別

Serializable 接口是一個序列化 Java 類的接口,以便於它們能夠在網絡上傳輸或者能夠將它們的狀態保存在磁盤上,是 JVM 內嵌的默認序列化方式,成本高、脆弱並且不安全。Externalizable 容許你控制整個序列化過程,指定特定的二進制格式,增長安全機制。


關於JVM

JVM特性

平臺無關性. Java語言的一個很是重要的特色就是與平臺的無關性。而使用Java虛擬機是實現這一特色的關鍵。通常的高級語言若是要在不一樣的平臺上運行,至少須要編譯成不一樣的目標代碼。而引入Java語言虛擬機後,Java語言在不一樣平臺上運行時不須要從新編譯。Java語言使用模式Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就能夠在多種平臺上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。

簡單解釋一下類加載器

有關類加載器通常會問你四種類加載器的應用場景以及雙親委派模型,更多的內容參看深刻理解JVM加載器

簡述堆和棧的區別

VM 中堆和棧屬於不一樣的內存區域,使用目的也不一樣。棧經常使用於保存方法幀和局部變量,而對象老是在堆上分配。棧一般都比堆小,也不會在多個線程之間共享,而堆被整個 JVM 的全部線程共享。

簡述JVM內存分配

  1. 基本數據類型比變量和對象的引用都是在棧分配的
  2. 堆內存用來存放由new建立的對象和數組
  3. 類變量(static修飾的變量),程序在一加載的時候就在堆中爲類變量分配內存,堆中的內存地址存放在棧中
  4. 實例變量:當你使用java關鍵字new的時候,系統在堆中開闢並不必定是連續的空間分配給變量,是根據零散的堆內存地址,經過哈希算法換算爲一長串數字以表徵這個變量在堆中的"物理位置」,實例變量的生命週期--當實例變量的引用丟失後,將被GC(垃圾回收器)列入可回收「名單」中,但並非立刻就釋放堆中內存
  5. 局部變量: 由聲明在某方法,或某代碼段裏(好比for循環),執行到它的時候在棧中開闢內存,當局部變量一但脫離做用域,內存當即釋放

其餘

java當中採用的是大端仍是小端?

XML解析的幾種方式和特色

DOM,SAX,PULL三種解析方式:

  • DOM:消耗內存:先把xml文檔都讀到內存中,而後再用DOM API來訪問樹形結構,並獲取數據。這個寫起來很簡單,可是很消耗內存。要是數據過大,手機不夠牛逼,可能手機直接死機
  • SAX:解析效率高,佔用內存少,基於事件驅動的:更加簡單地說就是對文檔進行順序掃描,當掃描到文檔(document)開始與結束、元素(element)開始與結束、文檔(document)結束等地方時通知事件處理函數,由事件處理函數作相應動做,而後繼續一樣的掃描,直至文檔結束。
  • PULL:與 SAX 相似,也是基於事件驅動,咱們能夠調用它的next()方法,來獲取下一個解析事件(就是開始文檔,結束文檔,開始標籤,結束標籤),當處於某個元素時能夠調用XmlPullParser的getAttributte()方法來獲取屬性的值,也可調用它的nextText()獲取本節點的值。

JDK 1.7特性

然 JDK 1.7 不像 JDK 5 和 8 同樣的大版本,可是,仍是有不少新的特性,如 try-with-resource 語句,這樣你在使用流或者資源的時候,就不須要手動關閉,Java 會自動關閉。Fork-Join 池某種程度上實現 Java 版的 Map-reduce。容許 Switch 中有 String 變量和文本。菱形操做符(<>)用於類型推斷,再也不須要在變量聲明的右邊申明泛型,所以能夠寫出可讀寫更強、更簡潔的代碼

JDK 1.8特性

java 8 在 Java 歷史上是一個開創新的版本,下面 JDK 8 中 5 個主要的特性: Lambda 表達式,容許像對象同樣傳遞匿名函數 Stream API,充分利用現代多核 CPU,能夠寫出很簡潔的代碼 Date 與 Time API,最終,有一個穩定、簡單的日期和時間庫可供你使用 擴展方法,如今,接口中能夠有靜態、默認方法。 重複註解,如今你能夠將相同的註解在同一類型上使用屢次。

Maven和ANT有什麼區別?

雖然二者都是構建工具,都用於建立 Java 應用,可是 Maven 作的事情更多,在基於「約定優於配置」的概念下,提供標準的Java 項目結構,同時能爲應用自動管理依賴(應用中所依賴的 JAR 文件),Maven 與 ANT 工具更多的不一樣之處請參見答案。 這就是全部的面試題,如此之多,是否是?我能夠保證,若是你能回答列表中的全部問題,你就能夠很輕鬆的應付任何核心 Java 或者高級 Java 面試。雖然,這裏沒有涵蓋 Servlet、JSP、JSF、JPA,JMS,EJB 及其它 Java EE 技術,也沒有包含主流的框架如 spring MVC,Struts 2.0,hibernate,也沒有包含 SOAP 和 RESTful web service,可是這份列表對作 Java 開發的、準備應聘 Java web 開發職位的人仍是一樣有用的,由於全部的 Java 面試,開始的問題都是 Java 基礎和 JDK API 相關的。若是你認爲我這裏有任何應該在這份列表中而被我遺漏了的 Java 流行的問題,你能夠自由的給我建議。個人目的是從最近的面試中建立一份最新的、最優的 Java 面試問題列表。

JDBC最佳實踐

  • 優先使用批量操做來插入和更新數據
  • 使用PreparedStatement來避免SQL漏洞
  • 使用數據鏈接池
  • 經過列名來獲取結果集

IO操做最佳實踐

  1. 使用有緩衝的IO類,不要單獨讀取字節或字符
  2. 使用NIO和NIO 2或者AIO,而非BIO
  3. 在finally中關閉流
  4. 使用內存映射文件獲取更快的IO

http://www.cnblogs.com/bluestorm/p/6429894.html

相關文章
相關標籤/搜索