InterV 1:基礎
一、面向對象
1、面向對象和面向過程的區別
面向對象關注于一個功能實現的行為,將一些行為封裝為一個對象來統一調用。
面向過程關注于一個功能實現的步驟,按步驟編程實現功能。
面向對象是以對象為中心的編程思想。
面向過程是一種事件為中心的編程思想。就是分析出解決問題所需的步驟,然后用函數把這些步驟實現,并按順序調用。
2、四個基本特性
抽象:將一類具有相同屬性和行為的事物抽象形成類的過程。Java中抽象的概念最直接的應用就是抽象類和接口,從復雜的業務邏輯中,提煉出它的本質。
封裝:將數據以及加在這些數據上的操作組織在一起,提供給可信的其他類或對象操作。遵循大原則(屬性私有,方法公開),工具方法也私有
繼承:通過繼承可以擁有現有類的所有功能,并在無需重新編寫原來類的情況下對這些功能進行擴展。
多態:同一種調用形式,作用于不同的對象,產生不同的反應。比如一個接口有不同的實現類,調用不同的實現類的方法,有不同的效果
滿足多態的條件?
1. 有父子關系(即繼承)
2. 子類的方法對父類的方法滿足復寫原則
3. 父類的對象用子類對象來實例化
4. 父類對象調用被子類復寫的方法
封裝考慮內部實現,抽象考慮的是外部行為。
封裝可以隱藏實現細節,使得代碼模塊化;
繼承可以擴展已存在的代碼模塊;
總結:
抽象、封裝、繼承都是為了解決代碼重用。
多態是為了實現接口重用,為了類在繼承和派生的時候,保證使用“家譜”中任一類的實例的某一屬性時的正確使用。
3、抽象類和接口的區別
相同點:
都是上層的抽象;
都不能被實例化;
都能包含抽象方法。
不同點:
在抽象類中可以寫非抽象的方法,從而避免在子類中重復書寫他們,提高代碼的復用性;接口中只能有抽象方法(JDK1.8新特性:默認方法(子類不一定要實現)、JDK1.9新特性:私有方法(增強默認方法));
一個類只能繼承一個直接父類(普通類或抽象類),但是可以實現多個接口。
下面比較一下兩者的語法區別:
1.抽象類可以有構造方法,接口中不能有構造方法。
2.抽象類中可以有普通成員變量,接口中沒有普通成員變量
3.抽象類中可以包含非抽象的普通方法,接口中的所有方法必須都是抽象的,不能有非抽象的普通方法。
4. 抽象類中的抽象方法的訪問類型可以是public,protected,但接口中的抽象方法只能是public類型的,并且默認即為public abstract類型。
5. 抽象類中可以包含靜態方法,接口中不能包含靜態方法
6. 抽象類和接口中都可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型可以任意,但接口中定義的變量只能是public static final類型,并且默認即為public static final類型。
7. 一個類可以實現多個接口,但只能繼承一個抽象類。
下面接著再說說兩者在應用上的區別:
接口更多的是在系統架構設計方法發揮作用,主要用于定義模塊之間的通信契約。而抽象類在代碼實現方面發揮作用,可以實現代碼的重用
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的自動拆箱操作。
自動裝箱也就是將基本數據類型封裝到對象中的操作,自動拆箱也就是將對象中的基本數據從對象中自動取出。
1、String和StringBuffer、StringBuilder的區別
性能差別:StringBuilder > StringBuffer > String;
String對字符串的操作(修改、拼接)其實是在創建新的對象,效率低下;
StringBuffer線程安全、StringBuilder線程不安全
2、hashCode和equals方法的關系
hashcode()方法是JDK根據對象的地址或者字符串的值計算出來的int類型的數值(哈希碼值)。
同一對象多次調用hashcode()方法,必須返回相同的數值。
如果兩個對象根據equals()方法比較是相等的,那么兩個對象調用hashcode()方法返回的結果必須相等。
如果兩個對象根據equals()方法比較是不相等的,那么兩個對象調用hashcode()方法返回的結果不一定不相等。
equals方法和hashCode方法這2個方法都是用來判斷2個對象是否相等的,但是他們是有區別的。
簡單來講,equals方法主要是用來判斷從表面上看或者從內容上看,2個對象是不是相等。舉個例子,有個學生類,屬性只有姓名和性別,那么我們可以認為只要姓名和性別相等,那么就說這2個對象是相等的。
hashcode相當于是一個對象的編碼,就好像文件中的md5,他和equals不同就在于他返回的是int型的,比較起來不直觀。我們一般在覆蓋equals的同時也要覆蓋hashcode,讓他們的邏輯一致。舉個例子,還是剛剛的例子,如果姓名和性別相等就算2個對象相等的話,那么hashcode的方法也要返回姓名的hashcode值加上性別的hashcode值,這樣從邏輯上,他們就一致了。
要從物理上判斷2個對象是否相等,用==就可以了,如果兩個對象的物理(內存)地址相等,那么這兩個對象肯定就是同一個對象。
3、Java中的集合類
Collection下所有子類集合都用于存儲Value,Map下所有子類集合都用于存儲Key-Value。
Collection的子類:

Map的子類:

