去年面了多個候選人,看看我挖的坑還有他們應該要補的Java基礎(二)

看看我在Object 通用方法埋了什麼坑

equals方法用過吧?看看這道題,說說看equals方法在前和在後有什麼區別?

image-20210110172344885

沒什麼區別。java

錯,初學者或者代碼打得少的人都會犯這個錯。程序員

test1會直接報空指針異常,你想一想看,null.equals看不起來不就怪怪的嗎?空指針怎麼可能有方法呢是吧,面試

拓展:咱們通常在企業開發中都會將已知的字面量放在equals,未知的參數放在equals後面,這樣能夠避免空指針,編程新手容易犯這個異常,我review過的代碼這麼實現的,說實話,挺屢次的,不止編程新人,兩三年工做經驗的都會這麼作,實在不該該,算法

說說看equlas 跟 == 的區別

equals方法比較的是字符串的內容是否相等,而 == 比較的則是對象地址。數據庫

這麼回答也是對的,可是,咱們更但願聽到更專業的回答,好比:編程

首先Java中的數據類型能夠分爲兩種,一種是基本數據類型,也稱原始數據類型,如byte,short,char,int,long,float,double,boolean 他們之間的比較,應用雙等號(==),比較的是他們的值。後端

另外一種是複合數據類型,包括類,當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,因此,除非 是同一個new出來的對象,他們的比較後的結果爲true,不然比較後結果爲false。設計模式

而JAVA當中全部的類都是繼承於Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行爲是比較對象的內存地址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而再也不是比較類在堆內存中的存放地址了。api

這麼回答顯的既專業又牛逼,還大氣上檔次,你學廢了嗎?服務器

聊聊對hashCode和equals方法的理解

通常問到這種問題,候選人均可以回答:

若是兩個對象equals方法相等,則它們的hashCode必定相同;

若是兩個對象的hashCode相同,它們的equals()方法則不必定相等。

而兩個對象的hashCode()返回值相等不能判斷這兩個對象是相等的,但兩個對象的hashcode()返回值不相等則能夠斷定兩個對象必定不相等。

考察對hashCode的理解 那請問hashCode有什麼做用呢?爲何要這麼規範

基本上初學者或者應屆生在這一步就被我卡主了, 支支吾吾的回答不了多少,基本上都在這裏掉分,由於上面的規範回答百度一查一大把,可是對hashCode做用有深刻了解的卻不多,這道題也能夠看出一我的的專業水平。

正確的回答是:

hashCode的做用其實是爲了提升在散列結構存儲中查找的效率,天然只有每一個對象的hashCode儘量的不一樣才能保證散列存儲性能的提升,這也是爲何Object默認提供hash碼都是不一樣的緣由。

舉個例子:

以HashSet爲例,要想保證元素不重複則須要調用對象的equals方法比較一次,可是若是這個結構放了不少元素,好比5000次,若是沒有hashCode的話則須要在每次加元素的時候對比5000次,而若是有hashCode則不同了,當集合要添加新的元素的時候先調用hashCode方法,這樣即可以定位到元素的地址,而無需屢次判斷。

考察對clone方法的理解看看這道題,結果輸出什麼?

image-20210110183459155

每次問到這道題,大部分人都是回答2,小部分人是回答1。

都錯,正確答案是直接報錯

image-20210110183635113

爲何?由於clone方法是Object的protect方法,須要子類顯示的去重寫clone方法,而且實現Cloneable 接口,這是規定。

那麼問題來了?加上Cloneable 接口後呢?究竟是2仍是1呢?

這又是我挖的坑,主要爲了考察候選人對淺拷貝和深拷貝的理解

最終結果實際上是2,由於直接用clone拷貝的時候是一種淺拷貝,最終拷貝對象和原始對象的引用類型引用仍是同一個對象。

若是想要實現深拷貝,則須要開發人員本身在clone方法內本身進行重寫。

拓展:在企業開發中通常咱們不容許直接用clone方法來作拷貝,存在拋出異常的風險,還須要進行類型轉換。若是是我review到這種代碼,都是直接告訴同事:在Effective Java 書上有講到,最好不要去使用 clone(),咱們可使用拷貝構造函數或者拷貝工廠來拷貝一個對象,好比這樣

image-20210111224630388

總結:咱們都知道Java是面向對象編程的,個人理解實際上就是面對Object編程,可以合理的使用和了解原理equals和hashCode是一個初級程序員必須具有的素養,而對淺拷貝和深拷貝的理解也基本是基礎中的基礎,但願看完這系列,你們能夠了然於胸,下次碰見這種面試題,直接吹。

看看我在抽象類和接口埋了什麼坑

考察候選人編碼水平 說說看你對抽象類的理解

