今年是2015年,在過去幾年中,電面(電話面試)是篩選程式設計師職位候選人的最流行的方式。它讓僱傭雙方很容易互相瞭解對方,候選人不需要去未來僱主的所在地,面試官也不用做額外的安排。這是我介紹程式設計師面試問題的文章的第二部分。我得到反饋說第一部分過於偏重編碼的題了,許多程式設計師希望我針對電面問題列一個類似的列表。為了順利通過電面進入下一輪,你必須足夠好地回答與你工作要求相關的全部問題。在大多針對Java和C++開發者的電面中,你不僅會遇到相應程式語言的問題,還會遇到其他技術的問題,比如SQL、XML、UNIX、泛型程式設計、物件導向程式設計、資料結構與演算法、網路、編碼以及工作的其他方面。由於程式設計師求職電面的多變性,你需要有特殊的技巧,以面試官期待的方式展示自己。
要記住一件重要的事,在回答電面問題的時候,儘早提出關鍵點,總是給出關鍵性回答。由於面試官的問題往往會覆蓋很大範圍的主題,他們更喜歡關鍵性回答,而不是“OK,我知道”之類的空話。在面對面的面試中,你會有機會更深入地解釋問題的。順便說一下,這並不是固定的規則,根據面試官對你的回答的反應,你可以瞭解到他期望得到什麼樣的回答。如果他進行追問,期望你多說一些,那麼你就應該多說。但如果他立刻跳到下一個問題,那麼你就應該回答得清晰簡潔。在這篇文章中,我要和你分享一些常見的有趣程式設計問題,它們是針對電面改編過的。其中大部分都來自科技公司的電面環節,包括Barclays、Citi、Nomura之類的銀行,和Infosys、TCS、CTS、Tech Mahindra和HCL之類的提供服務的公司。像我之前提過的,面試題是隨機選的,但大部分是基於基礎知識,因為那是面試官在電面時想考察的。儘管這些問題大多數是針對初級開發者(2至5年經驗),高階和資深程式設計師仍然可以把它們用作自己面試的題目。如果你是一名面試官,你可以用這些問題快速篩選開發職位的候選人。我在此提供簡短答案,並給出詳細答案的連結。
程式設計崗位電話面試問答Top 50
下面是幾乎50道程式設計師電面題目的列表。這些問題可以用來考察任何程式設計師、開發者、軟體工程師、測試和運營工程師,因為它們是基於程式設計的基礎知識的。但它們最適合程式設計師和開發者。順便說一下,如果你是Java開發者,並且在尋找Java電面題目,去看看那個列表。本列表更加普遍,適用於所有的程式設計師,包括Python、Ruby、Perl和C#開發者。
1. 從雜湊表,二叉樹和連結串列中取元素需要多少時間?如果你有數百萬記錄呢?
雜湊表需要O(1)時間,二叉樹需要O(logN) (N是樹中節點數),連結串列需要O(N) (N是連結串列中節點數)。如果資料結構工作正常(比如雜湊表沒有或只有相對少量衝突,二叉樹是平衡的),數百萬記錄並不影響效率。如果工作不正常,那麼效率會隨著記錄數上升而下降。
2. 覆蓋(Overriding)和過載(Overloading)的區別是什麼? (detailed answer)
覆蓋在執行時決定,過載是在編譯時決定。並且覆蓋和過載的機制不同,例如在Java中,過載方法的簽名必須不同於原先方法的,但對於覆蓋簽名必須相同。
3. fork一個程式和生成一個執行緒有什麼區別?
當你fork一個程式時,新的程式將執行和父程式相同的程式碼,只是在不同的記憶體空間中。但當你在已有程式中生成一個執行緒時,它會生成一個新的程式碼執行路線,但共享同一個記憶體空間。
4. 什麼是臨界區? (answer)
臨界區是一段程式碼,十分重要,在多執行緒中同一時間只能被一個執行緒執行。可以用訊號量或互斥量來保護臨界區。在Java中你可以用synchronized關鍵字或ReentrantLock來保護臨界區。
5. 值型別和引用型別有什麼區別? (answer)
值型別是更加優化的型別,總是不可變的(immutable),例如Java原始的int、long、double和float。引用型別指向一個物件,可能是可變的,也可能是不變的。你也可以說值型別指向一個值,引用型別指向一個物件。
6. 什麼是在程式中的堆和棧?(detailed answer)
在同一個程式中,有兩塊不同的記憶體區域。以Java來說,棧用來儲存原始值和指向物件的引用型別,但物件本身總是在堆中被建立。堆和棧的一個重要區別是,堆記憶體被所有執行緒共享,但每個執行緒有自己的棧。
7. 什麼是版本控制?(answer)
版本控制是用來儲存程式碼和管理程式碼庫版本的軟體,例如SVN、CVS、Git、Perforce和ClearCase。它們在對比程式碼、審查程式碼和從之前的穩定版本構造時十分高效。所有的專業開發都使用某種版本控制工具,否則你無法有效的管理程式碼,尤其是如果有20個開發者在同一個程式碼庫上工作的時候。版本控制工具在保持程式碼庫一致性和處理程式碼衝突上扮演著十分重要的角色。
8. 什麼是強型別程式設計語言?(answer)
在強型別語言中,編譯器確保型別的正確性,例如你無法在String型別中存放數字,反之亦然。Java是強型別語言,因此存在各種資料型別(如int、float、String、char、boolean等)。你只能將相容的值存入相應的型別中。與此相反,弱型別語言不要求在編譯時進行型別檢查,它們根據上下文處理值。Python和Perl是兩個常見的弱型別程式設計語言的例子,你可以將數字組成的字串儲存在數字型別中。
9. 可否描述一下有效(valid)的XML和格式正確(well-formed)的XML的區別?
格式正確的XML有根元素,所有標籤都是正確關閉的,屬性是正確定義的,它們的值正確地加上了引號。另一方面,有效的XML可以根據一個XSD檔案或模式(schema)進行驗證。所以一個XML可能是格式正確但不有效的(因為包含不被模式允許的標籤)。
10. DOM和SAX語法分析器有什麼區別?(detailed answer)
DOM語法分析器是駐留記憶體的,將整個XML檔案裝載到記憶體中,並建立一個DOM樹進行語法分析。SAX語法分析器是一個基於事件的語法分析器,所以它根據收到的事件(如開始標籤、結束標籤、屬性開始和屬性結束)來對XML文件進行語法分析。根據他們的分析方法,DOM語法分析器並不適用於大的XML檔案,因為它會佔用大量的記憶體空間,你的程式可能會耗盡記憶體。應該用SAX分析大的檔案。對於小的檔案,DOM往往比SAX快很多。
11. 執行緒和程式的關係是什麼?(detailed answer)
一個程式可以有多個執行緒,但一個執行緒總是屬於唯一的程式。兩個程式不能共享記憶體空間,除非它們有意通過共享記憶體進行程式間通訊。但是同一程式的兩個執行緒總是共享相同的記憶體。
12. 不可變(immutable)類是什麼意思?(detailed answer)
一個類,如果在建立之後它的狀態就不能被改變,那麼他就是不可變的。例如Java中的String。一旦你建立了一個String,例如“Java”,你就不能再改變它的內容。任何對這個字串的改變(例如轉換到大寫、與另一個String連線)將建立一個新的物件。不可變的物件在並行程式設計中很有用,因為它們可以在程式間被共享,不需要擔心同步。事實上,整個函式式程式設計的模型都是基於不可變物件構建的。
13. 你為何要建立模擬(mock)物件? (answer)
模擬物件在測試軟體中一個獨立的單元時很有用,事實上,存根(stub)和模擬都是建立自動化單元測試的有力工具。假設你在寫一個顯示貨幣兌換率的程式,但沒有一個可以連通的URL,現在如果想測試你的程式碼,可以用模擬物件。在Java的世界中,有很多框架可以為你生成強大的模擬物件,例如Mockito和PowerMock。
14. 什麼是SQL隱碼攻擊?
SQL隱碼攻擊是一種安全漏洞,它使得入侵者可以從系統中竊取資料。任何從使用者那裡得到輸入並不加驗證地建立SQL查詢的系統都可能被SQL隱碼攻擊。在這樣的系統中,入侵者可以輸入SQL程式碼,而不是資料,來獲取額外的資料。有很多用敏感資訊(如使用者id、密碼和個人資訊)被人利用這種漏洞獲取的例項。 在Java中,你可以用Prepared語句來避免SQL隱碼攻擊。
15. 在SQL中,內連線(inner join)和左連線(left join)有什麼區別?(answer)
在SQL中,主要有兩種連線型別,內連線和外連線。外連線包括右外連線和左外連線。內連線和左連線的主要區別是,內連線中兩個表都匹配的記錄才被選中,左連線中兩個表都匹配的記錄被選中,外加左表的所有記錄都被選中。要留意包含“所有”的查詢,它們往往要求左連線,例如寫一個SQL查詢來找所有的部門和它們的僱員人數。如果你用內連線處理這個查詢,你會漏掉沒有人工作的空部門。
16. MVC中的V代表什麼,意味著什麼?(answe)
在MVC模式中,V是檢視(View)。檢視是使用者看到的東西,比如網頁。這是一個非常重要的web應用開發設計模式,它基於關注點分離原則,目的是不同模組可以獨立修改,不影響其他模組。在Java的世界中,有很多提供MVC模式的開源框架,例如Struts 2和Spring MVC。順便說一下,M代表模型(Model),C代表控制器(Controller)。模型是實際的業務物件,例如使用者、僱員、訂單,控制器用來將請求分發給正確的處理單元。
17. 類和物件的區別是什麼? (detailed answer)
類是用來建立物件的設計圖。一個類包括程式碼和行為,一個物件包括狀態和行為。要建立一個物件,你必須建立一個表達物件結構的類。類還被用來在記憶體中對映物件,在Java中,JVM替你完成這項工作。
18. 什麼是疏耦合(loose-coupling)?
疏耦合是一種值得追求的軟體特性,它使得對軟體一個部分的修改不會影響到其他的部分。例如,在一個疏耦合的軟體中,對UI佈局的改變不應該影響後端的類結構。
19. 組合(composition),聚合(aggregation)和關聯(association)的區別是什麼? (detailed answer)
關聯的意思是兩個物件是相互聯絡的。組合是關聯的一種形式,即一個物件由多個物件組成,但是它們必須共存,例如人體由各種器官組合而成,獨立的器官不能生存,它們必須在身體內發揮作用。聚合也是關聯的一種形式,表示物件的集合,例如城市是居民的聚合。
20. 介面和抽象類有什麼區別? (detailed answer)
這是所有程式設計師面試最經典的問題。介面是最純粹的抽象形式,沒有任何具體的東西。抽象類是一些抽象和具體事物的組合體。這個區別在不同語言中可能會不同,例如在Java中你可以擴充套件(extend)多個介面,但只能繼承一個抽象類。更詳細的討論見於詳細答案。
21. 什麼是單元測試?(answer)
單元測試是測試獨立單元(而不是整個應用程式)功能性的一種方法。在不同語言中,有很多工具可以做單元測試。例如在Java中,你可以用JUnit或TestNG來寫單元測試。單元測試經常在構建時自動執行,或者在一個持續的環境(例如Jenkins)中執行。
22. 你能否描述三種不同的在應用程式釋出前對其進行測試的方式?
單元測試,整合測試,冒煙測試。單元測試用來測試獨立的單元是否依照預期工作,整合測試用來測試已被測試過的獨立單元能否共同工作,冒煙測試用來測試軟體最常用的功能是否正常工作,例如在一個飛機訂票網站中,你應該能訂票,取消或更改航班。
23. 迭代和遞迴有什麼區別?(detailed answer)
迭代通過迴圈來重複執行同一步驟,遞迴通過呼叫函式自身來做重複性工作。遞迴經常是複雜問題(例如漢諾塔、反轉連結串列或反轉字串)的清晰簡潔的解決方案。遞迴的一個缺陷是深度,由於遞迴在棧中儲存中間結果,你只能進行一定深度的遞迴,在那之後你的程式會因為StackOverFlowError而崩潰。這就是在產品程式碼中優先使用迭代而不是遞迴的原因。
24. &和&&運算子的區別是什麼?(detailed answer)
&是位運算子,&&是邏輯運算子。&和&&的一個區別是位運算子(&)可以被用於整型和布林型別,邏輯運算子(&&)只能被用於布林型別變數。當你寫a & b時,兩個整型數的每一位都會進行與運算。當你寫a && b時,第二個引數可能會也可能不會被執行,這也是它被稱為短路運算子的原因,至少在Java中是這樣的。我很喜歡這個問題,經常對初級開發者和畢業生問這個問題。
25. 1 XOR 1的結果是什麼?
答案是0,因為XOR在兩個運算元(按位)不同時返回1,相同時返回0。例如0 XOR 0仍然是零,但0 XOR 1和1 XOR 0的結果是1。
26. 如何得到一個整型數的最後一位? (answer)
用取模運算子,數字 % 10返回數字的最後一位。例如2345 % 10會返回5,567 % 10會返回7。類似的,除運算子可以用來去掉數字的最後一位,例如2345 / 10的結果是234,567 / 10的結果是56。這是值得了解的一個重要技巧,可以用來解決類似迴文數、反轉數的問題。
27. 什麼是測試驅動開發?
測試驅動是一種常見的開發方法,在這種方法中,測試程式碼在功能程式碼之前編寫。測試決定了程式的結構。測試驅動的純粹主義者在寫為應用寫測試之前,不會寫一行的應用程式碼。這能很大幅度地提高程式碼質量,經常被認為是巨星級開發者的品質。
28. 里氏替換原則(Liskov substitution principle, LSP)是什麼?(answer)
里氏替換原則是鮑勃大叔稱作SOLID的五條設計原則中的一條。里氏替換原則規定,所有的子類都能作為父類的代理(proxy)工作。例如,如果一個方法需要父類物件作為輸入,那麼如果你提供一個子類物件,它也應該正常工作。任何不能替代父類的類都違反了里氏替換原則。這實際上是一個難以答出的問題,如果你答出了,那麼就會給面試官留下好的印象。
29. 什麼是開閉(Open closed)設計原則?(answer)
開閉原則是SOLID中另一個重要的原則,它規定一個系統對擴充套件是開放的,但對修改是封閉的。意思是說,如果一個新的功能要被加入一個穩定的系統,那麼你不需要碰已被測試過的現有程式碼,新的功能可以通過只新增新類來實現。
30. 二叉樹和二叉查詢樹的區別是什麼?
二叉查詢樹是有序的二叉樹,所有節點(例如根節點)的左子樹節點的值都小於或等於該節點的值,右子樹節點的值都大於或等於該節點的值。它是一個重要的資料結構,可以用來表示有序的資料。
31. 你能否給出一個遞迴演算法的實際例子?(example)
遞迴演算法能適用在很多地方,例如與二叉樹和連結串列相關的演算法。幾個與遞迴演算法的例子包括反轉字串,計算斐波那契數列。其他的例子包括反轉連結串列、樹遍歷以及快速排序。
31. 演算法的時間複雜度是什麼?
時間複雜度表示的是執行時間對輸入量的比率。他表示一個演算法處理一定量的輸入需要多長時間。它是一個估計值,但足夠表示輸入量從十增長到一千萬時,你的演算法會有什麼樣的表現。
33. 連結串列和陣列有哪些重要區別?(detailed answer)
連結串列和陣列都是程式設計世界中重要的資料結構。它們間最明顯的區別是,陣列將元素存放在連續的地址中,連結串列將資料存放在記憶體中任意的位置。這使得連結串列有巨大的擴充套件自己的靈活性,因為記憶體總是分散的。這種情況總是可能的:你無法建立一個陣列來存放一百萬個整數,但可以用連結串列來存放,因為空間是存在的,只是不連續。其他的不同都是源於這項事實。例如,在陣列中,如果你知道下標,可以用O(1)的時間得到一個元素,但在連結串列中要花O(n)的時間。更多不同參見詳細答案。
33. 在雜湊表中處理衝突的方法都有哪些?
線性探測(linear probing),二次雜湊(double hashing)和連結(chaining)。線上性探測中,如果桶已經被佔據了,那麼函式會線性地檢查下一個桶,直到找到一個空位。在連結中,多個元素可以儲存在同一個桶中。
34. 正規表示式是什麼意思? (answer)
正規表示式是在文字型資料上進行模式匹配的方法。它是一種搜尋的強有力方法,例如搜尋長字串中的某些字元,例如搜尋一本書中是否含有某個單詞。所有主流程式設計語言都支援正規表示式,但是Perl正規表示式的能力是著名的。Java的java.util.regex包也支援類似Perl的正規表示式。你可以用正規表示式檢查email地址是否有效,電話號碼是否有效,郵政編碼是否有效,甚至社會保險號(SSN)是否有效。正規表示式最簡答的例子之一是檢查字串是不是一個數。
35. 什麼是無狀態(stateless)系統?
無狀態系統是不維護內部狀態的系統。這種系統在任何時刻,對相同的輸入都會給出相同的輸出。編寫優化一個無狀態系統總是比較簡單的,所以如果可能,你總是應該優先編寫無狀態系統。
36. 寫一個SQL查詢,在僱員表中查詢第二高的工資。 (solution)
這是SQL面試的經典題目之一,儘管已經很老了,還是很有趣,並且可以追問很多問題來測試候選人的知識深度。你可以用相關或不相關的子查詢來查詢第二高工資。如果你在用SQL Server或MySQL,你也可以用類似TOP和LIMIT之類的關鍵字,前提是面試官允許。查詢第二高工資的最簡答方法是:
這個查詢首先查詢最高工資,然後將它從列表中排除,再查詢最高工資。很明顯,第二次返回的是第二高工資。
37. 可否描述一下什麼是關聯的和不關聯的子查詢?(answer)
在關聯的子查詢中,內層查詢依賴於外層查詢,對外層查詢的每一行執行。非關聯的子查詢不依賴於外層查詢,可以獨立執行。因此,前者慢,後者快。順便說一下,關聯的子查詢有一些很棒的應用,其中包括在僱員表中查詢第N高的工資,這在上一道SQL問題中也有提到。
39. 不用算術運算子,如何判定一個數是否是二的冪?(solution)
當你聽到不能用算術運算子的限制時,應該立刻假定這是一道關於位運算的題。如果沒有這條限制,那麼你可以輕鬆地用取模和除運算子檢查一個數是不是二的冪。用位運算子,有一個很巧妙的方法來完成任務。你可以用下面這段程式碼來檢查一個數是不是二的冪
1 2 3 |
public static boolean powerOfTwo(int x) { return (x & (x - 1)) == 0; } |
x & (x-1)是一個很棒的技巧,可以將最右邊的為1的位設為0。我是從《高效程式的奧祕》這本書中學到的。
40. 如何在UNIX上找到一個正在執行的Java程式?(command)
你可以組合使用ps和grep命令來查詢UNIX機器上的任何程式。假設你的Java程式有名字,或者有任何可以用來匹配的文字,那麼使用這個命令。
ps -ef | grep “myJavaApp”
ps -e將列出所有的程式(所有使用者的程式,不只是你的),ps -f將顯示所有細節,包括PID。如果你想要深入調查或用kill命令殺死這個程式,你會需要PID。
41. 如何在UNIX中尋找大的檔案,例如1GB以上的檔案? (command)
你可以輕鬆地用find命令尋找大的檔案,因為它提供依據大小尋找檔案的選項。如果你的檔案系統滿了,你的Java程式因為沒有空間而崩潰,那麼就使用這個命令。這個命令可以列出所有大於1GB的檔案。你可以很容易地改變大小,例如尋找所有100MB以上的檔案,就用+100M。
find . – type f -size +1G -print
42. shell指令碼是什麼?
shell指令碼是包含程式元素(例如if和for迴圈)的一組shell命令,它可以自動做一些重複的任務。例如,你可以寫一個shell指令碼來每天清理日誌檔案,為記錄歷史備份資料,以及其他家務活、版本釋出、監視等等。
這個程式設計師電面問題列表到此為止了。你可能已經注意到了,只有42道題,但是標題說有50道,剩下的8道在哪?好吧,為了補償這8道題,我在此分享8篇文章,你可以找到剩餘的問題:
-
給程式設計師的20道字串程式設計題( read here)
-
給軟體開發者的15道資料結構與演算法題(see here)
-
10道所有開發者都應該指導的面試題(read more)
-
給2至3年經驗程式設計師的20道核心Java問題(check here)
-
21道SQL查詢面試題與答案(read more)
-
給Java程式設計師的23道難題 (read here)
-
給程式設計師的10道XML面試題(see here)
-
來自Java面試的50道多執行緒和併發問題 (check here)
-
來自程式設計師面試的20道軟體設計問題(read more)
-
18道Java設計模式面試題(see here)
感謝你一直讀到這裡,如果你喜歡這篇文章,並覺得它對你的電話面試有幫助,請與朋友和同事分享。十分感謝所有提高面試題質量的建議。
擴充套件閱讀:
http://javarevisited.blogspot.com/2015/02/50-programmer-phone-interview-questions-answers.html
【譯者注:譯文對原文的描述有修改。】