ArrayList是線程不安全的,Vector是線程安全的(二者底層都是數組類型結構),LinkedList線程不安全(底層鏈表類型結構);
ArrayList每次擴容50%,而Vector每次擴容翻倍;
Set集合存儲無序的不可重復元素,允許一個null元素。HashSet對象必須定義hashcode()方法,LinkedHashSet具備HashSet的性能,但內部使用鏈表維護元素的順序(插入順序)。TreeSet底層使用樹結構維護有序的元素。
HashMap是線程不安全的,可以存儲null值null鍵和;HashTable是線程安全的,不允許存儲null值null鍵;HashTable因為是線程安全的,所以性能低于HashMap。
4、什么是泛型?為什么要使用?泛型擦除?
泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。
泛型(JDK1.5特性)之前,當方法的參數類型設置為基類,那么可以往方法中傳入該基類下任意類型的對象,這樣方法就更具有通用性。另外,將方法參數設置為接口,更加方便(可實現多個接口)。
這樣存在的問題是,當需要獲取一個值的時候,必須強制類型轉換。而強制裝換類型的時候,容易使用錯誤的類型轉換導致報錯。
泛型擦除是指:在Java中使用泛型創建對象時,在編譯期間,所有的泛型信息都會被擦除,編譯后會變成原始類型。
5、Java中的異常
IndexOutOfBoundsEecption:元素越界異常;
ArrayIndexOutOfBoundsEecption:多個元素越界異常;
ClassCastException:類型轉換異常;
NullPointerException:空指針異常,null對象的應用;
RuntimeException:運行時異常;
IOException:IO操作異常;
ConnectException:連接失敗異常;
6、Java中的BIO,NIO,AIO
1)同步、異步、阻塞、非阻塞
同步:用戶觸發IO操作,你發起了請求就得等著對方給你返回結果,你不能走,針對調用方的,你發起了請求你等
異步:用戶觸發了IO操作,即發起了請求以后可以做自己的事,等處理完以后會給你返回處理完成的標志,針對調用方的,你發起了請求你不等
阻塞:你調用我,我試圖對文件進行讀寫的時候發現沒有可讀寫的文件,我的程序就會進入等待狀態,等可以讀寫了,我處理完給你返回結果,這里的等待和同步的等待有很大的區別,針對服務提供方的,你調用我我發現服務不可用我等
非阻塞:你調用我,我試圖對文件讀寫的時候發現沒有讀寫的文件,不等待直接返回,等我發現可以讀寫文件處理完了再給你返回成功標志,針對服務提供方的,你調用我我不等,我處理完了給你返回結果
AIO(異步非阻塞IO):NIO的2.0版本,引入了異步通道的概念,可以實現異步調用。
異步實現方式:通過java.util.concurrent.Future類來表示異步操作的結果;
在執行異步操作的時候傳入java.nio.channels。
7、序列化與反序列化
序列化是指將對象的狀態信息轉換為可以存儲或傳輸的形式的過程,通過序列化可以將對象的狀態保存為字節數組,需要的時候再將字節數組反序列化為對象。
8、IO和NIO區別
NIO是Java1.4的新特性,提供了與標準IO不同的工作方式:
標準IO基于字節流和字符流進行操作,而NIO是基于通道(Channel)和緩沖區(Buffer)進行操作,數據從通道讀取到緩沖區中,或者從緩沖區寫入到通道。
NIO引入了選擇器(Selectors)概念,選擇器用于監聽多個通道的事件(比如:連接打開、可讀、可寫),因此NIO可以通過一個線程監聽多個數據通道。相比標準IO為每一個連接創建一個線程,NIO大大降低了線程創建的資源開銷。
三、多線程
1、多線程的實現方式
通常使用繼承Thread類或實現Runnable接口
還可以通過Callable接口實現。
2、線程的狀態轉換
狀態:就緒,運行,synchronize阻塞,wait和sleep掛起,結束。wait必須在synchronized內部調用。
調用線程的start方法后線程進入就緒狀態,線程調度系統將就緒狀態的線程轉為運行狀態,遇到synchronized語句時,由運行狀態轉為阻塞,當synchronized獲得鎖后,由阻塞轉為運行,在這種情況可以調用wait方法轉為掛起狀態,當線程關聯的代碼執行完后,線程變為結束狀態。

3、sleep和wait的區別
1、sleep()是線程Thread的方法,而wait()是Object對象的方法。
2、sleep()不會釋放對象鎖、wait()會釋放對象鎖
3、sleep()可以在任何地方調用,wait()方法只可以在同步方法或同步塊中使用。
yield() 當前線程出讓cpu占有權,當前線程變成可運行狀態。
wait()\notify()\notifyAll()
調用以前,當前線程必須要持有鎖,調用它們線程會釋放鎖,等待通知機制。
notify() 喚醒一個線程(謹慎使用),具體喚醒哪個線程,由CPU決定。
notifyAll() 所有在對象O上wait的線程全部喚醒(應用較多)
4、如何停止一個線程
1、run方法代碼執行完成
2、線程運行時拋出一個未捕獲的異常,跳出線程
3、通過標志位跳出線程
4、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、死鎖
當一個鎖未被釋放,其他線程無法獲取鎖的時候,程序產生死鎖情況。
死鎖的兩種情況:
1、線程thread1先獲取鎖locka,然后在同步塊里嵌套競爭鎖lockb。而線程thread2先獲取鎖lockb,然后在同步塊里嵌套競爭鎖locka。
2、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:同上

浙公網安備 33010602011771號