前幾天,有朋友去面試之前問我關于后端架構相關的問題,但奈于我去年更多的工作是在移動SDK開發(fā)上,對此有所遺忘,實屬無奈,后面準備總結下.
今天要談的主題是關于求職.求職是在每個技術人員的生涯中都要經(jīng)歷多次,對于我們大部分人而言,在進入自己心儀的公司之前少不了準備工作,有一份全面細致面試題將幫助我們減少許多麻煩.在跳槽季來臨之前,特地做這個系列的文章,一方面幫助自己鞏固下基礎,另一方面也希望幫助想要換工作的朋友.
從12年開始,我先后做過爬蟲,搜索,機器學習,javaEE及Android等方面的事情,而其中主要的工具便是Java和C,所以這個系列的重點也放在這兩方面.感興趣的朋友可以關注:https://github.com/closedevice/interview-about.
為了更好的樹立知識體系,我附加了相關的思維導圖,分為pdf版和mindnote版.比如java相關的導圖如下:
由于時間倉促,有些地方未寫完,后面會繼續(xù)補充.如有不妥之處,歡迎及時與我溝通.
相關概念
面向對象的三個特征
封裝,繼承,多態(tài).這個應該是人人皆知.
多態(tài)的好處
允許不同類對象對同一消息做出響應,即同一消息可以根據(jù)發(fā)送對象的不同而采用多種不同的行為方式(發(fā)送消息就是函數(shù)調(diào)用).主要有以下優(yōu)點:
- 可替換性:多態(tài)對已存在代碼具有可替換性.
- 可擴充性:增加新的子類不影響已經(jīng)存在的類結構.
- 接口性:多態(tài)是超累通過方法簽名,想子類提供一個公共接口,由子類來完善或者重寫它來實現(xiàn)的.
- 靈活性:
- 簡化性:
代碼中如何實現(xiàn)多態(tài)
實現(xiàn)多態(tài)主要有以下三種方式:
- 接口實現(xiàn)
- 繼承父類重寫方法
- 同一類中進行方法重載
虛擬機是如何實現(xiàn)多態(tài)的
動態(tài)綁定技術(dynamic binding),執(zhí)行期間判斷所引用對象的實際類型,根據(jù)實際類型調(diào)用對應的方法.
接口的意義
接口的意義用三個詞就可以概括:規(guī)范,擴展,回調(diào).
抽象類的意義
抽象類的意義可以用三句話來概括:
- 為其他子類提供一個公共的類型
- 封裝子類中重復定義的內(nèi)容
- 定義抽象方法,子類雖然有不同的實現(xiàn),但是定義時一致的
接口和抽象類的區(qū)別
比較 | 抽象類 | 接口 |
---|---|---|
默認方法 | 抽象類可以有默認的方法實現(xiàn) | ,java 8之前,接口中不存在方法的實現(xiàn). |
實現(xiàn)方式 | 子類使用extends關鍵字來繼承抽象類.如果子類不是抽象類,子類需要提供抽象類中所聲明方法的實現(xiàn). | 子類使用implements來實現(xiàn)接口,需要提供接口中所有聲明的實現(xiàn). |
構造器 | 抽象類中可以有構造器, | 接口中不能 |
和正常類區(qū)別 | 抽象類不能被實例化 | 接口則是完全不同的類型 |
訪問修飾符 | 抽象方法可以有public,protected和default等修飾 | 接口默認是public,不能使用其他修飾符 |
多繼承 | 一個子類只能存在一個父類 | 一個子類可以存在多個接口 |
添加新方法 | 想抽象類中添加新方法,可以提供默認的實現(xiàn),因此可以不修改子類現(xiàn)有的代碼 | 如果往接口中添加新方法,則子類中需要實現(xiàn)該方法. |
父類的靜態(tài)方法能否被子類重寫
不能.子類繼承父類后,有相同的靜態(tài)方法和非靜態(tài),這是非靜態(tài)方法覆蓋父類中的方法(即方法重寫),父類的該靜態(tài)方法被隱藏(如果對象是父類則調(diào)用該隱藏的方法),另外子類可集成父類的靜態(tài)與非靜態(tài)方法,至于方法重載我覺得它其中一要素就是在同一類中,不能說父類中的什么方法與子類里的什么方法是方法重載的體現(xiàn).
什么是不可變對象
不可變對象指對象一旦被創(chuàng)建俯萌,狀態(tài)就不能再改變并村。任何修改都會創(chuàng)建一個新的對象,如 String她紫、Integer及其它包裝類窃爷。
能否創(chuàng)建一個包含可變對象的不可變對象?
當然可以創(chuàng)建一個包含可變對象的不可變對象的粤剧,你只需要謹慎一點,不要共享可變對象的引用就可以了长窄,如果需要變化時滔吠,就返回原對象的一個拷貝。最常見的例子就是對象中包含一個日期對象的引用.
java 創(chuàng)建對象的幾種方式
- 采用new
- 通過反射
- 采用clone
- 通過序列化機制
前2者都需要顯式地調(diào)用構造方法. 造成耦合性最高的恰好是第一種,因此你發(fā)現(xiàn)無論什么框架,只要涉及到解耦必先減少new的使用.
switch中能否使用string做參數(shù)
在idk 1.7之前,switch只能支持byte,short,char,int或者其對應的封裝類以及Enum類型挠日。從idk 1.7之后switch開始支持String.
Object中有哪些公共方法?
equals()
clone()
getClass()
notify(),notifyAll(),wait()
java當中的四種引用
強引用,軟引用,弱引用,虛引用.不同的引用類型主要體現(xiàn)在GC上:
- 強引用:如果一個對象具有強引用疮绷,它就不會被垃圾回收器回收。即使當前內(nèi)存空間不足嚣潜,JVM也不會回收它冬骚,而是拋出 OutOfMemoryError 錯誤,使程序異常終止。如果想中斷強引用和某個對象之間的關聯(lián)只冻,可以顯式地將引用賦值為null庇麦,這樣一來的話,JVM在合適的時間就會回收該對象
- 軟引用:在使用軟引用時喜德,如果內(nèi)存的空間足夠山橄,軟引用就能繼續(xù)被使用,而不會被垃圾回收器回收舍悯,只有在內(nèi)存不足時航棱,軟引用才會被垃圾回收器回收。
- 弱引用:具有弱引用的對象擁有的生命周期更短暫萌衬。因為當 JVM 進行垃圾回收饮醇,一旦發(fā)現(xiàn)弱引用對象,無論當前內(nèi)存空間是否充足奄薇,都會將弱引用回收驳阎。不過由于垃圾回收器是一個優(yōu)先級較低的線程,所以并不一定能迅速發(fā)現(xiàn)弱引用對象
- 虛引用:顧名思義馁蒂,就是形同虛設呵晚,如果一個對象僅持有虛引用,那么它相當于沒有引用沫屡,在任何時候都可能被垃圾回收器回收饵隙。
更多了解參見深入對象引用
WeakReference與SoftReference的區(qū)別?
這點在四種引用類型中已經(jīng)做了解釋,這里簡單說明一下即可:
雖然 WeakReference 與 SoftReference 都有利于提高 GC 和 內(nèi)存的效率,但是 WeakReference 沮脖,一旦失去最后一個強引用金矛,就會被 GC 回收,而軟引用雖然不能阻止被回收勺届,但是可以延遲到 JVM 內(nèi)存不足的時候驶俊。
為什么要有不同的引用類型
不像C語言,我們可以控制內(nèi)存的申請和釋放,在Java中有時候我們需要適當?shù)目刂茖ο蟊换厥盏臅r機,因此就誕生了不同的引用類型,可以說不同的引用類型實則是對GC回收時機不可控的妥協(xié).有以下幾個使用場景可以充分的說明:
- 利用軟引用和弱引用解決OOM問題:用一個HashMap來保存圖片的路徑和相應圖片對象關聯(lián)的軟引用之間的映射關系,在內(nèi)存不足時免姿,JVM會自動回收這些緩存圖片對象所占用的空間饼酿,從而有效地避免了OOM的問題.
- 通過軟引用實現(xiàn)Java對象的高速緩存:比如我們創(chuàng)建了一Person的類,如果每次需要查詢一個人的信息,哪怕是幾秒中之前剛剛查詢過的胚膊,都要重新構建一個實例故俐,這將引起大量Person對象的消耗,并且由于這些對象的生命周期相對較短,會引起多次GC影響性能。此時,通過軟引用和 HashMap 的結合可以構建高速緩存,提供性能.
java中==和eqauls()
的區(qū)別,equals()
和`hashcode的區(qū)別
==是運算符,用于比較兩個變量是否相等,而equals是Object類的方法,用于比較兩個對象是否相等.默認Object類的equals方法是比較兩個對象的地址,此時和==的結果一樣.換句話說:基本類型比較用==,比較的是他們的值.默認下,對象用==比較時,比較的是內(nèi)存地址,如果需要比較對象內(nèi)容,需要重寫equal方法
equals()
和hashcode()
的聯(lián)系
hashCode()
是Object類的一個方法,返回一個哈希值.如果兩個對象根據(jù)equal()方法比較相等,那么調(diào)用這兩個對象中任意一個對象的hashCode()方法必須產(chǎn)生相同的哈希值.
如果兩個對象根據(jù)eqaul()方法比較不相等,那么產(chǎn)生的哈希值不一定相等(碰撞的情況下還是會相等的.)
a.hashCode()有什么用?與a.equals(b)有什么關系
hashCode() 方法是相應對象整型的 hash 值紊婉。它常用于基于 hash 的集合類药版,如 Hashtable、HashMap喻犁、LinkedHashMap等等槽片。它與 equals() 方法關系特別緊密何缓。根據(jù) Java 規(guī)范,兩個使用 equal() 方法來判斷相等的對象筐乳,必須具有相同的 hashcode歌殃。
將對象放入到集合中時,首先判斷要放入對象的hashcode是否已經(jīng)在集合中存在,不存在則直接放入集合.如果hashcode相等,然后通過equal()方法判斷要放入對象與集合中的任意對象是否相等:如果equal()判斷不相等,直接將該元素放入集合中,否則不放入.
有沒有可能兩個不相等的對象有相同的hashcode
有可能,兩個不相等的對象可能會有相同的 hashcode 值蝙云,這就是為什么在 hashmap 中會有沖突氓皱。相等 hashcode 值的規(guī)定只是說如果兩個對象相等,必須有相同的hashcode 值勃刨,但是沒有關于不相等對象的任何規(guī)定波材。
可以在hashcode中使用隨機數(shù)字嗎?
不行,因為同一對象的 hashcode 值必須是相同的
“a==b”與a.equals(b)有什么區(qū)別
如果a 和b 都是對象身隐,則 a==b 是比較兩個對象的引用廷区,只有當 a 和 b 指向的是堆中的同一個對象才會返回 true,而 a.equals(b) 是進行邏輯比較贾铝,所以通常需要重寫該方法來提供邏輯一致性的比較隙轻。例如,String 類重寫 equals() 方法垢揩,所以可以用于兩個不同對象玖绿,但是包含的字母相同的比較。
3*0.1==0.3
返回值是什么
false叁巨,因為有些浮點數(shù)不能完全精確的表示出來斑匪。
a=a+b與a+=b有什么區(qū)別嗎?
隱式的將加操作的結果類型強制轉換為持有結果的類型。如果兩這個整型相加锋勺,如 byte蚀瘸、short 或者 int,首先會將它們提升到 int 類型庶橱,然后在執(zhí)行加法操作贮勃。如果加法操作的結果比 a 的最大值要大,則 a+b 會出現(xiàn)編譯錯誤苏章,但是 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 就會編譯出錯)
內(nèi)部類的作用
內(nèi)部類可以用多個實例,每個實例都有自己的狀態(tài)信息,并且與其他外圍對象的信息相互獨立.在單個外圍類當中,可以讓多個內(nèi)部類以不同的方式實現(xiàn)同一接口,或者繼承同一個類.創(chuàng)建內(nèi)部類對象的時刻病不依賴于外部類對象的創(chuàng)建.內(nèi)部類并沒有令人疑惑的”is-a”關系,它就像是一個獨立的實體.
內(nèi)部類提供了更好的封裝,除了該外圍類,其他類都不能訪問
final,finalize和finally的不同之處
final 是一個修飾符,可以修飾變量显蝌、方法和類预伺。如果 final 修飾變量订咸,意味著該變量的值在初始化后不能被改變。finalize 方法是在對象被回收之前調(diào)用的方法酬诀,給對象自己最后一個復活的機會脏嚷,但是什么時候調(diào)用 finalize 沒有保證。finally 是一個關鍵字瞒御,與 try 和 catch 一起用于異常的處理父叙。finally 塊一定會被執(zhí)行,無論在 try 塊中是否有發(fā)生異常肴裙。
clone()是哪個類型的方法?
java.lang.Cloneable 是一個標示性接口趾唱,不包含任何方法,clone 方法在 object 類中定義蜻懦。并且需要知道 clone() 方法是一個本地方法甜癞,這意味著它是由 c 或 c++ 或 其他本地語言實現(xiàn)的。
深拷貝和淺拷貝的區(qū)別是什么?
淺拷貝:被復制對象的所有變量都含有與原來的對象相同的值宛乃,而所有的對其他對象的引用仍然指向原來的對象悠咱。換言之,淺拷貝僅僅復制所考慮的對象征炼,而不復制它所引用的對象析既。
深拷貝:被復制對象的所有變量都含有與原來的對象相同的值,而那些引用其他對象的變量將指向被復制過的新對象柒室,而不再是原有的那些被引用的對象渡贾。換言之,深拷貝把要復制的對象所引用的對象都復制了一遍雄右。
數(shù)據(jù)類型相關
java中int char,long各占多少字節(jié)?
類型 | 位數(shù) | 字節(jié)數(shù) |
---|---|---|
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的區(qū)別
Integer是int的包裝類型,在拆箱和裝箱中,而知自動轉換.int是基本類型,直接存數(shù)值肃廓,而integer是對象智厌,用一個引用指向這個對象.
int 和Integer誰占用的內(nèi)存更多?
Integer 對象會占用更多的內(nèi)存。Integer是一個對象盲赊,需要存儲對象的元數(shù)據(jù)铣鹏。但是 int 是一個原始類型的數(shù)據(jù),所以占用的空間更少哀蘑。
String,StringBuffer和StringBuilder區(qū)別
String是字符串常量,final修飾;StringBuffer字符串變量(線程安全);
StringBuilder 字符串變量(線程不安全).
String和StringBuffer
String和StringBuffer主要區(qū)別是性能:String是不可變對象,每次對String類型進行操作都等同于產(chǎn)生了一個新的String對象,然后指向新的String對象.所以盡量不在對String進行大量的拼接操作,否則會產(chǎn)生很多臨時對象,導致GC開始工作,影響系統(tǒng)性能.
StringBuffer是對對象本身操作,而不是產(chǎn)生新的對象,因此在通常在有大量拼接的情況下我們建議使用StringBuffer.
但是需要注意現(xiàn)在JVM會對String拼接做一定的優(yōu)化:
String s=“This is only ”+”simple”+”test”
會被虛擬機直接優(yōu)化成String s=“This is only simple test”
,此時就不存在拼接過程.
StringBuffer和StringBuilder
StringBuffer是線程安全的可變字符串,其內(nèi)部實現(xiàn)是可變數(shù)組.StringBuilder是java 5.0新增的,其功能和StringBuffer類似,但是非線程安全.因此,在沒有多線程問題的前提下,使用StringBuilder會取得更好的性能.
什么是編譯器常量?使用它有什么風險?
公共靜態(tài)不可變(public static final )變量也就是我們所說的編譯期常量诚卸,這里的 public 可選的葵第。實際上這些變量在編譯時會被替換掉,因為編譯器知道這些變量的值合溺,并且知道這些變量在運行時不能改變卒密。這種方式存在的一個問題是你使用了一個內(nèi)部的或第三方庫中的公有編譯時常量,但是這個值后面被其他人改變了棠赛,但是你的客戶端仍然在使用老的值哮奇,甚至你已經(jīng)部署了一個新的jar。為了避免這種情況恭朗,當你在更新依賴 JAR 文件時屏镊,確保重新編譯你的程序。
java當中使用什么類型表示價格比較好?
如果不是特別關心內(nèi)存和性能的話痰腮,使用BigDecimal而芥,否則使用預定義精度的 double 類型。
如何將byte轉為String
可以使用 String 接收 byte[] 參數(shù)的構造器來進行轉換膀值,需要注意的點是要使用的正確的編碼棍丐,否則會使用平臺默認編碼,這個編碼可能跟原來的編碼相同沧踏,也可能不同歌逢。
可以將int強轉為byte類型么?會產(chǎn)生什么問題?
我們可以做強制轉換,但是Java中int是32位的而byte是8 位的翘狱,所以,如果強制轉化int類型的高24位將會被丟棄秘案,byte 類型的范圍是從-128.到128
關于垃圾回收
你知道哪些垃圾回收算法?
垃圾回收從理論上非常容易理解,具體的方法有以下幾種:
- 標記-清除
- 標記-復制
- 標記-整理
- 分代回收
更詳細的內(nèi)容參見深入理解垃圾回收算法
如何判斷一個對象是否應該被回收
這就是所謂的對象存活性判斷,常用的方法有兩種:1.引用計數(shù)法;2:對象可達性分析.由于引用計數(shù)法存在互相引用導致無法進行GC的問題,所以目前JVM虛擬機多使用對象可達性分析算法.
簡單的解釋一下垃圾回收
Java 垃圾回收機制最基本的做法是分代回收。內(nèi)存中的區(qū)域被劃分成不同的世代潦匈,對象根據(jù)其存活的時間被保存在對應世代的區(qū)域中阱高。一般的實現(xiàn)是劃分成3個世代:年輕、年老和永久茬缩。內(nèi)存的分配是發(fā)生在年輕世代中的赤惊。當一個對象存活時間足夠長的時候,它就會被復制到年老世代中凰锡。對于不同的世代可以使用不同的垃圾回收算法未舟。進行世代劃分的出發(fā)點是對應用中對象存活時間進行研究之后得出的統(tǒng)計規(guī)律。一般來說掂为,一個應用中的大部分對象的存活時間都很短裕膀。比如局部變量的存活時間就只在方法的執(zhí)行過程中∮禄基于這一點昼扛,對于年輕世代的垃圾回收算法就可以很有針對性.
調(diào)用System.gc()會發(fā)生什么?
通知GC開始工作,但是GC真正開始的時間不確定.
進程,線程相關
說說進程,線程,協(xié)程之間的區(qū)別
簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程.進程在執(zhí)行過程中擁有獨立的內(nèi)存單元,而多個線程共享內(nèi)存資源,減少切換次數(shù),從而效率更高.線程是進程的一個實體,是cpu調(diào)度和分派的基本單位,是比程序更小的能獨立運行的基本單位.同一進程中的多個線程之間可以并發(fā)執(zhí)行.
你了解守護線程嗎?它和非守護線程有什么區(qū)別
程序運行完畢,jvm會等待非守護線程完成后關閉,但是jvm不會等待守護線程.守護線程最典型的例子就是GC線程
什么是多線程上下文切換
多線程的上下文切換是指CPU控制權由一個已經(jīng)正在運行的線程切換到另外一個就緒并等待獲取CPU執(zhí)行權的線程的過程。
創(chuàng)建兩種線程的方式?他們有什么區(qū)別?
通過實現(xiàn)java.lang.Runnable或者通過擴展java.lang.Thread類.相比擴展Thread,實現(xiàn)Runnable接口可能更優(yōu).原因有二:
- Java不支持多繼承.因此擴展Thread類就代表這個子類不能擴展其他類.而實現(xiàn)Runnable接口的類還可能擴展另一個類.
- 類可能只要求可執(zhí)行即可,因此集成整個Thread類的開銷過大.
Runnable和Callable的區(qū)別
Runnable接口中的run()方法的返回值是void智绸,它做的事情只是純粹地去執(zhí)行run()方法中的代碼而已野揪;Callable接口中的call()方法是有返回值的,是一個泛型瞧栗,和Future斯稳、FutureTask配合可以用來獲取異步執(zhí)行的結果。
這其實是很有用的一個特性迹恐,因為多線程相比單線程更難挣惰、更復雜的一個重要原因就是因為多線程充滿著未知性,某條線程是否執(zhí)行了殴边?某條線程執(zhí)行了多久憎茂?某條線程執(zhí)行的時候我們期望的數(shù)據(jù)是否已經(jīng)賦值完畢?無法得知锤岸,我們能做的只是等待這條多線程的任務執(zhí)行完畢而已竖幔。而Callable+Future/FutureTask卻可以獲取多線程運行的結果,可以在等待時間太長沒獲取到需要的數(shù)據(jù)的情況下取消該線程的任務是偷,真的是非常有用拳氢。
什么導致線程阻塞
阻塞指的是暫停一個線程的執(zhí)行以等待某個條件發(fā)生(如某資源就緒),學過操作系統(tǒng)的同學對它一定已經(jīng)很熟悉了蛋铆。Java 提供了大量方法來支持阻塞馋评,下面讓我們逐一分析。
方法 | 說明 |
---|---|
sleep() | sleep() 允許 指定以毫秒為單位的一段時間作為參數(shù)刺啦,它使得線程在指定的時間內(nèi)進入阻塞狀態(tài)留特,不能得到CPU 時間,指定的時間一過玛瘸,線程重新進入可執(zhí)行狀態(tài)蜕青。 典型地,sleep() 被用在等待某個資源就緒的情形:測試發(fā)現(xiàn)條件不滿足后捧韵,讓線程阻塞一段時間后重新測試市咆,直到條件滿足為止 |
suspend() 和 resume() | 兩個方法配套使用,suspend()使得線程進入阻塞狀態(tài)再来,并且不會自動恢復蒙兰,必須其對應的resume() 被調(diào)用,才能使得線程重新進入可執(zhí)行狀態(tài)芒篷。典型地搜变,suspend() 和 resume() 被用在等待另一個線程產(chǎn)生的結果的情形:測試發(fā)現(xiàn)結果還沒有產(chǎn)生后,讓線程阻塞针炉,另一個線程產(chǎn)生了結果后挠他,調(diào)用 resume() 使其恢復。 |
yield() | yield() 使得線程放棄當前分得的 CPU 時間篡帕,但是不使線程阻塞殖侵,即線程仍處于可執(zhí)行狀態(tài)贸呢,隨時可能再次分得 CPU 時間。調(diào)用 yield() 的效果等價于調(diào)度程序認為該線程已執(zhí)行了足夠的時間從而轉到另一個線程 |
wait() 和 notify() | 兩個方法配套使用拢军,wait() 使得線程進入阻塞狀態(tài)楞陷,它有兩種形式,一種允許 指定以毫秒為單位的一段時間作為參數(shù)茉唉,另一種沒有參數(shù)固蛾,前者當對應的 notify() 被調(diào)用或者超出指定時間時線程重新進入可執(zhí)行狀態(tài),后者則必須對應的 notify() 被調(diào)用. |
wait(),notify()和suspend(),resume()之間的區(qū)別
初看起來它們與 suspend() 和 resume() 方法對沒有什么分別度陆,但是事實上它們是截然不同的艾凯。區(qū)別的核心在于,前面敘述的所有方法懂傀,阻塞時都不會釋放占用的鎖(如果占用了的話)趾诗,而這一對方法則相反。上述的核心區(qū)別導致了一系列的細節(jié)上的區(qū)別鸿竖。
首先沧竟,前面敘述的所有方法都隸屬于 Thread 類,但是這一對卻直接隸屬于 Object 類缚忧,也就是說悟泵,所有對象都擁有這一對方法。初看起來這十分不可思議闪水,但是實際上卻是很自然的糕非,因為這一對方法阻塞時要釋放占用的鎖,而鎖是任何對象都具有的球榆,調(diào)用任意對象的 wait() 方法導致線程阻塞朽肥,并且該對象上的鎖被釋放。而調(diào)用 任意對象的notify()方法則導致因調(diào)用該對象的 wait() 方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)持钉。
其次衡招,前面敘述的所有方法都可在任何位置調(diào)用,但是這一對方法卻必須在 synchronized 方法或塊中調(diào)用每强,理由也很簡單始腾,只有在synchronized 方法或塊中當前線程才占有鎖,才有鎖可以釋放空执。同樣的道理浪箭,調(diào)用這一對方法的對象上的鎖必須為當前線程所擁有,這樣才有鎖可以釋放辨绊。因此奶栖,這一對方法調(diào)用必須放置在這樣的 synchronized 方法或塊中,該方法或塊的上鎖對象就是調(diào)用這一對方法的對象。若不滿足這一條件宣鄙,則程序雖然仍能編譯袍镀,但在運行時會出現(xiàn)IllegalMonitorStateException 異常。
wait() 和 notify() 方法的上述特性決定了它們經(jīng)常和synchronized 方法或塊一起使用冻晤,將它們和操作系統(tǒng)的進程間通信機制作一個比較就會發(fā)現(xiàn)它們的相似性:synchronized方法或塊提供了類似于操作系統(tǒng)原語的功能流椒,它們的執(zhí)行不會受到多線程機制的干擾,而這一對方法則相當于 block 和wakeup 原語(這一對方法均聲明為 synchronized)明也。它們的結合使得我們可以實現(xiàn)操作系統(tǒng)上一系列精妙的進程間通信的算法(如信號量算法),并用于解決各種復雜的線程間通信問題惯裕。
關于 wait() 和 notify() 方法最后再說明兩點:
第一:調(diào)用 notify() 方法導致解除阻塞的線程是從因調(diào)用該對象的 wait() 方法而阻塞的線程中隨機選取的温数,我們無法預料哪一個線程將會被選擇,所以編程時要特別小心蜻势,避免因這種不確定性而產(chǎn)生問題撑刺。
第二:除了 notify(),還有一個方法 notifyAll() 也可起到類似作用握玛,唯一的區(qū)別在于够傍,調(diào)用 notifyAll() 方法將把因調(diào)用該對象的 wait() 方法而阻塞的所有線程一次性全部解除阻塞。當然挠铲,只有獲得鎖的那一個線程才能進入可執(zhí)行狀態(tài)冕屯。
談到阻塞,就不能不談一談死鎖拂苹,略一分析就能發(fā)現(xiàn)安聘,suspend() 方法和不指定超時期限的 wait() 方法的調(diào)用都可能產(chǎn)生死鎖。遺憾的是瓢棒,Java 并不在語言級別上支持死鎖的避免浴韭,我們在編程中必須小心地避免死鎖。
以上我們對 Java 中實現(xiàn)線程阻塞的各種方法作了一番分析脯宿,我們重點分析了 wait() 和 notify() 方法念颈,因為它們的功能最強大,使用也最靈活连霉,但是這也導致了它們的效率較低榴芳,較容易出錯。實際使用中我們應該靈活使用各種方法窘面,以便更好地達到我們的目的翠语。
為什么wait()方法和notify()/notifyAll()方法要在同步塊中被調(diào)用
這是JDK強制的,wait()方法和notify()/notifyAll()方法在調(diào)用前都必須先獲得對象的鎖
wait()方法和notify()/notifyAll()方法在放棄對象監(jiān)視器時有什么區(qū)別
wait()方法和notify()/notifyAll()方法在放棄對象監(jiān)視器的時候的區(qū)別在于:wait()方法立即釋放對象監(jiān)視器财边,notify()/notifyAll()方法則會等待線程剩余代碼執(zhí)行完畢才會放棄對象監(jiān)視器肌括。
wait()與sleep()的區(qū)別
關于這兩者已經(jīng)在上面進行詳細的說明,這里就做個概括好了:
- sleep()來自Thread類,和wait()來自Object類.調(diào)用sleep()方法的過程中,線程不會釋放對象鎖谍夭。而 調(diào)用 wait 方法線程會釋放對象鎖
- sleep()睡眠后不出讓系統(tǒng)資源黑滴,wait讓其他線程可以占用CPU
- sleep(milliseconds)需要指定一個睡眠時間,時間一到會自動喚醒.而wait()需要配合notify()或者notifyAll()使用
synchronized和ReentrantLock的區(qū)別
synchronized是和if紧索、else袁辈、for、while一樣的關鍵字珠漂,ReentrantLock是類晚缩,這是二者的本質區(qū)別。既然ReentrantLock是類媳危,那么它就提供了比synchronized更多更靈活的特性荞彼,可以被繼承、可以有方法待笑、可以有各種各樣的類變量鸣皂,ReentrantLock比synchronized的擴展性體現(xiàn)在幾點上:
(1)ReentrantLock可以對獲取鎖的等待時間進行設置,這樣就避免了死鎖
(2)ReentrantLock可以獲取各種鎖的信息
(3)ReentrantLock可以靈活地實現(xiàn)多路通知
另外暮蹂,二者的鎖機制其實也是不一樣的:ReentrantLock底層調(diào)用的是Unsafe的park方法加鎖寞缝,synchronized操作的應該是對象頭中mark word.
FutureTask是什么
這個其實前面有提到過,F(xiàn)utureTask表示一個異步運算的任務仰泻。FutureTask里面可以傳入一個Callable的具體實現(xiàn)類荆陆,可以對這個異步運算的任務的結果進行等待獲取、判斷是否已經(jīng)完成集侯、取消任務等操作慎宾。當然,由于FutureTask也是Runnable接口的實現(xiàn)類浅悉,所以FutureTask也可以放入線程池中趟据。
一個線程如果出現(xiàn)了運行時異常怎么辦?
如果這個異常沒有被捕獲的話,這個線程就停止執(zhí)行了术健。另外重要的一點是:如果這個線程持有某個某個對象的監(jiān)視器汹碱,那么這個對象監(jiān)視器會被立即釋放
如何在兩個線程間共享數(shù)據(jù)
通過在線程之間共享對象就可以了,然后通過wait/notify/notifyAll荞估、await/signal/signalAll進行喚起和等待咳促,比方說阻塞隊列BlockingQueue就是為線程之間共享數(shù)據(jù)而設計的
如何正確的使用wait()?使用if還是while?
wait() 方法應該在循環(huán)調(diào)用,因為當線程獲取到 CPU 開始執(zhí)行的時候勘伺,其他條件可能還沒有滿足跪腹,所以在處理前,循環(huán)檢測條件是否滿足會更好飞醉。下面是一段標準的使用 wait 和 notify 方法的代碼:
synchronized (obj) {
while (condition does not hold)
obj.wait(); // (Releases lock, and reacquires on wakeup)
... // Perform action appropriate to condition
}
什么是線程局部變量
線程局部變量是局限于線程內(nèi)部的變量冲茸,屬于線程自身所有,不在多個線程間共享。Java提供ThreadLocal類來支持線程局部變量轴术,是一種實現(xiàn)線程安全的方式难衰。但是在管理環(huán)境下(如 web 服務器)使用線程局部變量的時候要特別小心,在這種情況下逗栽,工作線程的生命周期比任何應用變量的生命周期都要長盖袭。任何線程局部變量一旦在工作完成后沒有釋放,Java 應用就存在內(nèi)存泄露的風險彼宠。
ThreadLoal的作用是什么?
簡單說ThreadLocal就是一種以空間換時間的做法在每個Thread里面維護了一個ThreadLocal.ThreadLocalMap把數(shù)據(jù)進行隔離鳄虱,數(shù)據(jù)不共享,自然就沒有線程安全方面的問題了.
生產(chǎn)者消費者模型的作用是什么?
(1)通過平衡生產(chǎn)者的生產(chǎn)能力和消費者的消費能力來提升整個系統(tǒng)的運行效率凭峡,這是生產(chǎn)者消費者模型最重要的作用
(2)解耦醇蝴,這是生產(chǎn)者消費者模型附帶的作用,解耦意味著生產(chǎn)者和消費者之間的聯(lián)系少想罕,聯(lián)系越少越可以獨自發(fā)展而不需要收到相互的制約
寫一個生產(chǎn)者-消費者隊列
可以通過阻塞隊列實現(xiàn),也可以通過wait-notify來實現(xiàn).
使用阻塞隊列來實現(xiàn)
//消費者
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來實現(xiàn)
該種方式應該最經(jīng)典,這里就不做說明了
ConcurrentHashMap的并發(fā)度是什么?
ConcurrentHashMap的并發(fā)度就是segment的大小,默認為16霉涨,這意味著最多同時可以有16條線程操作ConcurrentHashMap按价,這也是ConcurrentHashMap對Hashtable的最大優(yōu)勢,任何情況下笙瑟,Hashtable能同時有兩條線程獲取Hashtable中的數(shù)據(jù)嗎楼镐?
CyclicBarrier和CountDownLatch區(qū)別
這兩個類非常類似,都在java.util.concurrent下往枷,都可以用來表示代碼運行到某個點上框产,二者的區(qū)別在于:
- CyclicBarrier的某個線程運行到某個點上之后,該線程即停止運行错洁,直到所有的線程都到達了這個點秉宿,所有線程才重新運行;CountDownLatch則不是屯碴,某線程運行到某個點上之后描睦,只是給某個數(shù)值-1而已,該線程繼續(xù)運行
- CyclicBarrier只能喚起一個任務导而,CountDownLatch可以喚起多個任務
- CyclicBarrier可重用忱叭,CountDownLatch不可重用,計數(shù)值為0該CountDownLatch就不可再用了
java中的++操作符線程安全么?
不是線程安全的操作今艺。它涉及到多個指令韵丑,如讀取變量值,增加虚缎,然后存儲回內(nèi)存蝎宇,這個過程可能會出現(xiàn)多個線程交差
你有哪些多線程開發(fā)良好的實踐?
- 給線程命名
- 最小化同步范圍
- 優(yōu)先使用volatile
- 盡可能使用更高層次的并發(fā)工具而非wait和notify()來實現(xiàn)線程通信,如BlockingQueue,Semeaphore
- 優(yōu)先使用并發(fā)容器而非同步容器.
- 考慮使用線程池
關于volatile關鍵字
可以創(chuàng)建Volatile數(shù)組嗎?
Java 中可以創(chuàng)建 volatile類型數(shù)組,不過只是一個指向數(shù)組的引用粮呢,而不是整個數(shù)組。如果改變引用指向的數(shù)組享幽,將會受到volatile 的保護,但是如果多個線程同時改變數(shù)組的元素拾弃,volatile標示符就不能起到之前的保護作用了
volatile能使得一個非原子操作變成原子操作嗎?
一個典型的例子是在類中有一個 long 類型的成員變量值桩。如果你知道該成員變量會被多個線程訪問,如計數(shù)器豪椿、價格等奔坟,你最好是將其設置為 volatile。為什么搭盾?因為 Java 中讀取 long 類型變量不是原子的咳秉,需要分成兩步,如果一個線程正在修改該 long 變量的值鸯隅,另一個線程可能只能看到該值的一半(前 32 位)澜建。但是對一個 volatile 型的 long 或 double 變量的讀寫是原子。
一種實踐是用 volatile 修飾 long 和 double 變量蝌以,使其能按原子類型來讀寫炕舵。double 和 long 都是64位寬,因此對這兩種類型的讀是分為兩部分的跟畅,第一次讀取第一個 32 位咽筋,然后再讀剩下的 32 位,這個過程不是原子的徊件,但 Java 中 volatile 型的 long 或 double 變量的讀寫是原子的奸攻。volatile 修復符的另一個作用是提供內(nèi)存屏障(memory barrier),例如在分布式框架中的應用虱痕。簡單的說睹耐,就是當你寫一個 volatile 變量之前,Java 內(nèi)存模型會插入一個寫屏障(write barrier)部翘,讀一個 volatile 變量之前疏橄,會插入一個讀屏障(read barrier)。意思就是說略就,在你寫一個 volatile 域時捎迫,能保證任何線程都能看到你寫的值,同時表牢,在寫之前窄绒,也能保證任何數(shù)值的更新對所有線程是可見的,因為內(nèi)存屏障會將其他所有寫的值更新到緩存崔兴。
volatile類型變量提供什么保證?
volatile 主要有兩方面的作用:1.避免指令重排2.可見性保證.例如彰导,JVM 或者 JIT為了獲得更好的性能會對語句重排序蛔翅,但是 volatile 類型變量即使在沒有同步塊的情況下賦值也不會與其他語句重排序。 volatile 提供 happens-before 的保證位谋,確保一個線程的修改能對其他線程是可見的山析。某些情況下,volatile 還能提供原子性掏父,如讀 64 位數(shù)據(jù)類型笋轨,像 long 和 double 都不是原子的(低32位和高32位),但 volatile 類型的 double 和 long 就是原子的.
關于集合
Java中的集合及其繼承關系
關于集合的體系是每個人都應該爛熟于心的,尤其是對我們經(jīng)常使用的List,Map的原理更該如此.這里我們看這張圖即可:
更多內(nèi)容可見集合類總結
poll()方法和remove()方法區(qū)別?
poll() 和 remove() 都是從隊列中取出一個元素赊淑,但是 poll() 在獲取元素失敗的時候會返回空爵政,但是 remove() 失敗的時候會拋出異常。
LinkedHashMap和PriorityQueue的區(qū)別
PriorityQueue 是一個優(yōu)先級隊列,保證最高或者最低優(yōu)先級的的元素總是在隊列頭部陶缺,但是 LinkedHashMap 維持的順序是元素插入的順序钾挟。當遍歷一個 PriorityQueue 時,沒有任何順序保證饱岸,但是 LinkedHashMap 課保證遍歷順序是元素插入的順序掺出。
WeakHashMap與HashMap的區(qū)別是什么?
WeakHashMap 的工作與正常的 HashMap 類似,但是使用弱引用作為 key苫费,意思就是當 key 對象沒有任何引用時汤锨,key/value 將會被回收。
ArrayList和LinkedList的區(qū)別?
最明顯的區(qū)別是 ArrrayList底層的數(shù)據(jù)結構是數(shù)組黍衙,支持隨機訪問,而 LinkedList 的底層數(shù)據(jù)結構是雙向循環(huán)鏈表荠诬,不支持隨機訪問琅翻。使用下標訪問一個元素,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的區(qū)別?
Comparable 接口用于定義對象的自然順序,而 comparator 通常用于定義用戶定制的順序有决。Comparable 總是只有一個闸拿,但是可以有多個 comparator 來定義對象的順序。
如何實現(xiàn)集合排序?
你可以使用有序集合书幕,如 TreeSet 或 TreeMap新荤,你也可以使用有順序的的集合,如 list台汇,然后通過 Collections.sort() 來排序苛骨。
如何打印數(shù)組內(nèi)容
你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法來打印數(shù)組篱瞎。由于數(shù)組沒有實現(xiàn) toString() 方法,所以如果將數(shù)組傳遞給 System.out.println() 方法痒芝,將無法打印出數(shù)組的內(nèi)容俐筋,但是 Arrays.toString() 可以打印每個元素。
LinkedList的是單向鏈表還是雙向?
雙向循環(huán)列表,具體實現(xiàn)自行查閱源碼.
TreeMap是實現(xiàn)原理
采用紅黑樹實現(xiàn),具體實現(xiàn)自行查閱源碼.
遍歷ArrayList時如何正確移除一個元素
該問題的關鍵在于面試者使用的是 ArrayList 的 remove() 還是 Iterator 的 remove()方法严衬。這有一段示例代碼澄者,是使用正確的方式來實現(xiàn)在遍歷的過程中移除元素,而不會出現(xiàn) ConcurrentModificationException 異常的示例代碼瞳步。
什么是ArrayMap?它和HashMap有什么區(qū)別?
ArrayMap是Android SDK中提供的,非Android開發(fā)者可以略過.
ArrayMap是用兩個數(shù)組來模擬map,更少的內(nèi)存占用空間,更高的效率.
具體參考這篇文章:ArrayMap VS HashMap
HashMap的實現(xiàn)原理
1 HashMap概述: HashMap是基于哈希表的Map接口的非同步實現(xiàn)闷哆。此實現(xiàn)提供所有可選的映射操作,并允許使用null值和null鍵单起。此類不保證映射的順序抱怔,特別是它不保證該順序恒久不變。
2 HashMap的數(shù)據(jù)結構: 在java編程語言中嘀倒,最基本的結構就是兩種屈留,一個是數(shù)組,另外一個是模擬指針(引用)测蘑,所有的數(shù)據(jù)結構都可以用這兩個基本結構來構造的灌危,HashMap也不例外。HashMap實際上是一個“鏈表散列”的數(shù)據(jù)結構碳胳,即數(shù)組和鏈表的結合體勇蝙。
當我們往Hashmap中put元素時,首先根據(jù)key的hashcode重新計算hash值,根絕hash值得到這個元素在數(shù)組中的位置(下標),如果該數(shù)組在該位置上已經(jīng)存放了其他元素,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放入鏈尾.如果數(shù)組中該位置沒有元素,就直接將該元素放到數(shù)組的該位置上.
你了解Fail-Fast機制嗎
Fail-Fast即我們常說的快速失敗,更多內(nèi)容參看fail-fast機制
關于日期
SimpleDateFormat是線程安全的嗎?
非常不幸,DateFormat 的所有實現(xiàn)挨约,包括 SimpleDateFormat 都不是線程安全的味混,因此你不應該在多線程序中使用,除非是在對外線程安全的環(huán)境中使用诫惭,如 將 SimpleDateFormat 限制在 ThreadLocal 中翁锡。如果你不這么做,在解析或者格式化日期的時候夕土,可能會獲取到一個不正確的結果馆衔。因此,從日期怨绣、時間處理的所有實踐來說角溃,我強力推薦 joda-time 庫。
如何格式化日期?
Java 中篮撑,可以使用 SimpleDateFormat 類或者 joda-time 庫來格式日期开镣。DateFormat 類允許你使用多種流行的格式來格式化日期。參見答案中的示例代碼咽扇,代碼中演示了將日期格式化成不同的格式邪财,如 dd-MM-yyyy 或 ddMMyyyy陕壹。
關于異常
簡單描述java異常體系
相比沒有人不了解異常體系,關于異常體系的更多信息可以見:白話異常機制
什么是異常鏈
詳情直接參見白話異常機制,不做解釋了.
throw和throws的區(qū)別
throw用于主動拋出java.lang.Throwable 類的一個實例化對象,意思是說你可以通過關鍵字 throw 拋出一個 Error 或者 一個Exception树埠,如:throw new IllegalArgumentException(“size must be multiple of 2″)
,
而throws 的作用是作為方法聲明和簽名的一部分糠馆,方法被拋出相應的異常以便調(diào)用者能處理。Java 中怎憋,任何未處理的受檢查異常強制在 throws 子句中聲明又碌。
關于序列化
Java 中,Serializable 與 Externalizable 的區(qū)別
Serializable 接口是一個序列化 Java 類的接口绊袋,以便于它們可以在網(wǎng)絡上傳輸或者可以將它們的狀態(tài)保存在磁盤上毕匀,是 JVM 內(nèi)嵌的默認序列化方式,成本高癌别、脆弱而且不安全皂岔。Externalizable 允許你控制整個序列化過程,指定特定的二進制格式展姐,增加安全機制躁垛。
關于JVM
JVM特性
平臺無關性.
Java語言的一個非常重要的特點就是與平臺的無關性。而使用Java虛擬機是實現(xiàn)這一特點的關鍵圾笨。一般的高級語言如果要在不同的平臺上運行教馆,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機后擂达,Java語言在不同平臺上運行時不需要重新編譯土铺。Java語言使用模式Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼)板鬓,就可以在多種平臺上不加修改地運行悲敷。Java虛擬機在執(zhí)行字節(jié)碼時,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行穗熬。
簡單解釋一下類加載器
有關類加載器一般會問你四種類加載器的應用場景以及雙親委派模型,更多的內(nèi)容參看深入理解JVM加載器
簡述堆和棧的區(qū)別
VM 中堆和棧屬于不同的內(nèi)存區(qū)域镀迂,使用目的也不同丁溅。棧常用于保存方法幀和局部變量唤蔗,而對象總是在堆上分配。棧通常都比堆小窟赏,也不會在多個線程之間共享妓柜,而堆被整個 JVM 的所有線程共享。
簡述JVM內(nèi)存分配
- 基本數(shù)據(jù)類型比變量和對象的引用都是在棧分配的
- 堆內(nèi)存用來存放由new創(chuàng)建的對象和數(shù)組
- 類變量(static修飾的變量)涯穷,程序在一加載的時候就在堆中為類變量分配內(nèi)存棍掐,堆中的內(nèi)存地址存放在棧中
- 實例變量:當你使用java關鍵字new的時候,系統(tǒng)在堆中開辟并不一定是連續(xù)的空間分配給變量拷况,是根據(jù)零散的堆內(nèi)存地址作煌,通過哈希算法換算為一長串數(shù)字以表征這個變量在堆中的"物理位置”,實例變量的生命周期--當實例變量的引用丟失后掘殴,將被GC(垃圾回收器)列入可回收“名單”中,但并不是馬上就釋放堆中內(nèi)存
- 局部變量: 由聲明在某方法粟誓,或某代碼段里(比如for循環(huán))奏寨,執(zhí)行到它的時候在棧中開辟內(nèi)存,當局部變量一但脫離作用域鹰服,內(nèi)存立即釋放
其他
java當中采用的是大端還是小端?
XML解析的幾種方式和特點
DOM,SAX,PULL三種解析方式:
- DOM:消耗內(nèi)存:先把xml文檔都讀到內(nèi)存中病瞳,然后再用DOM API來訪問樹形結構,并獲取數(shù)據(jù)悲酷。這個寫起來很簡單套菜,但是很消耗內(nèi)存。要是數(shù)據(jù)過大设易,手機不夠牛逼逗柴,可能手機直接死機
- SAX:解析效率高,占用內(nèi)存少亡嫌,基于事件驅動的:更加簡單地說就是對文檔進行順序掃描嚎于,當掃描到文檔(document)開始與結束、元素(element)開始與結束挟冠、文檔(document)結束等地方時通知事件處理函數(shù)于购,由事件處理函數(shù)做相應動作,然后繼續(xù)同樣的掃描知染,直至文檔結束肋僧。
- PULL:與 SAX 類似,也是基于事件驅動控淡,我們可以調(diào)用它的next()方法嫌吠,來獲取下一個解析事件(就是開始文檔,結束文檔掺炭,開始標簽辫诅,結束標簽),當處于某個元素時可以調(diào)用XmlPullParser的getAttributte()方法來獲取屬性的值涧狮,也可調(diào)用它的nextText()獲取本節(jié)點的值炕矮。
JDK 1.7特性
然 JDK 1.7 不像 JDK 5 和 8 一樣的大版本,但是者冤,還是有很多新的特性肤视,如 try-with-resource 語句,這樣你在使用流或者資源的時候涉枫,就不需要手動關閉邢滑,Java 會自動關閉。Fork-Join 池某種程度上實現(xiàn) Java 版的 Map-reduce愿汰。允許 Switch 中有 String 變量和文本困后。菱形操作符(<>)用于類型推斷乐纸,不再需要在變量聲明的右邊申明泛型,因此可以寫出可讀寫更強摇予、更簡潔的代碼
JDK 1.8特性
java 8 在 Java 歷史上是一個開創(chuàng)新的版本锯仪,下面 JDK 8 中 5 個主要的特性:
Lambda 表達式,允許像對象一樣傳遞匿名函數(shù)
Stream API趾盐,充分利用現(xiàn)代多核 CPU庶喜,可以寫出很簡潔的代碼
Date 與 Time API,最終救鲤,有一個穩(wěn)定久窟、簡單的日期和時間庫可供你使用
擴展方法,現(xiàn)在本缠,接口中可以有靜態(tài)斥扛、默認方法。
重復注解丹锹,現(xiàn)在你可以將相同的注解在同一類型上使用多次稀颁。
Maven和ANT有什么區(qū)別?
雖然兩者都是構建工具,都用于創(chuàng)建 Java 應用楣黍,但是 Maven 做的事情更多匾灶,在基于“約定優(yōu)于配置”的概念下,提供標準的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 開發(fā)的、準備應聘 Java web 開發(fā)職位的人還是同樣有用的骑脱,因為所有的 Java 面試菜枷,開始的問題都是 Java 基礎和 JDK API 相關的。如果你認為我這里有任何應該在這份列表中而被我遺漏了的 Java 流行的問題叁丧,你可以自由的給我建議啤誊。我的目的是從最近的面試中創(chuàng)建一份最新的、最優(yōu)的 Java 面試問題列表拥娄。
JDBC最佳實踐
- 優(yōu)先使用批量操作來插入和更新數(shù)據(jù)
- 使用PreparedStatement來避免SQL漏洞
- 使用數(shù)據(jù)連接池
- 通過列名來獲取結果集
IO操作最佳實踐
- 使用有緩沖的IO類,不要單獨讀取字節(jié)或字符
- 使用NIO和NIO 2或者AIO,而非BIO
- 在finally中關閉流
- 使用內(nèi)存映射文件獲取更快的IO