硬核!20 道經典 Java 基礎面試題
前言
大家好,我是陌溪
最近整理了 20 道 Java 基礎面試題,大家一起加油哈
1. ArrayList 和 LinkedList 有什麼區別?
可以從它們的底層數據結構、效率、開銷進行闡述哈
-
ArrayList 是數組的數據結構,LinkedList 是鏈表的數據結構。
-
隨機訪問的時候,ArrayList 的效率比較高,因爲 LinkedList 要移動指針,而 ArrayList 是基於索引 (index) 的數據結構,可以直接映射到。
-
插入、刪除數據時,LinkedList 的效率比較高,因爲 ArrayList 要移動數據。
-
LinkedList 比 ArrayList 開銷更大,因爲 LinkedList 的節點除了存儲數據,還需要存儲引用。
2. final, finally, finalize 的區別
-
final 用於聲明屬性, 方法和類, 分別表示屬性不可變, 方法不可覆蓋, 類不可繼承.
-
finally 是異常處理語句結構的一部分,表示總是執行.
-
finalize 是 Object 類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關閉文件等. JVM 不保證此方法總被調用.
3. 重寫和重載的區別
-
作用範圍:重寫的作用範圍是父類和子類之間;重載是發生在一個類裏面
-
參數列表:重載必須不同;重寫不能修改
-
返回類型:重載可修改;重寫方法返回相同類型或子類
-
拋出異常:重載可修改;重寫可減少或刪除,一定不能拋出新的或者更廣的異常
-
訪問權限:重載可修改;重寫一定不能做更嚴格的限制
4. Java 8 的接口新增了哪些特性?
JDK8 的新特性
-
lambada 表達式
-
函數式接口
-
方法引用
-
默認方法
-
Stream API
-
Optional
-
Date Time API(如 LocalDate)
-
重複註解
-
Base64
-
JVM 的新特性(如元空間 Metaspace 代替持久代)
5. 兩個對象的 hashCode() 相同,則 equals() 是否也一定爲 true?
兩個對象 equals 相等,則它們的 hashcode 必須相等,如果兩個對象的 hashCode() 相同,則 equals() 不一定爲 true。
hashCode 的常規協定:
在 Java 應用程序執行期間,在對同一對象多次調用 hashCode 方法時,必須一致地返回相同的整數,前提是將對象進行 equals 比較時所用的信息沒有被修改。從某一應用程序的一次執行到同一應用程序的另一次執行,該整數無需保持一致。
兩個對象的 equals() 相等,那麼對這兩個對象中的每個對象調用 hashCode 方法都必須生成相同的整數結果。
兩個對象的 equals() 不相等,那麼對這兩個對象中的任一對象上調用 hashCode 方法不要求一定生成不同的整數結果。但是,爲不相等的對象生成不同整數結果可以提高哈希表的性能。
6. 抽象類和接口有什麼區別
-
抽象類要被子類繼承,接口要被子類實現。
-
抽象類可以有構造方法,接口中不能有構造方法。
-
抽象類中可以有普通成員變量,接口中沒有普通成員變量,它的變量只能是公共的靜態的常量
-
一個類可以實現多個接口,但是隻能繼承一個父類,這個父類可以是抽象類。
-
接口只能做方法聲明,抽象類中可以作方法聲明,也可以做方法實現。
-
抽象級別(從高到低):接口 > 抽象類 > 實現類。
-
抽象類主要是用來抽象類別,接口主要是用來抽象方法功能。
-
抽象類的關鍵字是 abstract,接口的關鍵字是 interface
7. BIO、NIO、AIO 有什麼區別?
BIO 是同步並阻塞的,NIO 是同步非阻塞,AIO 是異步非阻塞。
BIO:線程發起 IO 請求,不管內核是否準備好 IO 操作,從發起請求起,線程一直阻塞,直到操作完成。
NIO:線程發起 IO 請求,立即返回;內核在做好 IO 操作的準備之後,通過調用註冊的回調函數通知線程做 IO 操作,線程開始阻塞,直到操作完成。
AIO:線程發起 IO 請求,立即返回;內存做好 IO 操作的準備之後,做 IO 操作,直到操作完成或者失敗,通過調用註冊的回調函數通知線程做 IO 操作完成或者失敗。
BIO 是一個連接一個線程。,NIO 是一個請求一個線程。,AIO 是一個有效請求一個線程。
BIO:同步並阻塞,服務器實現模式爲一個連接一個線程,即客戶端有連接請求時服務器端就需要啓動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。
NIO:同步非阻塞,服務器實現模式爲一個請求一個線程,即客戶端發送的連接請求都會註冊到多路複用器上,多路複用器輪詢到連接有 I/O 請求時才啓動一個線程進行處理。
AIO:異步非阻塞,服務器實現模式爲一個有效請求一個線程,客戶端的 IO 請求都是由 OS 先完成了再通知服務器應用去啓動線程進行處理。
8. String,Stringbuffer,StringBuilder 的區別
String:
-
String 類是一個不可變的類,一旦創建就不可以修改。
-
String 是 final 類,不能被繼承
-
String 實現了 equals() 方法和 hashCode() 方法
StringBuffer:
-
繼承自 AbstractStringBuilder,是可變類。
-
StringBuffer 是線程安全的
-
可以通過 append 方法動態構造數據。
StringBuilder:
-
繼承自 AbstractStringBuilder,是可變類。
-
StringBuilder 是非線性安全的。
-
執行效率比 StringBuffer 高。
9. 靜態代理和動態代理的區別
靜態代理中代理類在編譯期就已經確定,而動態代理則是 JVM 運行時動態生成,靜態代理的效 率相對動態代理來說相對高一些,但是靜態代理代碼冗餘大,一單需要修改接口,代理類和委 託類都需要修改。
10. JAVA 中的幾種基本數據類型是什麼,各自佔用多少字節呢
對於 boolean,官方文檔未明確定義,它依賴於 JVM 廠商的具體實現。邏輯上理解是佔用 1 位,但是實際中會考慮計算機高效存儲因素
感興趣的小夥伴,可以去看官網
11. Comparator 與 Comparable 有什麼區別?
-
Comparable & Comparator 都是用來實現集合中元素的比較、排序的,只是 Comparable 是在集合內部定義的方法實現的排序,Comparator 是在集合外部實現的排序,所以,如想實現排序,就需要在集合外定義 Comparator 接口的方法或在集合內實現 Comparable 接口的方法。
-
Comparator 位於包 java.util 下,而 Comparable 位於包 java.lang 下。
-
Comparable 是一個對象本身就已經支持自比較所需要實現的接口(如 String、Integer 自己就可以完成比較大小操作,已經實現了 Comparable 接口) 自定義的類要在加入 list 容器中後能夠排序,可以實現 Comparable 接口,在用 Collections 類的 sort 方法排序時,如果不指定 Comparator,那麼就以自然順序排序, 這裏的自然順序就是實現 Comparable 接口設定的排序方式。
-
而 Comparator 是一個專用的比較器,當這個對象不支持自比較或者自比較函數不能滿足你的要求時,你可以寫一個比較器來完成兩個對象之間大小的比較。
-
可以說一個是自已完成比較,一個是外部程序實現比較的差別而已。用 Comparator 是策略模式(strategy design pattern),就是不改變對象自身,而用一個策略對象(strategy object)來改變它的行爲。比如:你想對整數採用絕對值大小來排序,Integer 是不符合要求的,你不需要去修改 Integer 類(實際上你也不能這麼做)去改變它的排序行爲,只要使用一個實現了 Comparator 接口的對象來實現控制它的排序就行了。
12. JDK 動態代理和 CGLIB 動態代理的區別
-
JDK 動態代理只能對實現了接口的類生成代理,而不能針對類。
-
CGLIB 是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法。因爲是繼承, 所以該類或方法最好不要聲明成 final。
13. String 類能被繼承嗎,爲什麼。
首先,String 是一個 final 修飾的類,final 修飾的類不可以被繼承。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
String 類爲什麼不能被繼承呢?
有兩個原因:
-
效率性,String 類作爲最常用的類之一,禁止被繼承和重寫,可以提高效率。
-
安全性,String 類中有很多調用底層的本地方法,調用了操作系統的 API,如果方法可以重寫,可能被植入惡意代碼,破壞程序。
14. 說說 Java 中多態的實現原理
-
多態機制包括靜態多態(編譯時多態)和動態多態(運行時多態)
-
靜態多態比如說重載,動態多態一般指在運行時才能確定調用哪個方法。
-
我們通常所說的多態一般指運行時多態,也就是編譯時不確定究竟調用哪個具體方法,一直等到運行時才能確定。
-
多態實現方式:子類繼承父類(extends)和類實現接口(implements)
-
多態核心之處就在於對父類方法的改寫或對接口方法的實現,以取得在運行時不同的執行效果。
-
Java 裏對象方法的調用是依靠類信息裏的方法表實現的,對象方法引用調用和接口方法引用調用的大致思想是一樣的。當調用對象的某個方法時,JVM 查找該對象類的方法表以確定該方法的直接引用地址,有了地址後才真正調用該方法。
舉個例子吧,假設有個 Fruit 父類,一個 taste 味道方法,兩個子類 Apple 和 Pear,如下:
abstract class Fruit {
abstract String taste() ;
}
class Apple extends Fruit {
@Override
String taste() {
return "酸酸的";
}
}
class Pear extends Fruit {
@Override
String taste() {
return "甜甜的";
}
}
public class Test {
public static void main(String[] args) {
Fruit f = new Apple();
System.out.println(f.taste());
}
}
程序運行,當調用對象 Fruit f 的方法 taste 時,JVM 查找 Fruit 對象類的方法表以確定 taste 方法的直接引用地址,到底來自 Apple 還是 Pear,確定後才真正調用對應子類的 taste 方法,
15. 說下面向對象四大特性
封裝、繼承、多態、抽象。
16. int 和 Integer 有什麼區別,還有 Integer 緩存的實現
這裏考察 3 個知識點吧:
-
int 是基本數據類型,interger 是 int 的封裝類
-
int 默認值爲 0 ,而 interger 默認值爲 null, Interger 使用需要判空處理
-
Integer 的緩存機制:爲了節省內存和提高性能,Integer 類在內部通過使用相同的對象引用實現緩存和重用,Integer 類默認在 - 128 ~ 127 之間,可以通過 -XX:AutoBoxCacheMax 進行修改,且這種機制僅在自動裝箱的時候有用,在使用構造器創建 Integer 對象時無用。
看個 Integer 的緩存的例子,加深一下印象哈:
Integer a = 10;
Integer b = 10;
Integer c = 129;
Integer d = 129;
System.out.println(a == b);
System.out.println(c == d);
輸出結果:
true
false
17. 說說反射的用途及實現原理,Java 獲取反射的三種方法
這道面試題,看我這篇文章哈:談談 Java 反射:從入門到實踐,再到原理
Java 獲取反射的三種方法:
-
第一種,使用 Class.forName 靜態方法。
-
第二種,使用類的. class 方法
-
第三種,使用實例對象的 getClass() 方法。
18. & 和 && 的區別
-
按位與, a&b 表示把 a 和 b 都轉換成二進制數,再進行與的運算;
-
& 和 && 都是邏輯運算符號,&& 又叫短路運算符
-
邏輯與,a&& b ,a&b 都表示當且僅當兩個操作數均爲 true 時,其結果才爲 true,否則爲 false。
-
邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運算符左右兩端的布爾值都是 true,整個表達式的值纔是 true。但是,&& 之所以稱爲短路運算,是因爲如果 && 左邊的表達式的值是 false,右邊的表達式會被直接短路掉,不會進行運算。
19. Java 中 IO 流分爲幾種?
-
Java 中的流分爲兩種:一種是字節流,另一種是字符流。
-
IO 流分別由四個抽象類來表示(兩輸入兩輸出):InputStream,OutputStream,Reader,Writer。
20.Java 有哪些數據類型
定義:Java 語言是強類型語言,對於每一種數據都定義了明確的具體的數據類型,在內存中分 配了不同大小的內存空間。
基本數據類型
-
數值型
-
整數類型 (byte,short,int,long)
-
浮點類型 (float,double)
-
字符型 (char)
-
布爾型 (boolean)
引用數據類型
-
類 (class)
-
接口 (interface)
-
數組 ([])
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/yCsAPhPsc5Cn8yJF7CoGGw