31道Java核心面試題,一次性打包送給你
二哥,你好,找工作找了仨月,還沒有找到,很焦慮,我該怎么辦呢?你那有沒有 Java 方面的面試題可以分享一波???
以上是讀者田田給我發(fā)的私信,看完后于我心有戚戚焉啊,最近境況確實(shí)不容樂觀,并非是個(gè)人的原因造成的。那,既然需要面試題,二哥就義不容辭,必須得準(zhǔn)備一波。
這次我花了一周的時(shí)間,準(zhǔn)備了 31 道 Java 核心面試題,希望能夠幫助到田田,以及其他和田田類似情況的讀者朋友。
01、請說出 Java 14 版本中更新的重要功能
Java 14 發(fā)布于 2020 年 3 月 17 日,更新的重要功能有:
- switch 表達(dá)式
- instanceof 增強(qiáng)表達(dá)式,預(yù)覽功能
- 文本塊,第二次預(yù)覽
- Records,預(yù)覽功能
剛好我之前寫過一篇文章,關(guān)于 Java 14 的開箱體驗(yàn),很香,讀者朋友需要的話,可以點(diǎn)下面的鏈接看一看。
Java 14 開箱,它真香香香香
02、請說出 Java 13 版本中更新的重要功能
Java 13 發(fā)布于 2019 年 9 月 17 日,更新的重要功能有:
- 文本塊,預(yù)覽功能
- switch 表達(dá)式,預(yù)覽功能
- Java Socket 重新實(shí)現(xiàn)
- FileSystems.newFileSystem() 方法
- 支持 Unicode 12.1
- 可伸縮、低延遲的垃圾收集器改進(jìn),用于返回未使用的內(nèi)存
03、請說出 Java 12 版本中更新的重要功能
Java 12 發(fā)布于 2019 年 3 月 19 日,更新的重要功能有:
- JVM 更新
- File.mismatch() 方法
- 緊湊型數(shù)字格式
- String 類新增了一些方法,比如說 indent()
04、請說出 Java 11 版本中更新的重要功能
Java 11 是繼 Java 8 之后的第二個(gè)商用版本,如果你下載的是 Oracle JDK,則需要進(jìn)行付費(fèi);如果想繼續(xù)使用免費(fèi)版本,需要下載 Open JDK。
Oracle JDK 中會有一些 Open JDK 沒有的、商用閉源的功能。
Java 11 更新的重要功能有:
- 可以直接使用 java 命令運(yùn)行 Java 程序,源代碼將會隱式編譯和運(yùn)行。
- String 類新增了一些方法,比如說 isBlank()、lines()、strip() 等等。
- Files 類新增了兩個(gè)讀寫方法,readString() 和 writeString()。
- 可以在 Lambda 表達(dá)式中使用 var 作為變量類型。
05、請說出 Java 10 版本中更新的重要功能
Java 10 更新的重要功能有:
- 局部變量類型推斷,舉個(gè)例子,var list = new ArrayList();,可以使用 var 來作為變量類型,Java 編譯器知道 list 的類型為字符串的 ArrayList。
- 增強(qiáng) java.util.Locale。
- 提供了一組默認(rèn)的根證書頒發(fā)機(jī)構(gòu)(CA)。
06、請說出 Java 9 版本中更新的重要功能
Java 9 更新的重要功能有:
- 模塊系統(tǒng)
- 不可變的 List、Set、Map 的工廠方法
- 接口中可以有私有方法
- 垃圾收集器改進(jìn)
07、請說出 Java 8 版本中更新的重要功能
Java 8 發(fā)布于 2014 年 3 月份,可以說是 Java 6 之后最重要的版本更新,深受開發(fā)者的喜愛。
- 函數(shù)式編程和 Lambda 表達(dá)式
- Stream 流
- Java Date Time API
- 接口中可以使用默認(rèn)方法和靜態(tài)方法
我強(qiáng)烈建議點(diǎn)開上面的鏈接閱讀以下,以正確理解這些概念。
08、請說出 Java 面向?qū)ο缶幊讨械囊恍┲匾拍?/strong>
- 抽象
- 封裝
- 多態(tài)
- 繼承
09、Java 聲稱的平臺獨(dú)立性指的是什么?
常見的操作系統(tǒng)有 Windows、Linux、OS-X,那么平臺獨(dú)立性意味著我們可以在任何操作系統(tǒng)中運(yùn)行相同源代碼的 Java 程序,比如說我們可以在 Windows 上編寫 Java 程序,然后在 Linux 上運(yùn)行它。
10、什么是 JVM?
JVM(Java Virtual Machine)俗稱 Java 虛擬機(jī)。之所以稱為虛擬機(jī),是因?yàn)樗鼘?shí)際上并不存在。它提供了一種運(yùn)行環(huán)境,可供 Java 字節(jié)碼在上面運(yùn)行。
JVM 提供了以下操作:
- 加載字節(jié)碼
- 驗(yàn)證字節(jié)碼
- 執(zhí)行字節(jié)碼
- 提供運(yùn)行時(shí)環(huán)境
JVM 定義了以下內(nèi)容:
- 存儲區(qū)
- 類文件格式
- 寄存器組
- 垃圾回收堆
- 致命錯(cuò)誤報(bào)告等
我們來嘗試?yán)斫庖幌?JVM 的內(nèi)部結(jié)構(gòu),它包含了類加載器(Class Loader)、運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Areas)和執(zhí)行引擎(Excution Engine)。
1)類加載器
類加載器是 JVM 的一個(gè)子系統(tǒng),用于加載類文件。每當(dāng)我們運(yùn)行一個(gè) Java 程序,它都會由類加載器首先加載。Java 中有三個(gè)內(nèi)置的類加載器:
- 啟動類加載器(Bootstrap Class-Loader),加載 jre/lib 包下面的 jar 文件,比如說常見的 rt.jar(包含了 Java 標(biāo)準(zhǔn)庫下的所有類文件,比如說 java.lang 包下的類,java.net 包下的類,java.util 包下的類,java.io 包下的類,java.sql 包下的類)。
- 擴(kuò)展類加載器(Extension or Ext Class-Loader),加載 jre/lib/ext 包下面的 jar 文件。
- 應(yīng)用類加載器(Application or App Clas-Loader),根據(jù)程序的類路徑(classpath)來加載 Java 類。
一般來說,Java 程序員并不需要直接同類加載器進(jìn)行交互。JVM 默認(rèn)的行為就已經(jīng)足夠滿足大多數(shù)情況的需求了。不過,如果遇到了需要和類加載器進(jìn)行交互的情況,而對類加載器的機(jī)制又不是很了解的話,就不得不花大量的時(shí)間去調(diào)試
ClassNotFoundException 和 NoClassDefFoundError 等異常。
對于任意一個(gè)類,都需要由它的類加載器和這個(gè)類本身一同確定其在 JVM 中的唯一性。也就是說,如果兩個(gè)類的加載器不同,即使兩個(gè)類來源于同一個(gè)字節(jié)碼文件,那這兩個(gè)類就必定不相等(比如兩個(gè)類的 Class 對象不 equals)。
是不是有點(diǎn)暈,來來來,通過一段簡單的代碼了解下。
- public class Test {
- public static void main(String[] args) {
- ClassLoader loader = Test.class.getClassLoader();
- while (loader != null) {
- System.out.println(loader.toString());
- loaderloader = loader.getParent();
- }
- }
- }
每個(gè) Java 類都維護(hù)著一個(gè)指向定義它的類加載器的引用,通過 類名.class.getClassLoader() 可以獲取到此引用;然后通過 loader.getParent() 可以獲取類加載器的上層類加載器。
上面這段代碼的輸出結(jié)果如下:
- sun.misc.Launcher$AppClassLoader@18b4aac2
- sun.misc.Launcher$ExtClassLoader@4617c264
第一行輸出為 Test 的類加載器,即應(yīng)用類加載器,它是 sun.misc.Launcher$AppClassLoader 類的實(shí)例;第二行輸出為擴(kuò)展類加載器,是 sun.misc.Launcher$ExtClassLoader 類的實(shí)例。那啟動類加載器呢?
按理說,擴(kuò)展類加載器的上層類加載器是啟動類加載器,但在我這個(gè)版本的 JDK 中, 擴(kuò)展類加載器的 getParent() 返回 null。所以沒有輸出。
2)運(yùn)行時(shí)數(shù)據(jù)區(qū)
運(yùn)行時(shí)數(shù)據(jù)區(qū)又包含以下內(nèi)容。
- PC寄存器(PC Register),也叫程序計(jì)數(shù)器(Program Counter Register),是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的信號指示器。
- JVM 棧(Java Virtual Machine Stack),與 PC 寄存器一樣,JVM 棧也是線程私有的。每一個(gè) JVM 線程都有自己的 JVM 棧,這個(gè)棧與線程同時(shí)創(chuàng)建,它的生命周期與線程相同。
- 本地方法棧(Native Method Stack),JVM 可能會使用到傳統(tǒng)的棧來支持 Native 方法(使用 Java 語言以外的其它語言[C語言]編寫的方法)的執(zhí)行,這個(gè)棧就是本地方法棧。
- 堆(Heap),在 JVM 中,堆是可供各條線程共享的運(yùn)行時(shí)內(nèi)存區(qū)域,也是供所有類實(shí)例和數(shù)據(jù)對象分配內(nèi)存的區(qū)域。
- 方法區(qū)(Method area),在 JVM 中,被加載類型的信息都保存在方法區(qū)中。包括類型信息(Type Information)和方法列表(Method Tables)。方法區(qū)是所有線程共享的,所以訪問方法區(qū)信息的方法必須是線程安全的。
- 運(yùn)行時(shí)常量池(Runtime Constant Pool),運(yùn)行時(shí)常量池是每一個(gè)類或接口的常量池在運(yùn)行時(shí)的表現(xiàn)形式,它包括了編譯器可知的數(shù)值字面量,以及運(yùn)行期解析后才能獲得的方法或字段的引用。簡而言之,當(dāng)一個(gè)方法或者變量被引用時(shí),JVM 通過運(yùn)行時(shí)常量區(qū)來查找方法或者變量在內(nèi)存里的實(shí)際地址。
3)執(zhí)行引擎
執(zhí)行引擎包含了:
- 解釋器:讀取字節(jié)碼流,然后執(zhí)行指令。因?yàn)樗粭l一條地解釋和執(zhí)行指令,所以它可以很快地解釋字節(jié)碼,但是執(zhí)行起來會比較慢。
- 即時(shí)(Just-In-Time,JIT)編譯器:即時(shí)編譯器用來彌補(bǔ)解釋器的缺點(diǎn),提高性能。執(zhí)行引擎首先按照解釋執(zhí)行的方式來執(zhí)行,然后在合適的時(shí)候,即時(shí)編譯器把整段字節(jié)碼編譯成本地代碼。然后,執(zhí)行引擎就沒有必要再去解釋執(zhí)行方法了,它可以直接通過本地代碼去執(zhí)行。執(zhí)行本地代碼比一條一條進(jìn)行解釋執(zhí)行的速度快很多。編譯后的代碼可以執(zhí)行的很快,因?yàn)楸镜卮a是保存在緩存里的。
11、JDK 和 JVM 有什么區(qū)別?
JDK 是 Java Development Kit 的首字母縮寫,是提供給 Java 開發(fā)人員的軟件環(huán)境,包含 JRE 和一組開發(fā)工具。可分為以下版本:
- 標(biāo)準(zhǔn)版(大多數(shù)開發(fā)人員用的就是這個(gè))
- 企業(yè)版
- 微型版
JDK 包含了一個(gè)私有的 JVM 和一些其他資源,比如說編譯器(javac 命令)、解釋器(java 命令)等,幫助 Java 程序員完成開發(fā)工作。
12、JVM 和 JRE 有什么區(qū)別?
Java Runtime Environment(JRE)是 JVM 的實(shí)現(xiàn)。JRE 由 JVM 和 Java 二進(jìn)制文件以及其他類組成,可以執(zhí)行任何程序。JRE 不包含 Java 編譯器,調(diào)試器等任何開發(fā)工具。
13、哪個(gè)類是所有類的超類?
java.lang.Object 是所有 Java 類的超類,我們不需要繼承它,因?yàn)槭请[式繼承的。
14、為什么 Java 不支持多重繼承?
如果有兩個(gè)類共同繼承(extends)一個(gè)有特定方法的父類,那么該方法會被兩個(gè)子類重寫。然后,如果你決定同時(shí)繼承這兩個(gè)子類,那么在你調(diào)用該重寫方法時(shí),編譯器不能識別你要調(diào)用哪個(gè)子類的方法。這也正是著名的菱形問題,見下圖。
ClassC 同時(shí)繼承了 ClassA 和 ClassB,ClassC 的對象在調(diào)用 ClassA 和 ClassB 中重載的方法時(shí),就不知道該調(diào)用 ClassA 的方法,還是 ClassB 的方法。
15、為什么 Java 不是純粹的面向?qū)ο缶幊陶Z言?
之所以不能說 Java 是純粹的面向?qū)ο缶幊陶Z言,是因?yàn)?Java 支持基本數(shù)據(jù)類型,比如說 int、short、long、double 等,盡管它們有自己的包裝器類型,但它們的確不能算是對象。
16、path 和 classpath 之間有什么區(qū)別?
path 是操作系統(tǒng)用來查找可執(zhí)行文件的環(huán)境變量,我的電腦上就定義了下圖這些 path 變量,比如 Java 和 Maven 的。
classpath 是針對 Java 而言的,用于指定 Java 虛擬機(jī)載入的字節(jié)碼文件路徑。
17、Java 中 `main()` 方法的重要性是什么?
每個(gè)程序都需要一個(gè)入口,對于 Java 程序來說,入口就是 main 方法。
public static void main(String[] args) {}
- public 關(guān)鍵字是另外一個(gè)訪問修飾符,除了可以聲明方法和變量(所有類可見),還可以聲明類。main() 方法必須聲明為 public。
- static 關(guān)鍵字表示該變量或方法是靜態(tài)變量或靜態(tài)方法,可以直接通過類訪問,不需要實(shí)例化對象來訪問。
- void 關(guān)鍵字用于指定方法沒有返回值。
另外,main 關(guān)鍵字為方法的名字,Java 虛擬機(jī)在執(zhí)行程序時(shí)會尋找這個(gè)標(biāo)識符;args 為 main() 方法的參數(shù)名,它的類型為一個(gè) String 數(shù)組,也就是說,在使用 java 命令執(zhí)行程序的時(shí)候,可以給 main() 方法傳遞字符串?dāng)?shù)組作為參數(shù)。
- java HelloWorld 沉默王二 沉默王三
javac 命令用來編譯程序,java 命令用來執(zhí)行程序,HelloWorld 為這段程序的類名,沉默王二和沉默王三為字符串?dāng)?shù)組,中間通過空格隔開,然后就可以在 main() 方法中通過 args[0] 和 args[1] 獲取傳遞的參數(shù)值了。
- public class HelloWorld {
- public static void main(String[] args) {
- if ("沉默王二".equals(args[0])) {
- }
- if ("沉默王三".equals(args[1])) {
- }
- }
- }
main() 方法的寫法并不是唯一的,還有其他幾種變體,盡管它們可能并不常見,可以簡單來了解一下。
第二種,把方括號 [] 往 args 靠近而不是 String 靠近:
- public static void main(String []args) { }
第三種,把方括號 [] 放在 args 的右側(cè):
- public static void main(String args[]) { }
第四種,還可以把數(shù)組形式換成可變參數(shù)的形式:
- public static void main(String...args) { }
第五種,在 main() 方法上添加另外一個(gè)修飾符 strictfp,用于強(qiáng)調(diào)在處理浮點(diǎn)數(shù)時(shí)的兼容性:
- public strictfp static void main(String[] args) { }
也可以在 main() 方法上添加 final 關(guān)鍵字或者 synchronized 關(guān)鍵字。
第六種,還可以為 args 參數(shù)添加 final 關(guān)鍵字:
- public static void main(final String[] args) { }
第七種,最復(fù)雜的一種,所有可以添加的關(guān)鍵字統(tǒng)統(tǒng)添加上:
- final static synchronized strictfp void main(final String[] args) { }
當(dāng)然了,并不需要為了裝逼特意把 main() 方法寫成上面提到的這些形式,使用 IDE 提供的默認(rèn)形式就可以了。
18、Java 的重寫(Override)和重載(Overload)有什么區(qū)別?
先來看一段重寫的代碼吧。
- class LaoWang{
- public void write() {
- System.out.println("老王寫了一本《基督山伯爵》");
- }
- }
- public class XiaoWang extends LaoWang {
- @Override
- public void write() {
- System.out.println("小王寫了一本《茶花女》");
- }
- }
重寫的兩個(gè)方法名相同,方法參數(shù)的個(gè)數(shù)也相同;不過一個(gè)方法在父類中,另外一個(gè)在子類中。就好像父類 LaoWang 有一個(gè) write() 方法(無參),方法體是寫一本《基督山伯爵》;子類 XiaoWang 重寫了父類的 write() 方法(無參),但方法體是寫一本《茶花女》。
來寫一段測試代碼。
- public class OverridingTest {
- public static void main(String[] args) {
- LaoWang wang = new XiaoWang();
- wang.write();
- }
- }
大家猜結(jié)果是什么?
小王寫了一本《茶花女》
在上面的代碼中,們聲明了一個(gè)類型為 LaoWang 的變量 wang。在編譯期間,編譯器會檢查 LaoWang 類是否包含了 write() 方法,發(fā)現(xiàn) LaoWang 類有,于是編譯通過。在運(yùn)行期間,new 了一個(gè) XiaoWang 對象,并將其賦值給 wang,此時(shí) Java 虛擬機(jī)知道 wang 引用的是 XiaoWang 對象,所以調(diào)用的是子類 XiaoWang 中的 write() 方法而不是父類 LaoWang 中的 write() 方法,因此輸出結(jié)果為“小王寫了一本《茶花女》”。
再來看一段重載的代碼吧。
- class LaoWang{
- public void read() {
- System.out.println("老王讀了一本《Web全棧開發(fā)進(jìn)階之路》");
- }
- public void read(String bookname) {
- System.out.println("老王讀了一本《" + bookname + "》");
- }
- }
重載的兩個(gè)方法名相同,但方法參數(shù)的個(gè)數(shù)不同,另外也不涉及到繼承,兩個(gè)方法在同一個(gè)類中。就好像類 LaoWang 有兩個(gè)方法,名字都是 read(),但一個(gè)有參數(shù)(書名),另外一個(gè)沒有(只能讀寫死的一本書)。
來寫一段測試代碼。
- public class OverloadingTest {
- public static void main(String[] args) {
- LaoWang wang = new LaoWang();
- wang.read();
- wang.read("金瓶梅");
- }
- }
這結(jié)果就不用猜了。變量 wang 的類型為 LaoWang,wang.read() 調(diào)用的是無參的 read() 方法,因此先輸出“老王讀了一本《Web全棧開發(fā)進(jìn)階之路》”;wang.read("金瓶") 調(diào)用的是有參的 read(bookname) 方法,因此后輸出“老王讀了一本《金瓶》”。在編譯期間,編譯器就知道這兩個(gè) read() 方法時(shí)不同的,因?yàn)樗鼈兊姆椒ê灻?方法名稱+方法參數(shù))不同。
簡單的來總結(jié)一下:
1)編譯器無法決定調(diào)用哪個(gè)重寫的方法,因?yàn)橹粡淖兞康念愋蜕鲜菬o法做出判斷的,要在運(yùn)行時(shí)才能決定;但編譯器可以明確地知道該調(diào)用哪個(gè)重載的方法,因?yàn)橐妙愋褪谴_定的,參數(shù)個(gè)數(shù)決定了該調(diào)用哪個(gè)方法。
2)多態(tài)針對的是重寫,而不是重載。
- 如果在一個(gè)類中有多個(gè)相同名字的方法,但參數(shù)不同,則稱為方法重載。
- 父類中有一個(gè)方法,子類中有另外一個(gè)和它有相同簽名(方法名相同,參數(shù)相同、修飾符相同)的方法時(shí),則稱為方法重寫。子類在重寫父類方法的時(shí)候可以加一個(gè) @Override 注解。
19、`main()` 方法可以重載嗎?
可以,一個(gè)類中可以有多個(gè)名稱為“main”的方法:
- public class MainTest {
- public static void main(String[] args) {
- System.out.println("main(String[] args)");
- }
- public static void main(String[] args,String arg) {
- System.out.println("(String[] args,String arg");
- }
- }
但該類在運(yùn)行的時(shí)候,只會找到一個(gè)入口,即 public static void main(String[] args)。
20、一個(gè) Java 源文件中有多個(gè) public 類嗎?
一個(gè) Java 源文件中不能有多個(gè) public 類。
21、什么是 Java 的 package(包)?
在 Java 中,我們使用 package(包)對相關(guān)的類、接口和子包進(jìn)行分組。這樣做的好處有:
- 使相關(guān)類型更容易查找
- 避免命名沖突,比如說 com.itwanger.Hello 和 com.itwangsan.Hello 不同
- 通過包和訪問權(quán)限控制符來限定類的可見性
可以使用 package 關(guān)鍵字來定義一個(gè)包名,需要注意的是,這行代碼必須處于一個(gè)類中的第一行。強(qiáng)烈建議在包中聲明類,不要缺省,否則就失去了包結(jié)構(gòu)的帶來的好處。
包的命名應(yīng)該遵守以下規(guī)則:
- 應(yīng)該全部是小寫字母
- 可以包含多個(gè)單詞,單詞之間使用“.”連接,比如說 java.lang
- 名稱由公司名或者組織名確定,采用倒序的方式,比如說,我個(gè)人博客的域名是 www.itwanger.com,所以我創(chuàng)建的包名是就是 com.itwanger.xxxx。
每個(gè)包或者子包都在磁盤上有自己的目錄結(jié)構(gòu),如果 Java 文件時(shí)在 com.itwanger.xxxx 包下,那么該文件所在的目錄結(jié)構(gòu)就應(yīng)該是 com->itwanger->xxxx。
默認(rèn)情況下,java.lang 包是默認(rèn)導(dǎo)入的,我們不需要顯式地導(dǎo)入該包下的任何類。
- package com.cmower.bb;
- public class PackageTest {
- public static void main(String[] args) {
- Boolean.toString(true);
- }
- }
Boolean 類屬于 java.lang 包,當(dāng)使用它的時(shí)候并不需要顯式導(dǎo)入。
22、什么是訪問權(quán)限修飾符?
訪問權(quán)限修飾符對于 Java 來說,非常重要,目前共有四種:public、private、protected 和 default(缺?。?/p>
一個(gè)類只能使用 public 或者 default 修飾,public 修飾的類你之前已經(jīng)見到過了,現(xiàn)在我來定義一個(gè)缺省權(quán)限修飾符的類給你欣賞一下。
- class Dog {
- }
哈哈,其實(shí)也沒啥可以欣賞的。缺省意味著這個(gè)類可以被同一個(gè)包下的其他類進(jìn)行訪問;而 public 意味著這個(gè)類可以被所有包下的類進(jìn)行訪問。
假如硬要通過 private 和 protected 來修飾類的話,編譯器會生氣的,它不同意。
private 可以用來修飾類的構(gòu)造方法、字段和方法,只能被當(dāng)前類進(jìn)行訪問。protected 也可以用來修飾類的構(gòu)造方法、字段和方法,但它的權(quán)限范圍更寬一些,可以被同一個(gè)包中的類進(jìn)行訪問,或者當(dāng)前類的子類。
可以通過下面這張圖來對比一下四個(gè)權(quán)限修飾符之間的差別:
- 同一個(gè)類中,不管是哪種權(quán)限修飾符,都可以訪問;
- 同一個(gè)包下,private 修飾的無法訪問;
- 子類可以訪問 public 和 protected 修飾的;
- public 修飾符面向世界,哈哈,可以被所有的地方訪問到。
23、什么是 final 關(guān)鍵字?
final 關(guān)鍵字修飾類的時(shí)候,表示該類無法被繼承。比如,String 類就是 final 的,無法被繼承。
final 關(guān)鍵字修飾方法的時(shí)候,表示子類無法覆蓋它。
final 關(guān)鍵字修飾變量的時(shí)候,表示該變量只能被賦值一次,盡管變量的狀態(tài)可以更改。
關(guān)于 final 更詳細(xì)的內(nèi)容,可以參照我之前寫了另外一篇文章:
我去,你竟然還不會用 final 關(guān)鍵字
24、什么是 static 關(guān)鍵字?
static 關(guān)鍵字可以用來修飾類變量,使其具有全局性,即所有對象將共享同一個(gè)變量。
static 關(guān)鍵字可以用來修飾方法,該方法稱為靜態(tài)方法,只可以訪問類的靜態(tài)變量,并且只能調(diào)用類的靜態(tài)方法。
關(guān)于 static 更詳細(xì)的內(nèi)容,可以參照我之前寫了另外一篇文章:
面試官:兄弟,說說Java的static關(guān)鍵字吧
25、finally 和 finalize 有什么區(qū)別?
finally 通常與 try-catch 塊一起使用,即使 try-catch 塊引發(fā)了異常,finally 塊中的代碼也會被執(zhí)行,用于釋放 try 塊中創(chuàng)建的資源。
finalize() 是 Object 類的一個(gè)特殊方法,當(dāng)對象正在被垃圾回收時(shí),垃圾收集器將會調(diào)用該方法??梢灾貙懺摲椒ㄓ糜卺尫畔到y(tǒng)資源。
26、可以將一個(gè)類聲明為 static 的嗎?
不能將一個(gè)外部類聲明為 static 的,但可以將一個(gè)內(nèi)部類聲明為 static 的——稱為靜態(tài)內(nèi)部類。
27、什么是靜態(tài)導(dǎo)入?
如果必須在一個(gè)類中使用其他類的靜態(tài)變量或者靜態(tài)方法,通常我們需要先導(dǎo)入該類,然后使用“類名.變量/方法”的形式調(diào)用。
- import java.lang.Math;
- double test = Math.PI * 5;
也可以通過靜態(tài)導(dǎo)入的方式,就不需要再使用類名了。
- import static java.lang.Math.PI;
- double test = PI * 5;
不過,靜態(tài)導(dǎo)入容易引發(fā)混亂(變量名或者方法名容易沖突),因此最好避免使用靜態(tài)導(dǎo)入。
28、什么是 try-with-resources?
try-with-resources 是 Java 7 時(shí)引入的一個(gè)自動資源管理語句,在此之前,我們必須通過 try-catch-finally 的方式手動關(guān)閉資源,當(dāng)我們忘記關(guān)閉資源的時(shí)候,就容易導(dǎo)致內(nèi)存泄漏。
關(guān)于 try-with-resources 更詳細(xì)的內(nèi)容,可以參照我之前寫了另外一篇文章:
我去,你竟然還在用 try–catch-finally
29、什么是 multi-catch?
Java 7 改進(jìn)的另外一個(gè)地方就是 multi-catch,可以在單個(gè) catch 中捕獲多個(gè)異常,當(dāng)一個(gè) try 塊拋出多個(gè)類似的異常時(shí),這種寫法更短,更清晰。
- catch(IOException | SQLException ex){
- logger.error(ex);
- throw new MyException(ex.getMessage());
- }
當(dāng)有多個(gè)異常的時(shí)候,可以使用管道表示符“|”隔開。
30、什么是 static 塊?
static 塊是由 Java ClassLoader 將類加載到內(nèi)存中時(shí)執(zhí)行的代碼塊。通常用于初始化類的靜態(tài)變量或者創(chuàng)建靜態(tài)資源。
31、什么是接口?
接口是 Java 編程語言中的一個(gè)核心概念,不僅在 JDK 源碼中使用很多,還在 Java 設(shè)計(jì)模式、框架和工具中使用很多。接口提供了一種在 Java 中實(shí)現(xiàn)抽象的方法,用于定義子類的行為約定。
關(guān)于接口更詳細(xì)的內(nèi)容,可以參照我之前寫了另外一篇文章:
可能是把 Java 接口講得最通俗的一篇文章
鳴謝
說句實(shí)在話,這 31 道 Java 核心面試題在面試的過程中還是很常見的,值得好好復(fù)習(xí)一遍。