10道虐心的Java面试题,被面试官虐哭了,同事一题都没答对
10道虐心的Java面試題,被面試官虐哭了,同事一題都沒答對
Java高級架構
有一天,小王告訴我,他去一家公司面試 Java 崗,結果被面試官虐哭了。整整 10 道 Java 面試題,小王一道也沒答正確。
他沮喪地給我說,“哥,說點我的情況,你愿意聽嗎?我和一個女孩相處,女孩大我兩歲,我非科班。本來打算國慶換一家薪水高點的,好確認關系。我經驗不多,技術一般般,之前在一家外包公司,有一個甲方內推,我就魯莽地把外包的工作辭了,結果沒想到面試被虐了,我擔心女朋友會不會因為我沒有工作和我分手。”
聽他這么一說,確實挺虐心的。后來我就安慰他,要他端正心態,先把這些面試題整明白,然后繼續找工作,不要想太多。
借這個機會,我就把小王遇到的這 10 道面試題分享出來,希望能對其他小伙伴一些幫助。
第一題,下面這串代碼打印的結果是什么
public class Test {public static void main(String[] args) {System.out.println(Math.min(Double.MIN_VALUE, 0.0d));} }小王之所以沒答對這道題,是因為他覺得 Double.MIN_VALUE 和 Integer.MIN_VALUE 一樣,是個負數,應該小于 0.0d。
但事實上,Double. MIN_VALUE 和 Double. MAX_VALUE 一樣,都是正數,Double. MIN_VALUE 的值是2^(-1074),直接打印 Double. MIN_VALUE 的話,輸出結果為?4.9E-324。
因此這道題的正確答案是輸出?0.0。
第二題,在 try 塊或者 catch 語句中執行 return 語句或者?System.exit()?會發生什么,finally 語句還會執行嗎?
小王之所以沒答對這道題,是因為在他的刻板印象中,finally 語句是無論如何都會執行的。
但事實上,在 try 塊或者 catch 語句中執行 return 語句時,finally 語句會執行;在 try 塊或者 catch 語句中執行?System.exit()?時,finally 語句不會執行。
public class Test1 {public static void main(String[] args) {returnTryExec();returnCatchExec();exitTryExec();exitCatchExec();}public static int returnTryExec() {try {return 0;} catch (Exception e) {} finally {System.out.println("finally returnTryExec");return -1;}}public static int returnCatchExec() {try { } catch (Exception e) {return 0;} finally {System.out.println("finally returnCatchExec");return -1;}}public static void exitTryExec() {try {System.exit(0);} catch (Exception e) {} finally {System.out.println("finally exitTryExec");}}public static void exitCatchExec() {try { } catch (Exception e) {System.exit(0);} finally {System.out.println("finally exitCatchExec");}} }程序執行結果如下所示:
finally returnTryExec finally returnCatchExec第三題,私有方法或者靜態方法能被重寫(override)嗎?
小王之所以沒答對這道題,是因為他不確定私有方法或者靜態方法與重寫之間的關系。
重寫的兩個方法名相同,方法參數的個數也相同;不過一個方法在父類中,另外一個在子類中。
class LaoWang{public void write() {System.out.println("老王寫了一本《基督山伯爵》");} } class XiaoWang extends LaoWang {@Overridepublic void write() {System.out.println("小王寫了一本《茶花女》");} } public class OverridingTest {public static void main(String[] args) {LaoWang wang = new XiaoWang();wang.write();} }父類 LaoWang 有一個?write()?方法(無參),方法體是寫一本《基督山伯爵》;子類 XiaoWang 重寫了父類的write()?方法(無參),但方法體是寫一本《茶花女》。
在 main 方法中,我們聲明了一個類型為 LaoWang 的變量 wang。在編譯期間,編譯器會檢查 LaoWang 類是否包含了?write()?方法,發現 LaoWang 類有,于是編譯通過。在運行期間,new 了一個 XiaoWang 對象,并將其賦值給 wang,此時 Java 虛擬機知道 wang 引用的是 XiaoWang 對象,所以調用的是子類 XiaoWang 中的write()?方法而不是父類 LaoWang 中的?write()?方法,因此輸出結果為“小王寫了一本《茶花女》”。
而私有方法對子類是不可見的,它僅在當前聲明的類中可見,private 關鍵字滿足了封裝的最高級別要求。另外,Java 中的私有方法是通過編譯期的靜態綁定的方式綁定的,不依賴于特定引用變量所持有的對象類型。
方法重寫適用于動態綁定,因此私有方法無法被重寫。
class LaoWang{public LaoWang() {write();read();}public void write() {System.out.println("老王寫了一本《基督山伯爵》");}private void read() {System.out.println("老王在讀《哈姆雷特》");} } class XiaoWang extends LaoWang {@Overridepublic void write() {System.out.println("小王寫了一本《茶花女》");}private void read() {System.out.println("小王在讀《威尼斯商人》");} } public class PrivateOrrideTest {public static void main(String[] args) {LaoWang wang = new XiaoWang();} }程序輸出結果如下所示:
小王寫了一本《茶花女》 老王在讀《哈姆雷特》在父類的構造方法中,分別調用了?write()?和?read()?方法,write()?方法是 public 的,可以被重寫,因此執行了子類的?write()?方法,read()?方法是私有的,無法被重寫,因此執行的仍然是父類的?read()?方法。
和私有方法類似,靜態方法在編譯期也是通過靜態綁定的方式綁定的,不依賴于特定引用變量所持有的對象類型。方法重寫適用于動態綁定,因此靜態方法無法被重寫。
public class StaticOrrideTest {public static void main(String[] args) {Laozi zi = new Xiaozi();zi.write();} } class Laozi{public static void write() {System.out.println("老子寫了一本《基督山伯爵》");} } class Xiaozi extends Laozi {public static void write() {System.out.println("小子寫了一本《茶花女》");} }程序輸出結果如下所示:
老子寫了一本《基督山伯爵》引用變量 zi 的類型為 Laozi,所以?zi.write()?執行的是父類中的?write()?方法。
靜態方法也叫類方法,直接通過類名就可以調用,通過對象調用的時候,IDE 會發出警告。
?
第四題,1.0/0.0?得到的結果是什么?會拋出異常嗎,還是會出現編譯錯誤?
小王之所以沒答對這道題,是因為他沒有深入研究過 double 類型和 int 類型的除法運算。
數字在 Java 中可以分為兩種,一種是整形,一種是浮點型。不太清楚的小伙伴先去研究一下數據類型。
當浮點數除以 0 的時候,結果為 Infinity 或者 NaN。
System.out.println(1.0 / 0.0); // Infinity System.out.println(0.0 / 0.0); // NaNInfinity 的中文意思是無窮大,NaN 的中文意思是這不是一個數字(Not a Number)。
當整數除以 0 的時候(10 / 0),會拋出異常:
Exception in thread "main" java.lang.ArithmeticException: / by zeroat com.itwanger.eleven.ArithmeticOperator.main(ArithmeticOperator.java:32)通常,我們在進行整數的除法運算時,需要先判斷除數是否為 0,以免程序拋出異常。
第五題,Java 支持多重繼承嗎?
小王之所以沒答對這道題,是因為他知道,通過接口可以達到多重繼承的目的。
來定義兩個接口,Fly 會飛,Run 會跑。
public interface Fly {void fly(); } public interface Run {void run(); }然后讓一個類同時實現這兩個接口。
public class Pig implements Fly,Run{@Overridepublic void fly() {System.out.println("會飛的豬");}@Overridepublic void run() {System.out.println("會跑的豬");} }但說到多重繼承,討論的關鍵字是 extends,而非 implements。
Java 只支持單一繼承,是因為涉及到菱形問題。如果有兩個類共同繼承一個有特定方法的父類,那么該方法可能會被兩個子類重寫。然后,如果你決定同時繼承這兩個子類,那么在你調用該重寫方法時,編譯器不能識別你要調用哪個子類的方法。
?
類 C 同時繼承了類 A 各類 B,類 C 的對象在調用類 A 各類 B 中重寫的方法時,就不知道該調用類 A 的方法,還是類 B 的方法。
第六題,當在 HashMap 中放入一個已經存在的 key 時,會發生什么?
小王之所以沒答對這道題,是因為他沒有深入研究過 HashMap 的工作原理。
Hash,一般譯作“散列”,也有直接音譯為“哈希”的,這玩意什么意思呢?就是把任意長度的數據通過一種算法映射到固定長度的域上(散列值)。
再直觀一點,就是對一串數據 wang 進行雜糅,輸出另外一段固定長度的數據 er——作為數據 wang 的特征。我們通常用一串指紋來映射某一個人,別小瞧手指頭那么大點的指紋,在你所處的范圍內很難找出第二個和你相同的(人的散列算法也好厲害,有沒有)。
對于任意兩個不同的數據塊,其散列值相同的可能性極小,也就是說,對于一個給定的數據塊,找到和它散列值相同的數據塊極為困難。再者,對于一個數據塊,哪怕只改動它的一個比特位,其散列值的改動也會非常的大——這正是 Hash 存在的價值!
大家應該知道,HashMap 的底層數據結構是一個數組,通過?hash()?方法來確定下標。
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }當我們放入一個鍵值對的時候,會先調用?hash()?方法對 key 進行哈希算法,如果 key 是相同的,那么哈希后的結果也是相同的,意味著數組中的下標是相同的,新放入的值就會覆蓋原來的值。
第七題,下面這段代碼將會打印出什么?
public class Test {public static void main(String[] args) {char[] chars = new char[]{'\u0097'};String str = new String(chars);byte[] bytes = str.getBytes();System.out.println(Arrays.toString(bytes));} }小王之所以沒答對這道題,是因為他沒有深入研究過字符編碼方面的一些知識。
在這段程序中,我們通過一個字符數組創建了一個字符串對象,然后調用 String 類的?getByte()?方法得到字節數組并將其打印到控制臺。
這道面試題考察的核心并不是最終的打印結果(結果是不確定的),而是字符編碼。通常情況下,我們在調用getBytes()?方法時,要指定編碼,比如說?str.getBytes(StandardCharsets.UTF_8)。
當我們沒有指定編碼的時候,JDK 會調用平臺默認的字符編碼,而不同的操作系統,編碼不盡相同的,bytes 的結果也就會不同。
當使用 UTF_8 時,結果為?-62, -105,當使用 GB2312 時,結果為?63。
第八題,當方法在父類中拋出?NullPointerException?時,是否可以使用拋出?RuntimeException?的方法來重寫它?
小王之所以沒答對這道題,是因為他被重寫(overriding)和重載(overloading)的概念搞混了。
方法重寫和方法重載時,方法名可以完全相同,但根本的不同在于方法重寫時發生在運行時,方法重載時發生在編譯時。
另外,方法重寫和方法重載時的規則也不盡相同。在 Java 中,不能重寫 private、static 和 final 方法,但可以重載它們。
我們來重點看一下方法重寫時的規則:
1)方法簽名必須相同,包括返回類型、參數的數量、參數的類型和參數的順序。
2)重寫后的方法不能拋出比父類中更高級別的異常。舉例來說,如果父類中的方法拋出的是 IOException,那么子類中重寫的方法不能拋出 Exception,可以是 IOException 的子類或者不拋出任何異常。這條規則只適用于可檢查的異常。
可檢查(checked)異常必須在源代碼中顯式地進行捕獲處理,不檢查(unchecked)異常就是所謂的運行時異常,比如說 NullPointerException、
ArrayIndexOutOfBoundsException 之類的,不會在編譯器強制要求。
3)重寫后的方法訪問權限不能比父類中的方法低,比如說父類中的方法是 public,重寫后的方法就不能是 protected。
public class ExceptionDemo {public static void main(String[] args) {Super s = new Child();s.write();} } class Super{public void write() throws NullPointerException { } }class Child extends Super {@Overridepublic void write() throws RuntimeException { } }RuntimeException 和 NullPointerException 屬于不檢查異常,所以本題的答案是可以的。如果是可檢查異常的話,IDE 就會發出警告。
?
第九題,下面這段代碼使用了?compareTo()?方法,有問題嗎?
class Employee implements Comparable {private int id;@Overridepublic int compareTo(Object o) {Employee emp = (Employee) o;return this.id - emp.id;} }小王之所以沒答對這道題,是因為他想當然地認為 id 的都是正整數。
當我們需要按照一定的規則進行排序的時候,通常要實現 Comparable 接口,并實現 compareTo 方法,規則如下:
1)如果當前對象小于另外一個對象,則 compareTo 方法必須返回負數;如果當前對象大于另外一個對象,則必須返回正數;如果兩個對象相等,則返回零。
2)通常來說,compareTo 方法必須和 equals 方法一致,如果兩個對象通過 equals 方法判斷的結果為 true,那么 compareTo 必須返回零。
不過,JDK 中有一個反例,就是 BigDecimal。
BigDecimal bd1 = new BigDecimal("2.0"); BigDecimal bd2 = new BigDecimal("2.00");System.out.println("equals: " + bd1.equals(bd2)); System.out.println("compareTo: " + bd1.compareTo(bd2));輸出結果如下所示:
equals: false compareTo: 0這是因為 JDK 認為 2.0 和 2.00 的精度不一樣,所以不能 equals,但值確實是相等的。
3)不能使用減法來比較整數值,因為減法的結果可能溢出。應該使用?Integer.compareTo()?來進行比較。如果你想通過減法操作來提高性能,必須得確保兩個操作數是正整數,或者確保兩者相差的值小于 Integer.MAX_VALUE。
public class CompareDemo {public static void main(String[] args) {List<Employee> list = new ArrayList<>();list.add(new Employee(1));list.add(new Employee(Integer.MIN_VALUE));list.add(new Employee(Integer.MAX_VALUE));Collections.sort(list);System.out.println(list);} }class Employee implements Comparable {private int id;public Employee(int id) {this.id = id;}@Overridepublic int compareTo(Object o) {Employee emp = (Employee) o;return this.id - emp.id;}@Overridepublic String toString() {return "Employee{" +"id=" + id +'}';} }程序的輸出結果如下所示:
[Employee{id=1}, Employee{id=2147483647}, Employee{id=-2147483648}]排序就亂了。因為?Integer.MIN_VALUE - 1?變成了正數?2147483647。
第十題,StringBuffer 和 StringBuilder 之間有什么區別?
小王之所以沒答對這道題,是因為他覺得這道題太簡單了,結果說反了,大意了啊。
StringBuilder 是 JDK 1.5 之后引入的,它和 StringBuffer 最大的區別就在于它的一系列方法都是非同步的。
?
好了,以上就是小王這次面試遇到的 10 道虐心的面試題,本來最后一道是送分題,結果大意說反了,讓小王更加懊惱。年后是跳槽的高峰期,有打算的小伙伴要提前準備了,希望大家都能夠順利面上心儀的崗位。
總結
以上是生活随笔為你收集整理的10道虐心的Java面试题,被面试官虐哭了,同事一题都没答对的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: POST /product/:id 获取
- 下一篇: java美元兑换,(Java实现) 美元