抽象類和抽象方法都使用 abstract 關鍵字進行聲明,若是一個類中包含抽象方法,那麼這個類必須聲明爲抽象類,而且抽象類和普通類最大的區別是,抽象類不能被實例化,只能被繼承。

bingo,答案是對的,可是還不夠好

上面的是標準答案,隨便一本Java書籍均可以看到,可是能夠補充下大家對應用的理解,這能夠體現你的編碼水平到了哪一步,我這邊補充我理想的答案:

抽象類能夠實現代碼的重用,模板方法設計模式是抽象類的一個典型應用,例如咱們遊戲的全部活動都要進行同樣的初始化,可是初始化的時候須要根據不一樣活動進行不一樣功能開啓判斷,那麼就能夠定義一個活動的基類,將功能開啓判斷作成一個抽象方法,讓全部活動都繼承這個基類,而後在子類自行重寫功能開啓判斷。

補充上本身對應用的理解,能夠充分體現你的專業,面試官聽完,確定是心裏一頓尼瑪的牛逼。

繼續考察候選人編碼水平說說看你對接口的理解

接口能夠說成是抽象類的一種延伸,接口中的全部方法都必須是抽象的。實際上,接口中的方法定義默認爲public abstract類型,接口中的成員變量類型默認爲public static final。

大家懂的,這又是一個標準回答,實際上,不夠好,能夠補充如下幾句話

從Java8開始,接口也能夠擁有默認的方法實現了,爲啥Java團隊要作這個支持呢?

實際上,在Java上深耕多年的老司機其實都被接口沒有默認實現搞過,維護成本過高了,你想一想,若是你如今有100個類實現了一個接口,而在這個時候,須要給接口新增一個方法,特麼Java8以前不容許給接口加默認實現,因此你就得改100個類,這特麼不得搞出人命嗎?

真心話:可以說出對抽象類和接口應用的理解的候選人很少,基本上說出來的我心裏都是往牛逼去吹的,大寫的服,由於這充分體現一個開發人員對編碼方面的思考,你不知道一個寫代碼有思考和整潔,而不是隻顧實現功能的程序員,主管會多喜歡!!!

真正的直接背的答案說說看抽象類和接口的區別
  • 抽象類能夠有構造方法,接口中不能有構造方法。
  • 抽象類中能夠有普通成員變量,接口中沒有普通成員變量
  • 抽象類中能夠包含非抽象的普通方法,接口中的全部方法必須都是抽象的,不能有非抽象的普通方法。
  • 抽象類中能夠包含靜態方法,接口中不能包含靜態方法
  • 抽象類和接口中均可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型能夠任意,但接口中定義的變量只能是public static final類型,而且默認即爲public static final類型。
  • 一個類能夠實現多個接口,但只能繼承一個抽象類。

這個就是標準答案了,不過咱們面試官比較但願能夠在回答區別題的時候本身作些拓展回答,將上面接口和抽象類的理解都說一遍,而不是背書同樣的回答,切記,回答問題,碰見本身懂的知識點,儘可能作拓展,加上本身的理解,一是能夠向面試官展現你的專業,二是能夠拖時間,儘可能避免讓面試官想更多難題坑你

總結:看到這裏,估計不少人對抽象類和接口都嗤之以鼻,都是隨口能夠說出的答案,可是,你覺得咱們只是想要聽你背書式的回答嗎,錯了,咱們想知道的是後面你的拓展和理解,這才能夠體現你的編碼能力和專業。

看看我在重寫和重載裏埋了什麼坑

給你們來一個筆試中超級常見的題 看看如下這道題,說說看結果是啥?

image-20210111235033082

這道題簡直就是了,不少年前我來我司應聘的時候也碰見過這道題,說實話,應屆生和初級繞不過這道題,如今直接給你們亮牌:

image-20210111235450101

給你們說說如何理解這種題,理解如下的原則就能夠了:

JVM在調用一個方法時,會優先從本類中查找看是否有對應的方法,若是沒有再到父類中查看是否從父類繼承來。

若是沒有怎麼辦?那JVM就會對參數進行轉型,轉成父類以後看是否有對應的方法。

總的來講,方法調用的優先級以下:

  • this.func(this)
  • super.func(this)
  • this.func(super)
  • super.func(super)

記住這個過程就能夠了。

開始挖坑 說說看如下的是否是方法重載?

class TestMain {

public int show(int x) {
return x;
}

public void show(int x) {
System.out.println(x);
}
}

我面試過的大部分初級或者應屆都會回答是,不得不說這道題實在太坑了,不開玩笑,你看那堆被這道題埋過的人裏邊就有我。

千萬記住了,重載的定義是:在同一個類中,一個方法與已經存在的方法名稱上相同,而且參數類型、個數、順序至少有一個不一樣。這句話裏邊並無包含着返回值,若是隻是返回值不一樣,其它都相同根本不算是重載。

