就因為沒看這篇文章面試失敗了
阿新 • • 發佈:2020-07-26
# 前言
熬夜整理了一份java基礎面試題,希望大家支援,如果文中有錯誤希望大家指正;
公眾號:**知識追尋者**
> 知識追尋者(Inheriting the spirit of open source, Spreading technology knowledge;)
# 一 java基礎面試
## 1.1面向物件和麵向過程的區別
- 面向過程:
優點:**效能比面向物件高**,因為類呼叫時需要例項化,開銷比較大,比較消耗 資源
應用場景:微控制器、嵌入式開發、Linux/Unix ;
缺點:沒有面向物件易維護、易複用、易擴充套件
- 面向物件
優點:因為面向物件有封裝、繼承、多型性的特 性,可以設計出低耦合的系統,故易維護、易複用、易擴充套件;
應用場景:網頁開發,後臺開發等;
缺點:效能比面向過程低
## 1.2 面向物件的特性
- **封裝**: 將一個物件的屬性私有化,並提供一個對外訪問的方法;
- **繼承**:在已有類的基礎上建立新類;提供繼承資訊的類被稱為父類(超類、基類);得到繼 承資訊的類被稱為子類(派生類)
- **多型**:一個物件的多種表現形態;用同樣的物件引用呼叫同樣的方法但是做了不同的事情;可以向上轉型和向下轉型
多型實現形式:
重寫:子類對父類方法的重寫;
覆蓋:實現介面,並覆蓋方法;
## 1.3 抽象
抽象是指將物件抽象為具體類的過程;抽象只關注物件有哪些屬性和行為(方法);
## 1.4 Java語言特點
1. 簡單易學;
2. 面向物件(封裝,繼承,多型);
3. 跨平臺(Java虛擬機器實現平臺無關性);
4. 可靠性;
5. 安全性;
6. 支援多執行緒
7. 支援編譯與解釋;
8. 支援網路程式設計
## 1.5 JDK,JRE,JVM之間的區別
**JDK**: java開發工具包;包含JRE, javac等調優診斷工具;能夠建立和編譯程式;
**JRE**: java執行環境; 包括 Java 虛擬機器(JVM),Java 類庫,java 命令和其他的一些基礎構件;不能建立程式;
**JVM**:java虛擬機器,提供執行位元組碼檔案(.class)的環境支援;
> jdk 包含jre ; jre 包含 jvm
## 1.6 面向物件五大基本原則
- 單一職責原則SRP(Single Responsibility Principle);設計類時要功能單一;
- 開放封閉原則OCP(Open-Close Principle);一個模組對於拓展是開放的,對於修改是封閉;
- 裡式替換原則LSP(the Liskov Substitution Principle LSP);子類可以替換父類;
- 依賴倒置原則DIP(the Dependency Inversion Principle DIP);高層次的模組不應該依賴於低層次的模組,他們都應該依賴於抽象。抽象不應該依賴於具體實現,具體實現應該依賴於抽象;
- 介面分離原則ISP(the Interface Segregation Principle ISP);設計類時,功能介面拆分為多個;
> 開發設計類時需考慮的事情,面試中如果碰見能答幾個就幾個;
## 1.7 什麼是java主類
java主類是java程式碼執行的入口,即包含 main方法的類;
## 1.8 構造器能否被重寫
子類無法繼承父類的構造器,所以構造器不能被重寫(overidde),但可以被過載(overload);
## 1.9 過載和重寫的區別
`重寫 `:
- 發生在父類與子類之間
- 方法名,引數列表,必須相同,返回值小於等於父類;
- 訪問修飾符大於等於父類(public>protected>default>private);父類方法為private,則無法重寫;
- 重丟擲異常的範圍要小於等於父類異常;
`過載`:
- 發生在同一個類中
- 方法名相同引數列表不同(引數型別不同、個數不同、順序不同);
- 方法返回值和訪問修飾符可以不同;
## 1.10 equals與==的區別
**==** : 判定是否是相同一個物件 ,比較的是變數(棧)記憶體中存放的物件的(堆)記憶體地址,用來判斷兩個物件的地址是否相同,;
**equals**: equals用來比較的是兩個物件的內容是否相等; **但Object中的equals方法返回的卻是==的判斷**;
## 1.11什麼是hashCode
hashCode() 的作用是獲取雜湊碼,也稱為雜湊碼;它實際上一個int整數;雜湊碼的作用是確定該物件在雜湊表中的索引位置;散列表儲存的是鍵值對(key-value),即能根據鍵獲取值;
當一個物件插入散列表時先會比較物件與散列表中已有的物件hash值,若不同,則直接插入散列表,若相同(hash碰撞),則會呼叫equals 方法檢查是否真的相同, 如果equal()判斷不相等,直接將該元素放入集合中,否則不放入;
**物件中hashCode()與equals()的關係**
- 如果兩個物件相等,則hashcode也相等;
- 兩個物件相等,對兩個物件分別呼叫equals方法都返回true
- 兩個物件有相同的hashcode值,它們不一定是同一個物件;
- equals 方法被覆蓋過,則 hashCode 方法也必須被覆蓋;
## 1.12 什麼是值傳遞和引用傳遞
**值傳遞**:傳遞了物件的一個副本,即使副本被改變,也不會影響源物件;
**引用傳遞**:著傳遞的並不是實際的物件,而是物件的引用;對外部物件的改變會反映到實際物件;
> 一般認為,Java 內的傳遞都是值傳遞,Java 中例項物件的傳遞是引用傳遞;
## 1.13 什麼是抽象類與介面
- 抽象類是對類的抽象,是一種模板設計; 而介面是行為的抽象,可以理解為行為的規範;
- 抽象類中可以包含非抽象方法;而介面是絕對的抽象方法;
- 介面預設是public 方法,java8中介面支援預設(default)方法;
- 一個類可以實現多個介面,但只能實現一個抽象類;
- 抽象類不能使用final修飾(final修飾的類為固定類,無法被繼承)
## 1.14String、String StringBuffer 和 StringBuilder 的區別
- String是隻讀字串,並不是基本資料型別,而是一個物件;其無法改變;每次操作都會產生新物件;
- StringBuilder 並沒有對方法進行加同步鎖,執行緒不安全;
- StringBuffer 對方法加了同步鎖,執行緒安全;
> tip: 資料量少的時候使用 String; 單執行緒使用StringBuilder ; 多執行緒使用 StringBuffer ; 使用StringBuilder 效能比 StringBuffer 效能 提升大概有 10%~15%;
## 1.15 用最有效率的方法計算 2 乘以 8
2 << 3 = 8 ; 2 左移3位
## 1.16 & 與 && 的區別
& 運算子 按位與;
&& 運算子是短路與:&& 左邊的表示式的值是 false,右邊的表示式會被直接短路掉,不會進行運算
## 1.17 static 關鍵字
static 變數在 Java 中屬於類的,它在所有的例項中具有相同的值。當類被 Java 虛擬機器載入的時候,會對 static 變數進行初始化;故需要用例項來訪問 static 變數;
## 1.18 Java 支援的資料型別
1. 整數值型:byte,short,int,long;
2. 字元型:char;
3. 浮點型別:float,double;
4. 布林型:boolean;
| 型別 | 位數 | 位元組數 |
| :----- | :--- | :----- |
| short | 2 | 16 |
| int | 4 | 32 |
| long | 8 | 64 |
| float | 4 | 32 |
| double | 8 | 64 |
| char | 2 | 16 |
## 1.19 final, finally, finalize 的區別
- final:用於宣告屬性,方法和類, 分別表示屬性不可變, 方法不可覆蓋, 類不可繼承.
- finally:異常處理語句結構的一部分,表示最後總是會執行.
- finalize:Object類的一個方法,在垃圾收集器執行的時候會呼叫被回收物件的此方法;
## 1.20 instanceof 關鍵字
instanceof 嚴格來說是Java中的一個雙目運算子,用來檢查一個物件是否為一個類的例項;
## 1.21 為什麼不能用浮點型表示金額
浮點數為非精確值,應該使用BigDecimal來修飾金額;
## 1.22 自動裝箱與拆箱
- 裝箱:自動將基本資料型別轉換為包裝器型別,呼叫 Integer的valueOf(int) 方法;
- 拆箱:自動將包裝器型別轉換為基本資料型別。呼叫Integer的intValue方法
> tip: int 是基礎資料型別,佔用空間小; Integer 是物件佔用空間大;
## 1.23 switch中能否使用string做引數
在idk 1.7之前,switch只能支援byte, short, char, int或者其對應的封裝類以及Enum型別。從idk 1.7之後switch開始支援String。
> 可以用在byte上,但是不能用在long上。
## 1.24 java 建立物件的幾種方式
1. 採用new
2. 通過反射
3. 採用clone
4. 通過序列化機制
## 1.25 如何將byte轉為String
可以使用 String 接收 byte[] 引數的構造器來進行轉換, 但編碼必須正確;
## 1.26 final有哪些用法
1.被final修飾的類不可以被繼承
2.被final修飾的方法不可以被重寫
3.被final修飾的變數不可以被改變。
5.被final修飾的常量,在編譯階段會存入常量池中。
## 1.27 java當中的四種引用
1. 強引用:如果一個物件具有強引用,它就不會被垃圾回收器回收。即使當前記憶體空間不足,JVM也不會回收它,而是丟擲 OutOfMemoryError 錯誤,使程式異常終止。如果想中斷強引用和某個物件之間的關聯,可以顯式地將引用賦值為null,這樣一來的話,JVM在合適的時間就會回收該物件。
2. 軟引用:在使用軟引用時,如果記憶體的空間足夠,軟引用就能繼續被使用,而不會被垃圾回收器回收,只有在記憶體不足時,軟引用才會被垃圾回收器回收。
3. 弱引用:具有弱引用的物件擁有的生命週期更短暫。因為當 JVM 進行垃圾回收,一旦發現弱引用物件,無論當前記憶體空間是否充足,都會將弱引用回收。
4. 虛引用:如果一個物件僅持有虛引用,那麼它相當於沒有引用,在任何時候都可能被垃圾回收器回收。
## 1.28 Math. round(-1. 5) 等於多少?
Math.round(-1.5)的返回值是-1。四捨五入的原理是在引數上加0.5然後做向下取整
## 1.29 String str="i"與 String str=new String("i")一樣嗎?
tring str="i"的方式,Java 虛擬機器會將其分配到常量池中;而 String str=new String("i") 則會被分到堆記憶體中;所以一個是常量,一個是物件,不一樣;
## 1.30char 型變數中能不能儲存一箇中文漢字
char 型別可以儲存一箇中文漢字,因為 Java 中使用的編碼是 Unicode,一個 char 型別佔 2 個位元組,所以能放一箇中文。
## 1.31 break 和 continue 的區別
- break 跳出整個迴圈。
- continue 用於跳過本次迴圈,執行下次迴圈。
## 1.32 內部類與靜態內部類的區別
**內部類:**
1、內部類中的變數和方法不能宣告為靜態。
2、內部類例項化:B是A的內部類,例項化B:`A.B b = new A().new B()`。
3、內部類可以引用外部類的靜態或者非靜態屬性及方法。
**靜態內部類:**
1、靜態內部類屬性和方法可以宣告為靜態的或者非靜態的。
2、例項化靜態內部類:B是A的靜態內部類,`A.B b = new A.B()`。
3、靜態內部類只能引用外部類的靜態的屬性及方法。
## 1.33 throw和throws的區別?
- throw用於主動丟擲java.lang.Throwable 類的一個例項化物件,即通過關鍵字 throw 丟擲一個 Error 或者 一個Exception;
- throws 的作用是作為方法宣告和簽名的一部分;
## 1.34 error和exception的區別
- Error類和Exception類的父類都是throwable類
- Error類一般是指與虛擬機器相關的問題,如系統崩潰,虛擬機器錯誤,記憶體空間不足,方法呼叫棧溢等。直接終止程式即可;
- Exception類表示程式可以處理的異常,可以捕獲和有可能恢復。需要程式設計師手動處理;
## 1.35 什麼時候用斷言(assert)
斷言是一個包含布林表示式的語句,在執行這個語句時假定該表示式為true;如果表示式的值為false,那麼系統會報告一個AssertionError。
斷言有兩種形式:
- `assert Expression1`;表示一個boolean表示式
- `assert Expression1 : Expression2`; Expression2表示一個基本型別、表示式或者是一個Object,用於在失敗時輸出錯誤資訊
```
assert false;
assert i == 0:"123";// 當 i不等於0 時會輸出錯誤資訊
```
## 1.36 常見的五種執行時異常
- ClassCastException(類轉換異常)
- IndexOutOfBoundsException(陣列越界)
- NullPointerException(空指標異常)
- ArrayStoreException(資料儲存異常,運算元組是型別不一致)
- BufferOverflowException(快取溢位異常)
# 二 IO流面試
## 2.1 序列化的含義
- 序列化:將物件寫入到IO流中
- 反序列化:從IO流中恢復物件
**序列化機制將序列化的Java物件轉換為位位元組序列,這些位元組序列可以儲存在磁碟上,或通過網路傳輸,以達到以後恢復成原來的物件,通常被序列化的要實現Serializable介面,並指定序列值**
> Externalizable 可以控制整個序列化過程,指定特定的二進位制格式,增加安全機制
## 2.2 java 中 IO 流分類
- 按照流的流向分,可以分為輸入流和輸出流;
- 按照操作單元劃分,可以劃分為位元組流和字元流;
- 按照流的角色劃,可以分為節點流和處理流。
- InputStream/Reader: 所有的輸入流的基類,前者是位元組輸入流,後者是字元輸入流。
- OutputStream/Writer: 所有輸出流的基類,前者是位元組輸出流,後者是字元輸出流。
圖片來源網路:
![](https://gitee.com/lsc180/images/raw/master/img/20200726005822.png)
## 2.3 BIO、NIO、AIO的區別
1. BIO 就是傳統的 `java.io` 包,它是基於流模型實現的,互動的方式是同步、**阻塞方式IO**; 在讀入輸入流或者輸出流時,在讀寫完成之前,執行緒會一直處於阻塞狀態。
2. NIO (New IO)是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以構建多路複用的、同步非阻塞 IO 程式。
3. AIO(Asynchronous IO) 是 Java 1.7 之後引入的包,是 NIO 的升級版本,提供了非同步非堵塞的 IO 操作方式,所以人們叫它 AIO,非同步 IO 是基於事件和回撥機制實現。
# 三 集合面試
## 3.1 java 集合架構
![](https://gitee.com/lsc180/images/raw/master/img/collection.jpg)
## 3.2 Collection 和 Collections的區別
Collection:是java.uitl 下的介面,他是各種集合的父介面;
Conllecitons:是個java.util下的類,是針對集合的工具類,提供一系列靜態方法對集合的搜尋、查詢、同步等操作;
## 3.3 ArrayList和Vector的區別
- 共同點:都實現了List介面,為有序的集合,底層基於陣列實現,通過索引取值,允許元素重複和為null;都是實現 fail-fast機制;
- 區別:ArrayList不同步,Vector是同步(ArrayList 比 Vector 快,不會過載);Vector擴容原來的一倍,ArrayList擴容原來的0.5倍;
## 3.4List,Set,Map 三者的區別?
- `List`: 儲存的元素有序可重複。
- `Set`: 儲存的元素是無序的、不可重複的。
- `Map`: 使用鍵值對(kye-value)儲存,通過key 獲取value; key 不能重複;
## 3.5 ArrayList,LinkedList區別
共同點:
`ArrayList` 和 `LinkedList` 都是執行緒不安全;
區別:
- ArrayList的底層是陣列實現,LinkedList的底層是雙向連結串列實現。
- ArrayList 通過索引獲取值,查詢快;LinkedList通過遍歷連結串列獲取值查詢慢;
- ArrayList 增刪慢,LinkedList 增刪快;
## 3.6Enumeration和iterator介面的區別
- iterator是Enumeration介面的替代品,只提供了遍歷vector和HashTable型別集合元素的功能;
- Enumeration速度是iterator的2倍,同時佔用更少的記憶體。
- terator有fail-fast機制,比Enumeration更安全
- Iterator能夠刪除元素,Enumeration並不能刪除元素
## 3.7 簡述Iterator 迭代器
Iterator迭代器可以對集合進行遍歷, 遍歷方式都是 `hasNext()`和`next()`方法,,在當前遍歷集合元素被更改的時候,就會丟擲 `ConcurrentModificationException` 異常;
## 3.8 HashMap和Hashtable的區別
共同點: 都實現 Map介面;
區別:
- HashMap不同步;Hashtable同步;HashMap效率比Hashtable高;
- HashMap允許為null ;Hashtable不允許為null
- Hashtable 預設的初始大小為 11,之後每次擴充,容量變為原來的 2n+1,HashMap 預設的初始化大小為 16;
- HashMap 在 jdk1.8 改變了資料結構為 陣列 + 連結串列 + 紅黑樹方式; HashTable使用全表鎖,效率低下;
## 3.9HashSet 和 HashMap 的區別
- HashMap 實現了Map介面;HashSet實現了Set介面
- HashMap儲存鍵值對;HashSet僅僅儲存物件
- HashMap使用put()方法將元素放入map中;HashSet使用add()方法將元素放入set中;
- HashMap中使用鍵物件來計算hashcode值;ashSet使用成員物件來計算hashcode值;
- HashSet較HashMap來說比較慢;
## 3.10 HashMap和TreeMap區別
TreeMap實現SortMap介面,能夠將記錄根據鍵排序(預設升序排序),也可以指定排序的比較器Comparator,當用Iterator 遍歷TreeMap時得到排序後的結果;
> 對於插入、刪除和定位元素等操作,選擇HashMap;如果對一個有序的key集合進行遍歷,選擇TreeMap
## 3.11 併發集合和普通集合區別
併發集合常見的有 `ConcurrentHashMap`、`ConcurrentLinkedQueue`、`ConcurrentLinkedDeque` 等。併發集合位於 java.util.concurrent 包下,是 jdk1.5 之後才有;其相比與普通集合添加了 synchronized 同步鎖,執行緒安全,但效率低;
## 3.12 ConcurrentHashMap1.7和1.8的區別
jdk1.8的實現不再使用jdk1.7的Segment\+ HashEntry分段鎖機制實現,利用`Node陣列`+`CAS+Synchronized`來保證執行緒安全;底層採用`陣列+連結串列+紅黑樹`的儲存結構;
ConcurrentHashMap1.7
![](https://gitee.com/lsc180/images/raw/master/img/20200726102407.png)dk1.7
ConcurrentHashMap1.8
![](https://gitee.com/lsc180/images/raw/master/img/20200726102754.png)
## 3.13HashMap 的長度為什麼是2的冪次方
目的是為了能讓 HashMap 存取高效,儘量減少Hash碰撞,儘量使Hash演算法的結果均勻分佈,每個連結串列/紅黑樹長度大致相同;
演算法實際是取模,hash%length,計算機中求餘效率不如位移運算,原始碼中做了優化hash&(length-1);
hash%length==hash&(length-1)的前提是length是2的n次方
## 3.14 ArrayList集合如何高效加入10萬條資料
直接在初始化的時候就指定ArrayList的容量值;
## 3.15 如何選用集合
根據集合的特點來選用集合;根據鍵獲取值選用 `Map` 介面下的集合;需要排序選擇 `TreeMap`,不需要排序選擇 `HashMap`,需要執行緒安全選 `ConcurrentHashMap`。
只存放元素值時,就選擇實現`Collection` 介面的集合;需要元素唯一時選擇實現 `Set` 介面的集合,如TreeSet` 或 `HashSet; 不需要元素唯一性就選擇實現 List 介面,如 `ArrayList` 或 `LinkedList`;
## 3.16 快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什麼
- 快速失敗:當你在迭代一個集合的時候,如果有另一個執行緒正在修改你正在訪問的那個集合時,就會丟擲一個 ConcurrentModification 異常。 在 java.util 包下的都是快速失敗。
- 安全失敗:你在迭代的時候會去底層集合做一個拷貝,所以你在修改上層集合的時候是不會受影響的,不會丟擲 ConcurrentModification 異常。在java.util.concurrent 包下的全是安全失敗的。
附:
HashMap 原始碼分析: https://blog.csdn.net/youku1327/article/details/105332136;
ArrayList原始碼分析https://blog.csdn.net/youku1327/article/details/105314040
> tip: 需要懂 HashMap,ArrayList,LinkedList,ConcurrentHashMap底層實