面試專題(Java基礎)
面向物件
1.面向物件和麵向過程的區別
面向過程關注於一個功能實現的步驟,按步驟程式設計實現功能。
面向物件關注於一個功能實現的行為,將一些行為封裝為一個物件來統一呼叫。
面向過程是一種事件為中心的程式設計思想。就是分析出解決問題所需的步驟,然後用函式把這些步驟實現,並按順序呼叫。面向過程是以物件為中心的程式設計思想。
2.四個基本特性
抽象:Java中抽象的概念最直接的應用就是抽象類和介面,從複雜的業務邏輯中,提煉出它的本質。
封裝:封裝將資料以及加在這些資料上的操作組織在一起,提供給可信的其他類或物件操作。
繼承
多型:多型性是指允許不同類的物件對同一訊息作出響應。即同一訊息可以根據傳送物件的不同而採用多種不同的行為方式。(傳送訊息就是函式呼叫)。實現多型的兩種方式:重寫、過載。
封裝考慮內部實現,抽象考慮的是外部行為。封裝可以隱藏實現細節,使得程式碼模組化;繼承可以擴充套件已存在的程式碼模組;他們都是為了解決程式碼重用。而多型是為了實現介面重用,為了類在繼承和派生的時候,保證使用“家譜”中任一類的例項的某一屬性時的正確使用。
3.抽象類和介面的區別
相同點:
都是上層的抽象;
都不能被例項化;
都能包含抽象方法。
不同點:
在抽象類中可以寫非抽象的方法,從而避免在子類中重複書寫他們,提高程式碼的複用性;介面中只能有抽象方法(JDK1.8新特性:預設方法(子類不一定要實現)、JDK1.9新特性:私有方法(增強預設方法));
一個類只能繼承一個直接父類(普通類或抽象類),但是可以實現多個介面。
4.訪問控制符
private:同類可見
default:同包可見
protected:同包可見,子類可見
public:全域性可見
5.過載和重寫
過載是指一個類中允許存在多個同名函式,而這些函式的引數不同;重寫是指子類重新定義父類的方法。
6.構造器Constructor是否可被override?
構造器不允許被重寫。
構造器不是方法,所有用於修飾方法特性的修飾符,都不能用來修飾構造器。
構造器是例項化物件過程中用來初始化這個物件用的。
語言特性
1.自動裝箱與拆箱
以問題引入:
JDK實際編譯的程式碼:
將Integer a = 120; 編譯為:Integer a = Integer.valueOf(120); 就是JDK的自動裝箱操作。
將int e = a; 編譯為:int e = a.intValue(); 就是JDK的自動拆箱操作。
自動裝箱也就是將基本資料型別封裝到物件中的操作,自動拆箱也就是將物件中的基本資料從物件中自動取出。
2.String和StringBuffer、StringBuilder的區別
效能差別:StringBuilder > StringBuffer > String;
String對字串的操作(修改、拼接)其實是在建立新的物件,效率低下;
StringBuffer執行緒安全、StringBuilder執行緒不安全
3,hashCode和equals方法的關係
hashcode()方法是JDK根據物件的地址或者字串的值計算出來的int型別的數值(雜湊碼值)。
同一物件多次呼叫hashcode()方法,必須返回相同的數值。
如果兩個物件根據equals()方法比較是相等的,那麼兩個物件呼叫hashcode()方法返回的結果必須相等。
如果兩個物件根據equals()方法比較是不相等的,那麼兩個物件呼叫hashcode()方法返回的結果不一定不相等。
4.Java中的集合類
Collection下所有子類集合都用於儲存Value,Map下所有子類集合都用於儲存Key-Value。
ArrayList是執行緒不安全的,Vector是執行緒安全的(二者底層都是陣列型別結構),LinkedList執行緒不安全(底層連結串列型別結構);
ArrayList每次擴容50%,而Vector每次擴容翻倍;
Set集合儲存無序的不可重複元素,允許一個null元素。HashSet物件必須定義hashcode()方法,LinkedHashSet具備HashSet的效能,但內部使用連結串列維護元素的順序(插入順序)。TreeSet底層使用樹結構維護有序的元素。
HashMap是執行緒不安全的,可以儲存null值null鍵和;HashTable是執行緒安全的,不允許儲存null值null鍵;HashTable因為是執行緒安全的,所以效能低於HashMap。
5.什麼是泛型?為什麼要使用?泛型擦除?
泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。
泛型(JDK1.5特性)之前,當方法的引數型別設定為基類,那麼可以往方法中傳入該基類下任意型別的物件,這樣方法就更具有通用性。另外,將方法引數設定為介面,更加方便(可實現多個介面)。
這樣存在的問題是,當需要獲取一個值的時候,必須強制型別轉換。而強制裝換型別的時候,容易使用錯誤的型別轉換導致報錯。
泛型擦除是指:在Java中使用泛型建立物件時,在編譯期間,所有的泛型資訊都會被擦除,編譯後會變成原始型別。
6.Java中的異常
IndexOutOfBoundsEecption:元素越界異常;
ArrayIndexOutOfBoundsEecption:多個元素越界異常;
ClassCastException:型別轉換異常;
NullPointerException:空指標異常,null物件的應用;
RuntimeException:執行時異常;
IOException:IO操作異常;
ConnectException:連線失敗異常;
7.Java中的BIO,NIO,AIO
BIO(同步阻塞IO):一個連線對應一個執行緒
當有客戶端連線請求時,服務端需要啟動一個執行緒進行處理,如果這個連線不做任何處理,會造成不必要的執行緒開銷,可以通過執行緒池機制改善,從而實現偽非同步IO;
NIO(同步非阻塞IO):N個連線對應一個執行緒
客戶端所有的連線請求都會註冊到多路複用器上,服務端通過一個多路複用器來處理所有請求。
AIO(非同步非阻塞IO):NIO的2.0版本,引入了非同步通道的概念,可以實現非同步呼叫。
非同步實現方式:通過java.util.concurrent.Future類來表示非同步操作的結果;
在執行非同步操作的時候傳入java.nio.channels。
8.序列化與反序列化
序列化是指將物件的狀態資訊轉換為可以儲存或傳輸的形式的過程,通過序列化可以將物件的狀態儲存為位元組陣列,需要的時候再將位元組陣列反序列化為物件。
9.IO和NIO區別
NIO是Java1.4的新特性,提供了與標準IO不同的工作方式:
標準IO基於位元組流和字元流進行操作,而NIO是基於通道(Channel)和緩衝區(Buffer)進行操作,資料從通道讀取到緩衝區中,或者從緩衝區寫入到通道。
NIO引入了選擇器(Selectors)概念,選擇器用於監聽多個通道的事件(比如:連線開啟、可讀、可寫),因此NIO可以通過一個執行緒監聽多個數據通道。相比標準IO為每一個連線建立一個執行緒,NIO大大降低了執行緒建立的資源開銷。
多執行緒
1.多執行緒的實現方式
通常使用繼承Thread類或實現Runnable介面
還可以通過Callable介面實現。
2.執行緒的狀態轉換
new 新建執行緒
Runnable 可執行狀態(執行start()方法,CPU決定是否執行)
Blocking 阻塞狀態(執行緒被阻塞於鎖)
Waiting 等待、計時等待(等待某些條件成熟)
Stop 終止狀態(執行緒執行結束)
3.sleep和wait的區別
- sleep()是執行緒Thread的方法,而wait()是Object物件的方法。
- sleep()不會釋放物件鎖、wait()會釋放物件鎖
- sleep()可以在任何地方呼叫,wait()方法之可以在同步方法或同步塊中使用。
yield() 當前執行緒出讓cpu佔有權,當前執行緒變成可執行狀態。
wait()\notify()\notifyAll()
呼叫以前,當前執行緒必須要持有鎖,呼叫它們執行緒會釋放鎖,等待通知機制。
notify() 喚醒一個執行緒(謹慎使用),具體喚醒哪個執行緒,由CPU決定。
notifyAll() 所有在物件O上wait的執行緒全部喚醒(應用較多)
4.如何停止一個執行緒
- run方法程式碼執行完成
- 執行緒執行時丟擲一個未捕獲的異常,跳出執行緒
- 通過標誌位跳出執行緒
- interrupt() 向需要中斷的執行緒傳送停止指令;isInterrupted() 執行緒檢查自己的中斷標誌位;Thread.interrupted() 將中斷標誌位復位為false;
不安全方式
Stop() 立刻停止執行緒,但不會釋放執行緒執行所應用的資源
Suspend() 立刻掛起執行緒,但不會釋放執行緒執行鎖應用的資源,容易造成死鎖
5.volatile關鍵字
在多個執行緒之間,訪問同一個被volatile修飾的物件時,所有執行緒共享這個物件的值。
但是volatile不是執行緒安全的(多個執行緒同時修改這個變數時,最終結果不一定是最後修改的那個值;可以保證執行緒的可見性,不可以保證操作的原子性)
6.synchronized如何使用
加鎖
可以修飾方法或程式碼塊以同步的方式執行(同一時間只會有一個執行緒執行)
類鎖與例項鎖本質上是兩把鎖,類鎖鎖的是每一個類的class物件。
7.synchronized和Lock的區別
synchronized是一個Java的關鍵字,Lock是一個介面;
synchronized程式碼塊執行完或執行緒丟擲異常時結束執行緒,Lock必須顯示呼叫釋放鎖的方法:unlock();
synchronized修飾的鎖其他執行緒在等待獲取鎖的階段,會一直阻塞等待直到得到鎖為止(不可中斷鎖);Lock有多種方式可以獲取鎖,不一定一直阻塞等待(可中斷鎖)。
synchronized無法判斷鎖的狀態,Lock可以判斷;
synchronized是非公平鎖,而Lock可以設定為公平鎖;
Lock用法:
lock()(阻塞執行緒等待獲取鎖)
lockInterruptibly():可中斷(阻塞執行緒等待獲取鎖,會響應中斷)
tryLock():嘗試非阻塞的獲取鎖(非阻塞方式嘗試獲取鎖,無法獲取則返回false)
unlock()
公平鎖與非公平鎖:
公平鎖,先對鎖發出獲取請求的一定先獲得鎖。非公平鎖則反之(效能更高)。
ReentrantLock(boolean)可選擇公平鎖或非公平鎖,預設使用非公平鎖。
鎖的可重入:
遞迴的時候發生鎖的重入
synchronized隱式支援鎖的重入
ReentrantLock的lock()支援鎖的重入
排它鎖:同一時刻只有一個執行緒獲取鎖;
讀寫鎖:同一時刻執行多個讀執行緒訪問,但是隻允許一個寫執行緒,寫鎖會阻塞所有鎖。(ReentrantReadWriteLock,相比synchronized速度更快)
Condition介面有何作用?
Condition介面與Lock配合,來實現等待通知機制。
8.什麼是執行緒安全
當多個執行緒訪問某個類時,這個類始終都能表現出正確的行為,那麼就稱這個類是執行緒安全的。
9.死鎖
當一個鎖未被釋放,其他執行緒無法獲取鎖的時候,程式產生死鎖情況。
死鎖的兩種情況:
- 執行緒thread1先獲取鎖locka,然後在同步塊裡巢狀競爭鎖lockb。而執行緒thread2先獲取鎖lockb,然後在同步塊裡巢狀競爭鎖locka。
- Lock.unlock()方法的錯誤使用,導致死鎖。
10.Java執行緒池
什麼是執行緒池?用於管理執行緒的一個工具。
執行緒池的作用?限制系統中執行執行緒的數量;降低資源的消耗、提高響應速度、提高執行緒的可管理性。
Java常見的執行緒池:
Executors.newSingleThreadExecutor:單個執行緒的執行緒池;
Executors.newFixedThreadExecutor:固定執行緒數量的執行緒池;
Executors.newCacheThreadExecutor:可快取執行緒;
Executors.newScheduledThreadPool:建立一個定長執行緒池,支援定時和週期性的執行執行緒;
11.併發工具類和併發容器類
常用的併發工具類:
閉鎖:CountDownLatch
柵欄:CyclicBarrier
訊號量:Semaphore
交換者:Exchanger
CountDownLatch 閉鎖允許一個執行緒或多個執行緒等待特定情況,同步完成執行緒中其他任務。
CyclicBarrier和CountDownLatch都可以協同多個執行緒,讓指定數量的執行緒等待期他所有的執行緒都滿足某些條件之後才繼續執行。CyclicBarrier可以重複使用(reset),而CountDownLatch只能夠使用一次,如果還需要使用,必須重現new一個CountDownLatch物件。
構造方法CyclicBarrier(int, Runnable) 所有執行緒達到屏障後,執行Runnable。
Semaphore 型號量用來控制同時訪問特定資源的執行緒數量。
Exchanger 交換者用於在兩個執行緒之間傳輸資料,被呼叫後等待另一個執行緒達到交換點,然後相互互動資料。
常用的併發容器:
ConcurrentHashMap:JDK1.7實現:分段鎖;JDK1.8實現:元素(key)鎖+連結串列+紅黑樹
SkipList:跳錶自動隨機維護一套索引,用於高效的索引List中的有序資料。
ConcurrentSkipListMap:TreeMap的併發實現
ConcurrentSkipListSet:TreeSet的併發實現
ConcurrentLinkedQueue:LinkedList的併發實現
CopyOnWriteArrayList:寫時複製,在新增元素是,複製一個新的容器,在新容器中新增元素;讀資料都在Old容器中操作,進行讀寫分離。資料一致性較弱,適合讀多寫少的場景。
CopyOnWriteArraySet:同上