固然,idea是會直接提示報錯的,這也是爲何我沒有截idea的源碼出來給你們看的緣由,一看有爆紅大家就知道有問題了。不過仍是那句話,這並不意味着idea能夠檢測出來的東西,你就能夠不懂,這是基礎。

總結:說實話,在筆試題中出現重載和重寫那兩道題實在是太常見了,百分之九十的應屆生或者初級開發都被坑過,因此看完這篇文章記得收藏啊,並非你看了就記住了,而是記得在面試前從新看一遍,這但是關係到你offer上那個金光閃閃的數字的上限的啊

看看我在反射方面埋了什麼坑

終於到了反射了,咱們面試官其實很喜歡這個知識點,能夠用它來篩掉一大批的api工程師

瞭解過反射嗎?說說看什麼是反射?

在運行時,Java 反射,能夠獲取任意類的名稱、package 信息、全部屬性、方法、註解、類型、類加載器、現實接口等,而且能夠調用任意方法和實例化任意一個類的獨享,經過反射咱們能夠實現動態裝配,下降代碼的耦合度、實現動態代理等,不過反射的過分使用會嚴重消耗系統資源。

上面的回答很標準,不過開始深挖了 你剛剛的回答有提到了反射的做用,說說哪裏用到了反射機制?

例子不少,好比:

  • JDBC中,利用反射動態加載了數據庫驅動程序。
  • Web服務器中利用反射調用了Sevlet的服務方法。
  • 還有多框架都用到反射機制,注入屬性,調用方法,如Spring等。

基本上回答幾個就能夠了,這裏由於還沒到框架模塊,因此先不對Spring、JDBC等源碼進行深挖,通常都是基礎題面完了,纔開始挖對框架的使用和源碼的理解。

繼續挖,咱們面試官很喜歡抓細節,因此最好保證你說的東西你都有必定的瞭解,不然嘿嘿嘿你剛剛還說到了動態代理,說說你對動態代理的理解,以及有什麼用

動態代理其實就是是運行時動態生成代理類。 動態代理的應用有 Spring AOP、測試框架的後端 mock、rpc,Java註解對象獲取等應用。

怎麼實現動態代理呢?

目前動態代理能夠提供兩種,分爲JDK 原生動態代理和 CGLIB動態代理;

JDK 原生動態代理是基於接口實現的,而 CGLIB是基於繼承當前類的子類實現的,當目標類有接口的時候纔會使用JDK動態代理,實際上是由於JDK動態代理沒法代理一個沒有接口的類,由於JDK動態代理是利用反射機制生成一個實現代理接口的匿名類;

而CGLIB是針對類實現代理,主要是對指定的類生成一個子類,而且覆蓋其中的方法。

挖一個大坑 你上面有說到 Spring AOP有用到了動態代理,那你說說看AOP用到了哪一種方式?

大部分都會回答JDK 原生動態代理,小部分人會回答CGLIB。

但其實二者都是錯的,這是我挖好的坑,太多候選人都只知道AOP用了動態代理了,可是可以完整回答出來用了哪一種的卻寥寥無幾,畢竟只有看過源碼的人才能回答這個問題,通常可以回答出來的,我這邊都會額外加分

實際答案是:在Spring中默認使用的是JDK動態代理,除非目標類沒有實現接口,纔會轉爲CGLIB代理,若是想要強行使用CGLIB代理免責須要在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true" />

然而在SpringBoot中,從2.0開始就默認使用CGLIB代理。**

說說看反射機制的優缺點

優勢:能夠動態執行,在運行期間根據業務功能動態執行方法、訪問屬性,最大限度發揮了java的靈活性。 缺點:對性能有影響,這類操做老是慢於直接執行java代碼。

對性能有影響,那請問怎麼解決這個問題?

到了這裏基本上不少候選人都沒法回答了,我見過太多一開始就說性能性能的,但是一說到如何解決,就基本支支吾吾的回答不出來。

記住了,若是聊到性能,記得想好怎麼回答優化方面的問題,不然就是本身搬起石頭砸本身的腳。

實際上能夠從如下幾個方面回答:

  • 在系統啓動時,將反射獲得元數據保存起來,使用時,只需從內存中調用便可。
  • 儘可能用高點的JDK,由於高版本的虛擬機會對執行次數較多的方法進行優化,例如使用jit技術,而低版本的那個時候還沒實現,
  • 能夠考慮使用高性能的反射庫,Spring內部也有提供一些。

總結:這裏其實建議你們多去玩玩反射,不要只會api調用,對於咱們面試官而言,熟悉和理解反射是一箇中級程序員必備的條件。

看看我在異常和註解方面埋的坑

作爲一個Javer,應該對Error 和 Exception很熟悉吧,說說看它們的區別

目前來講,能夠做爲異常拋出的類,分爲兩種: Error 和 Exception。

而其中 Error 用來表示 JVM 沒法處理的錯誤,而後Exception 也分爲兩種,分別是:

  • 受檢異常 :須要用 try...catch... 語句捕獲並進行處理,而且能夠從異常中恢復;
  • 非受檢異常 :是程序運行時錯誤,例如除 0 會引起 Arithmetic Exception,此時程序崩潰而且沒法恢復。
說說看什麼是註解?以及有什麼用

註解提供了一種相似註釋的機制,用來將任何的信息或數據與類、方法、或者成員變量等進行關聯。

Annontation像一種修飾符同樣,應用於包、類型、構造方法、方法、成員變量、參數及本地變量的聲明語句中。

註解的用處不少,舉兩個最多見的例子:

  • 生成文檔。這是最多見的,也是java 最先提供的註解,系統會在掃描到使用了註解的類後,根據註解信息生成文檔。
  • 在編譯時進行格式檢查。如@override 放在方法前,若是你這個方法並非覆蓋了超類方法,則編譯時就能檢查出。
Java.lang.annotation 提供了四種元註解,是哪四種?有什麼用呢?

JAVA 中有如下幾個『元註解』:

  • @Target:註解的做用目標
  • @Retention:註解的生命週期
  • @Documented:註解是否應當被包含在 JavaDoc 文檔中
  • @Inherited:是否容許子類繼承該註解

沒錯,這幾個元註解太常見了,可是卻不多人可以真的用的好。

說說看@Target做用目標有哪些?

關於@Target的做用目標有多個目標類型,直接截圖以下:

image-20210112230657929

基本上回答幾個就能夠了,不過記住了,上面的TYPE就是指的是類,也就是說這個註解是做用在類上的。

@Retention註解的生命週期有哪幾種?有什麼區別?

@Retention註解的生命週期能夠分爲如下幾種:

  • etentionPolicy.SOURCE:當前註解編譯期可見,不會寫入 class 文件
  • RetentionPolicy.CLASS:類加載階段丟棄,會寫入 class 文件
  • RetentionPolicy.RUNTIME:永久保存,能夠反射獲取

區別在於:

第一種是隻能在編譯期可見,編譯後會被丟棄;

第二種會被編譯器編譯進 class 文件中,不管是類或是方法,乃至字段,他們都是有屬性表的,而 JAVA 虛擬機也定義了幾種註解屬性表用於存儲註解信息,可是這種可見性不能帶到方法區,類加載時會予以丟棄;

第三種則是永久存在的可見性,能夠反射獲取,基本上用這種居多。

對於註解生命周器的理解能夠避免一些坑,仍是那句話,基本上註解這塊能回答的比較流暢的,說明都是玩過組件或者看過源碼的,由於對於開發一個組件來講,註解太經常使用了。

送命題,百分之九十的候選人答不上來 父類使用了註解,請問子類繼承了這個父類後,是否也攜帶了這個註解呢?若是沒有,要怎麼實現這個過程?

這道題不多人會回答的上來,畢竟你們對@Inherited這個元註解太陌生了。

在註解上使用元註解@Inherited,表示該註解會被子類所繼承,注意注意,僅針對類喲,成員、方法並不受該元註解的影響

所以,若是想要讓子類也繼承父類的註解,則須要給註解加上元註解@Inherited。

拓展:通常初級開發都基本不會用上註解,由於註解更多的是用來寫組件用的,咱們項目組這邊就很喜歡自定義一些註解來實現組件,由於用起來實在是太舒服了。

總結:關於異常這塊,Error 和 Exception是常常會被問到的基礎考點,而註解這塊是一個可以加分的點,但願你們別錯過這個知識點,學透它,不過記住上面幾個坑點,其實也差很少了。

最後

上一篇文章:https://mp.weixin.qq.com/s/F73r_f5YcBOayPdsin4gBg 點贊和閱讀量都很高,說明文章質量是ok的,感謝你們的支持;

Java基礎系列也基本差很少了,說實話,這兩篇文章的坑點,只要你記住了,Java基礎這塊基本上沒問題了。

後續安排:

  • 【好好面試】Java集合系列
  • 【好好面試】Java崗位常考算法
  • Caffeine源碼解析

有興趣的關注我一波,Java面試官帶大家跨過一個個的面試坑,保證不虧。

爲了感謝最近你們的支持,我這邊特意跳了一些Java相關的資源,不少專題,好比JAVA+TCP、Java反射機制、Java多線程專題等,都是針對性訓練的利器,建議人手一份。

圖片

圖片

給文章點個再看,關注公衆號,回覆:Java面試 便可拿到資源。

公衆號:飯談編程 原文連接:https://mp.weixin.qq.com/s/F73r_f5YcBOayPdsin4gBg

謝謝點贊支持👍👍👍!

相關文章
相關標籤/搜索