Java 基礎(chǔ)八股文 34 卷(圖解版)
大家好,我是二哥呀。
關(guān)注二哥的讀者當(dāng)中應(yīng)該有不少都是 Java 程序員,那么近期二哥整理了一波 Java 基礎(chǔ)的面試八股文——一共 34 卷,希望大家能理解它,看透它~
先來看一下大綱吧,是不是有一種很滿足的感覺?
1.介紹一下 java 吧
2.java 有哪些數(shù)據(jù)類型?
3.接口和抽象類有什么區(qū)別?
4.重載和重寫什么區(qū)別?
5.常見的異常有哪些?
6.異常要怎么解決?
7.arrayList 和 linkedList 的區(qū)別?
8.hashMap 1.7 和 hashMap 1.8 的區(qū)別?
9.hashMap 線程不安全體現(xiàn)在哪里?
10.那么 hashMap 線程不安全怎么解決?
11.concurrentHashMap 1.7 和 1.8 有什么區(qū)別
12.介紹一下 hashset 吧
13.什么是泛型?
14.泛型擦除是什么?
15.說說進(jìn)程和線程的區(qū)別?
16.volatile 有什么作用?
17.什么是包裝類?為什么需要包裝類?
18.Integer a = 1000,Integer b = 1000,a==b 的結(jié)果是什么?那如果 a,b 都為1,結(jié)果又是什么?
19.JMM 是什么?
20.創(chuàng)建對象有哪些方式
21.講講單例模式懶漢式吧
22.volatile 有什么作用
23.怎么保證線程安全?
24.synchronized 鎖升級(jí)的過程
25.cas 是什么?
26.聊聊 ReentrantLock 吧
27.多線程的創(chuàng)建方式有哪些?
28.線程池有哪些參數(shù)?
29.線程池的執(zhí)行流程?
30.線程池的拒絕策略有哪些?
31.介紹一下四種引用類型?
32.深拷貝、淺拷貝是什么?
33.聊聊 ThreadLocal 吧
34.一個(gè)對象的內(nèi)存布局是怎么樣的?
1.介紹一下 java 吧
java 是一門開源的跨平臺(tái)的面向?qū)ο蟮?/strong>計(jì)算機(jī)語言.

跨平臺(tái)是因?yàn)?java 的 class 文件是運(yùn)行在虛擬機(jī)上的,其實(shí)跨平臺(tái)的,而虛擬機(jī)是不同平臺(tái)有不同版本,所以說 java 是跨平臺(tái)的.
面向?qū)ο笥袔讉€(gè)特點(diǎn):

1.封裝 兩層含義:一層含義是把對象的屬性和行為看成一個(gè)密不可分的整體,將這兩者'封裝'在一個(gè)不可分割的獨(dú)立單元(即對象)中 另一層含義指'信息隱藏,把不需要讓外界知道的信息隱藏起來,有些對象的屬性及行為允許外界用戶知道或使用,但不允許更改,而另一些屬性或行為,則不允許外界知曉,或只允許使用對象的功能,而盡可能隱藏對象的功能實(shí)現(xiàn)細(xì)節(jié)。
優(yōu)點(diǎn):
1.良好的封裝能夠減少耦合,符合程序設(shè)計(jì)追求'高內(nèi)聚,低耦合'。
2.類內(nèi)部的結(jié)構(gòu)可以自由修改。
3.可以對成員變量進(jìn)行更精確的控制。
4.隱藏信息實(shí)現(xiàn)細(xì)節(jié)。
2.繼承 繼承就是子類繼承父類的特征和行為,使得子類對象(實(shí)例)具有父類的實(shí)例域和方法,或子類從父類繼承方法,使得子類具有父類相同的行為。
優(yōu)點(diǎn):
1.提高類代碼的復(fù)用性
2.提高了代碼的維護(hù)性
3.多態(tài) 1.方法重載:在一個(gè)類中,允許多個(gè)方法使用同一個(gè)名字,但方法的參數(shù)不同,完成的功能也不同。 2.對象多態(tài):子類對象可以與父類對象進(jìn)行轉(zhuǎn)換,而且根據(jù)其使用的子類不同完成的功能也不同(重寫父類的方法)。 多態(tài)是同一個(gè)行為具有多個(gè)不同表現(xiàn)形式或形態(tài)的能力。Java語言中含有方法重載與對象多態(tài)兩種形式的多態(tài):
優(yōu)點(diǎn)
消除類型之間的耦合關(guān)系 可替換性 可擴(kuò)充性 接口性 靈活性 簡化性
2.java 有哪些數(shù)據(jù)類型?

java 主要有兩種數(shù)據(jù)類型
1.基本數(shù)據(jù)類型 byte,short,int,long屬于數(shù)值型中的整數(shù)型 float,double屬于數(shù)值型中的浮點(diǎn)型 char屬于字符型 boolean屬于布爾型 基本數(shù)據(jù)有八個(gè), 2.引用數(shù)據(jù)類型 引用數(shù)據(jù)類型有三個(gè),分別是類,接口和數(shù)組
3.接口和抽象類有什么區(qū)別?
1.接口是抽象類的變體,接口中所有的方法都是抽象的。而抽象類是聲明方法的存在而不去實(shí)現(xiàn)它的類。 2.接口可以多繼承,抽象類不行。 3.接口定義方法,不能實(shí)現(xiàn),默認(rèn)是 public abstract,而抽象類可以實(shí)現(xiàn)部分方法。 4.接口中基本數(shù)據(jù)類型為 public static final 并且需要給出初始值,而抽類象不是的。
4.重載和重寫什么區(qū)別?
重寫:
1.參數(shù)列表必須完全與被重寫的方法相同,否則不能稱其為重寫而是重載. 2.返回的類型必須一直與被重寫的方法的返回類型相同,否則不能稱其為重寫而是重載。 3.訪問修飾符的限制一定要大于被重寫方法的訪問修飾符 4.重寫方法一定不能拋出新的檢查異?;蛘弑缺恢貙懛椒ㄉ昝鞲訉挿旱臋z查型異常。
重載:
1.必須具有不同的參數(shù)列表; 2.可以有不同的返回類型,只要參數(shù)列表不同就可以了; 3.可以有不同的訪問修飾符; 4.可以拋出不同的異常;
5.常見的異常有哪些?
NullPointerException 空指針異常 ArrayIndexOutOfBoundsException 索引越界異常 InputFormatException 輸入類型不匹配 SQLException SQL異常 IllegalArgumentException 非法參數(shù) NumberFormatException 類型轉(zhuǎn)換異常 等等....
6.異常要怎么解決?
Java標(biāo)準(zhǔn)庫內(nèi)建了一些通用的異常,這些類以Throwable為頂層父類。
Throwable又派生出Error類和Exception類。
錯(cuò)誤:Error類以及他的子類的實(shí)例,代表了JVM本身的錯(cuò)誤。錯(cuò)誤不能被程序員通過代碼處理,Error很少出現(xiàn)。因此,程序員應(yīng)該關(guān)注Exception為父類的分支下的各種異常類。
異常:Exception以及他的子類,代表程序運(yùn)行時(shí)發(fā)送的各種不期望發(fā)生的事件。可以被Java異常處理機(jī)制使用,是異常處理的核心。
處理方法:
1.try()catch(){}
try{
//?程序代碼
}catch(ExceptionName?e1){
//Catch?塊
}
2.throw throw 關(guān)鍵字作用是拋出一個(gè)異常,拋出的時(shí)候是拋出的是一個(gè)異常類的實(shí)例化對象,在異常處理中,try 語句要捕獲的是一個(gè)異常對象,那么此異常對象也可以自己拋出 3.throws 定義一個(gè)方法的時(shí)候可以使用 throws 關(guān)鍵字聲明。使用 throws 關(guān)鍵字聲明的方法表示此方法不處理異常,而交給方法調(diào)用處進(jìn)行處理。
7.arrayList 和 linkedList 的區(qū)別?

1.ArrayList 是實(shí)現(xiàn)了基于數(shù)組的,存儲(chǔ)空間是連續(xù)的。LinkedList 基于鏈表的,存儲(chǔ)空間是不連續(xù)的。(LinkedList 是雙向鏈表)
2.對于隨機(jī)訪問 get 和 set ,ArrayList 覺得優(yōu)于 LinkedList,因?yàn)?LinkedList 要移動(dòng)指針。
3.對于新增和刪除操作 add 和 remove ,LinedList 比較占優(yōu)勢,因?yàn)?ArrayList 要移動(dòng)數(shù)據(jù)。
4.同樣的數(shù)據(jù)量 LinkedList 所占用空間可能會(huì)更小,因?yàn)?ArrayList 需要預(yù)留空間便于后續(xù)數(shù)據(jù)增加,而 LinkedList 增加數(shù)據(jù)只需要增加一個(gè)節(jié)點(diǎn)
8.hashMap 1.7 和 hashMap 1.8 的區(qū)別?
只記錄重點(diǎn)
| 不同點(diǎn) | hashMap 1.7 | hashMap 1.8 |
|---|---|---|
| 數(shù)據(jù)結(jié)構(gòu) | 數(shù)組+鏈表 | 數(shù)組+鏈表+紅黑樹 |
| 插入數(shù)據(jù)的方式 | 頭插法 | 尾插法 |
| hash 值計(jì)算方式 | 9次擾動(dòng)處理(4次位運(yùn)算+5次異或) | 2次擾動(dòng)處理(1次位運(yùn)算+1次異或) |
| 擴(kuò)容策略 | 插入前擴(kuò)容 | 插入后擴(kuò)容 |
9.hashMap 線程不安全體現(xiàn)在哪里?
在 hashMap1.7 中擴(kuò)容的時(shí)候,因?yàn)椴捎玫氖穷^插法,所以會(huì)可能會(huì)有循環(huán)鏈表產(chǎn)生,導(dǎo)致數(shù)據(jù)有問題,在 1.8 版本已修復(fù),改為了尾插法
在任意版本的 hashMap 中,如果在插入數(shù)據(jù)時(shí)多個(gè)線程命中了同一個(gè)槽,可能會(huì)有數(shù)據(jù)覆蓋的情況發(fā)生,導(dǎo)致線程不安全。
10.那么 hashMap 線程不安全怎么解決?

一.給 hashMap 直接加鎖,來保證線程安全 二.使用 hashTable,比方法一效率高,其實(shí)就是在其方法上加了 synchronized 鎖 三.使用 concurrentHashMap , 不管是其 1.7 還是 1.8 版本,本質(zhì)都是減小了鎖的粒度,減少線程競爭來保證高效.
11.concurrentHashMap 1.7 和 1.8 有什么區(qū)別
只記錄重點(diǎn)
| 不同點(diǎn) | concurrentHashMap 1.7 | concurrentHashMap 1.8 |
|---|---|---|
| 鎖粒度 | 基于segment | 基于entry節(jié)點(diǎn) |
| 鎖 | reentrantLock | synchronized |
| 底層結(jié)構(gòu) | Segment + HashEntry + Unsafe | Synchronized + CAS + Node + Unsafe |
12.介紹一下 hashset 吧

上圖是 set 家族整體的結(jié)構(gòu),
set 繼承于 Collection 接口,是一個(gè)不允許出現(xiàn)重復(fù)元素,并且無序的集合.
HashSet 是基于 HashMap 實(shí)現(xiàn)的,底層采用 HashMap 來保存元素
元素的哈希值是通過元素的 hashcode 方法 來獲取的, HashSet 首先判斷兩個(gè)元素的哈希值,如果哈希值一樣,接著會(huì)比較 equals 方法 如果 equls 結(jié)果為 true ,HashSet 就視為同一個(gè)元素。如果 equals 為 false 就不是同一個(gè)元素。
13.什么是泛型?
泛型:把類型明確的工作推遲到創(chuàng)建對象或調(diào)用方法的時(shí)候才去明確的特殊的類型
14.泛型擦除是什么?
因?yàn)榉盒推鋵?shí)只是在編譯器中實(shí)現(xiàn)的而虛擬機(jī)并不認(rèn)識(shí)泛型類項(xiàng),所以要在虛擬機(jī)中將泛型類型進(jìn)行擦除。也就是說,在編譯階段使用泛型,運(yùn)行階段取消泛型,即擦除。擦除是將泛型類型以其父類代替,如String 變成了Object等。其實(shí)在使用的時(shí)候還是進(jìn)行帶強(qiáng)制類型的轉(zhuǎn)化,只不過這是比較安全的轉(zhuǎn)換,因?yàn)樵诰幾g階段已經(jīng)確保了數(shù)據(jù)的一致性。
15.說說進(jìn)程和線程的區(qū)別?
進(jìn)程是系統(tǒng)資源分配和調(diào)度的基本單位,它能并發(fā)執(zhí)行較高系統(tǒng)資源的利用率.
線程是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位,創(chuàng)建、銷毀、切換成本要小于進(jìn)程,可以減少程序并發(fā)執(zhí)行時(shí)的時(shí)間和空間開銷,使得操作系統(tǒng)具有更好的并發(fā)性。
16.volatile 有什么作用?

1.保證內(nèi)存可見性 可見性是指線程之間的可見性,一個(gè)線程修改的狀態(tài)對另一個(gè)線程是可見的。也就是一個(gè)線程修改的結(jié)果,另一個(gè)線程馬上就能看到。 2.禁止指令重排序 cpu 是和緩存做交互的,但是由于 cpu 運(yùn)行效率太高,所以會(huì)不等待當(dāng)前命令返回結(jié)果從而繼續(xù)執(zhí)行下一個(gè)命令,就會(huì)有亂序執(zhí)行的情況發(fā)生
17.什么是包裝類?為什么需要包裝類?
Java 中有 8 個(gè)基本類型,分別對應(yīng)的 8 個(gè)包裝類
byte -- Byte boolean -- Boolean short -- Short char -- Character int -- Integer long -- Long float -- Float double -- Double
為什么需要包裝類:
基本數(shù)據(jù)類型方便、簡單、高效,但泛型不支持、集合元素不支持 不符合面向?qū)ο笏季S 包裝類提供很多方法,方便使用,如 Integer 類 toHexString(int i)、parseInt(String s) 方法等等
18.Integer a = 1000,Integer b = 1000,a==b 的結(jié)果是什么?那如果 a,b 都為1,結(jié)果又是什么?
Integer a = 1000,Integer b = 1000,a==b 結(jié)果為false
Integer a = 1,Integer b = 1,a==b 結(jié)果為true
這道題主要考察 Integer 包裝類緩存的范圍,在-128~127之間會(huì)緩存起來,比較的是直接緩存的數(shù)據(jù),在此之外比較的是對象
19.JMM 是什么?

JMM 就是 Java內(nèi)存模型(java memory model)。因?yàn)樵诓煌挠布a(chǎn)商和不同的操作系統(tǒng)下,內(nèi)存的訪問有一定的差異,所以會(huì)造成相同的代碼運(yùn)行在不同的系統(tǒng)上會(huì)出現(xiàn)各種問題。所以java內(nèi)存模型(JMM)屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實(shí)現(xiàn)讓java程序在各種平臺(tái)下都能達(dá)到一致的并發(fā)效果。
Java內(nèi)存模型規(guī)定所有的變量都存儲(chǔ)在主內(nèi)存中,包括實(shí)例變量,靜態(tài)變量,但是不包括局部變量和方法參數(shù)。每個(gè)線程都有自己的工作內(nèi)存,線程的工作內(nèi)存保存了該線程用到的變量和主內(nèi)存的副本拷貝,線程對變量的操作都在工作內(nèi)存中進(jìn)行。線程不能直接讀寫主內(nèi)存中的變量。
每個(gè)線程的工作內(nèi)存都是獨(dú)立的,線程操作數(shù)據(jù)只能在工作內(nèi)存中進(jìn)行,然后刷回到主存。這是 Java 內(nèi)存模型定義的線程基本工作方式。
20.創(chuàng)建對象有哪些方式
有五種創(chuàng)建對象的方式
1、new關(guān)鍵字
Person?p1?=?new?Person();
2.Class.newInstance
Person?p1?=?Person.class.newInstance();
3.Constructor.newInstance
Constructor?constructor?=?Person.class.getConstructor();
Person?p1?=?constructor.newInstance();
4.clone
Person?p1?=?new?Person();
Person?p2?=?p1.clone();
5.反序列化
Person?p1?=?new?Person();
byte[]?bytes?=?SerializationUtils.serialize(p1);
Person?p2?=?(Person)SerializationUtils.deserialize(bytes);
21.講講單例模式懶漢式吧
直接貼代碼
//?懶漢式
public?class?Singleton?{
//?延遲加載保證多線程安全
????Private?volatile?static?Singleton?singleton;
????private?Singleton(){}
????public?static?Singleton?getInstance(){
????????if(singleton?==?null){
????????????synchronized(Singleton.class){
????????????????if(singleton?==?null){
????????????????????singleton?=?new?Singleton();
????????????????}
????????????}
????????}
????????return?singleton;
????}
}
使用 volatile 是防止指令重排序,保證對象可見,防止讀到半初始化狀態(tài)的對象 第一層if(singleton == null) 是為了防止有多個(gè)線程同時(shí)創(chuàng)建 synchronized 是加鎖防止多個(gè)線程同時(shí)進(jìn)入該方法創(chuàng)建對象 第二層if(singleton == null) 是防止有多個(gè)線程同時(shí)等待鎖,一個(gè)執(zhí)行完了后面一個(gè)又繼續(xù)執(zhí)行的情況
22.volatile 有什么作用

1.保證內(nèi)存可見性 當(dāng)一個(gè)被volatile關(guān)鍵字修飾的變量被一個(gè)線程修改的時(shí)候,其他線程可以立刻得到修改之后的結(jié)果。當(dāng)一個(gè)線程向被volatile關(guān)鍵字修飾的變量寫入數(shù)據(jù)的時(shí)候,虛擬機(jī)會(huì)強(qiáng)制它被值刷新到主內(nèi)存中。當(dāng)一個(gè)線程讀取被volatile關(guān)鍵字修飾的值的時(shí)候,虛擬機(jī)會(huì)強(qiáng)制要求它從主內(nèi)存中讀取。 2.禁止指令重排序 指令重排序是編譯器和處理器為了高效對程序進(jìn)行優(yōu)化的手段,cpu ?是與內(nèi)存交互的,而 cpu 的效率想比內(nèi)存高很多,所以 cpu 會(huì)在不影響最終結(jié)果的情況下,不等待返回結(jié)果直接進(jìn)行后續(xù)的指令操作,而 volatile 就是給相應(yīng)代碼加了內(nèi)存屏障,在屏障內(nèi)的代碼禁止指令重排序。
23.怎么保證線程安全?
1.synchronized關(guān)鍵字 可以用于代碼塊,方法(靜態(tài)方法,同步鎖是當(dāng)前字節(jié)碼對象;實(shí)例方法,同步鎖是實(shí)例對象) 2.lock鎖機(jī)制
Lock?lock?=?new?ReentrantLock();
lock.?lock();
try?{
????System.?out.?println("獲得鎖");
}?catch?(Exception?e)?{
???
}?finally?{
????System.?out.?println("釋放鎖");
????lock.?unlock();
}
24.synchronized 鎖升級(jí)的過程
在 Java1.6 之前的版本中,synchronized 屬于重量級(jí)鎖,效率低下,鎖是 cpu 一個(gè)總量級(jí)的資源,每次獲取鎖都要和 cpu 申請,非常消耗性能。
在 jdk1.6 之后 Java 官方對從 JVM 層面對 synchronized 較大優(yōu)化,所以現(xiàn)在的 synchronized 鎖效率也優(yōu)化得很不錯(cuò)了,Jdk1.6 之后,為了減少獲得鎖和釋放鎖所帶來的性能消耗,引入了偏向鎖和輕量級(jí)鎖,增加了鎖升級(jí)的過程,由無鎖->偏向鎖->自旋鎖->重量級(jí)鎖
增加鎖升級(jí)的過程主要是減少用戶態(tài)到核心態(tài)的切換,提高鎖的效率,從 jvm 層面優(yōu)化鎖
25.cas 是什么?
cas 叫做 CompareAndSwap,比較并交換,很多地方使用到了它,比如鎖升級(jí)中自旋鎖就有用到,主要是通過處理器的指令來保證操作的原子性,它主要包含三個(gè)變量:
1.變量內(nèi)存地址 2.舊的預(yù)期值 A 3.準(zhǔn)備設(shè)置的新值 B
當(dāng)一個(gè)線程需要修改一個(gè)共享變量的值,完成這個(gè)操作需要先取出共享變量的值,賦給 A,基于 A 進(jìn)行計(jì)算,得到新值 B,在用預(yù)期原值 A 和內(nèi)存中的共享變量值進(jìn)行比較,如果相同就認(rèn)為其他線程沒有進(jìn)行修改,而將新值寫入內(nèi)存

CAS的缺點(diǎn)
CPU開銷比較大:在并發(fā)量比較高的情況下,如果許多線程反復(fù)嘗試更新某一個(gè)變量,卻又一直更新不成功,又因?yàn)樽孕臅r(shí)候會(huì)一直占用CPU,如果CAS一直更新不成功就會(huì)一直占用,造成CPU的浪費(fèi)。
ABA 問題:比如線程 A 去修改 1 這個(gè)值,修改成功了,但是中間 線程 B 也修改了這個(gè)值,但是修改后的結(jié)果還是 1,所以不影響 A 的操作,這就會(huì)有問題。可以用版本號(hào)來解決這個(gè)問題。
只能保證一個(gè)共享變量的原子性
26.聊聊 ReentrantLock 吧
ReentrantLock 意為可重入鎖,說起 ReentrantLock 就不得不說 AQS ,因?yàn)槠涞讓泳褪?strong style="color: rgb(53, 179, 120);">使用 AQS 去實(shí)現(xiàn)的。
ReentrantLock有兩種模式,一種是公平鎖,一種是非公平鎖。
公平模式下等待線程入隊(duì)列后會(huì)嚴(yán)格按照隊(duì)列順序去執(zhí)行 非公平模式下等待線程入隊(duì)列后有可能會(huì)出現(xiàn)插隊(duì)情況
公平鎖

第一步:獲取狀態(tài)的 state 的值 如果 state=0 即代表鎖沒有被其它線程占用,執(zhí)行第二步。 如果 state!=0 則代表鎖正在被其它線程占用,執(zhí)行第三步。 第二步:判斷隊(duì)列中是否有線程在排隊(duì)等待 如果不存在則直接將鎖的所有者設(shè)置成當(dāng)前線程,且更新狀態(tài) state 。 如果存在就入隊(duì)。 第三步:判斷鎖的所有者是不是當(dāng)前線程 如果是則更新狀態(tài) state 的值。 如果不是,線程進(jìn)入隊(duì)列排隊(duì)等待。
非公平鎖

獲取狀態(tài)的 state 的值 如果 state=0 即代表鎖沒有被其它線程占用,則設(shè)置當(dāng)前鎖的持有者為當(dāng)前線程,該操作用 CAS 完成。 如果不為0或者設(shè)置失敗,代表鎖被占用進(jìn)行下一步。 此時(shí)獲取 state 的值 如果是,則給state+1,獲取鎖 如果不是,則進(jìn)入隊(duì)列等待 如果是0,代表剛好線程釋放了鎖,此時(shí)將鎖的持有者設(shè)為自己 如果不是0,則查看線程持有者是不是自己
27.多線程的創(chuàng)建方式有哪些?
1、繼承Thread類,重寫run()方法
public?class?Demo?extends?Thread{
????//重寫父類Thread的run()
????public?void?run()?{
????}
????public?static?void?main(String[]?args)?{
????????Demo?d1?=?new?Demo();
????????Demo?d2?=?new?Demo();
????????d1.start();
????????d2.start();
????}
}
2.實(shí)現(xiàn)Runnable接口,重寫run()
public?class?Demo2?implements?Runnable{
????//重寫Runnable接口的run()
????public?void?run()?{
????}
????
????public?static?void?main(String[]?args)?{
????????Thread?t1?=?new?Thread(new?Demo2());
????????Thread?t2?=?new?Thread(new?Demo2());
????????t1.start();
????????t2.start();
????}
}
3.實(shí)現(xiàn) Callable 接口
public?class?Demo?implements?Callable{
????public?String?call()?throws?Exception?{
????????System.out.println("正在執(zhí)行新建線程任務(wù)");
????????Thread.sleep(2000);
????????return?"結(jié)果";
????}
????public?static?void?main(String[]?args)?throws?InterruptedException,?ExecutionException?{
????????Demo?d?=?new?Demo();
????????FutureTask?task?=?new?FutureTask<>(d);
????????Thread?t?=?new?Thread(task);
????????t.start();
????????//獲取任務(wù)執(zhí)行后返回的結(jié)果
????????String?result?=?task.get();
????}
????
}
4.使用線程池創(chuàng)建
public?class?Demo?{
????public?static?void?main(String[]?args)?{
????????Executor?threadPool?=?Executors.newFixedThreadPool(5);
????????for(int?i?=?0?;i?10?;?i++)?{
????????????threadPool.execute(new?Runnable()?{
????????????????public?void?run()?{
????????????????????//todo
????????????????}
????????????});
????????}
????????
????}
}
28.線程池有哪些參數(shù)?
1.corePoolSize:核心線程數(shù),線程池中始終存活的線程數(shù)。
2.maximumPoolSize: 最大線程數(shù),線程池中允許的最大線程數(shù)。
3.keepAliveTime: 存活時(shí)間,線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止。
4.unit: 單位,參數(shù)keepAliveTime的時(shí)間單位,7種可選。
5.workQueue: 一個(gè)阻塞隊(duì)列,用來存儲(chǔ)等待執(zhí)行的任務(wù),均為線程安全,7種可選。
6.threadFactory: 線程工廠,主要用來創(chuàng)建線程,默及正常優(yōu)先級(jí)、非守護(hù)線程。
7.handler:拒絕策略,拒絕處理任務(wù)時(shí)的策略,4種可選,默認(rèn)為AbortPolicy。
29.線程池的執(zhí)行流程?

判斷線程池中的線程數(shù)是否大于設(shè)置的核心線程數(shù) 如果沒有滿,則放入隊(duì)列,等待線程空閑時(shí)執(zhí)行任務(wù) 如果隊(duì)列已經(jīng)滿了,則判斷是否達(dá)到了線程池設(shè)置的最大線程數(shù) 如果沒有達(dá)到,就創(chuàng)建新線程來執(zhí)行任務(wù) 如果已經(jīng)達(dá)到了最大線程數(shù),則執(zhí)行指定的拒絕策略 如果小于,就創(chuàng)建一個(gè)核心線程來執(zhí)行任務(wù) 如果大于,就會(huì)判斷緩沖隊(duì)列是否滿了
30.線程池的拒絕策略有哪些?
AbortPolicy:直接丟棄任務(wù),拋出異常,這是默認(rèn)策略 CallerRunsPolicy:只用調(diào)用者所在的線程來處理任務(wù) DiscardOldestPolicy:丟棄等待隊(duì)列中最舊的任務(wù),并執(zhí)行當(dāng)前任務(wù) DiscardPolicy:直接丟棄任務(wù),也不拋出異常
31.介紹一下四種引用類型?
強(qiáng)引用 StrongReference
Object?obj?=?new?Object();?
//只要obj還指向Object對象,Object對象就不會(huì)被回收
垃圾回收器不會(huì)回收被引用的對象,哪怕內(nèi)存不足時(shí),JVM 也會(huì)直接拋出 OutOfMemoryError,除非賦值為 null。
軟引用 SoftReference
軟引用是用來描述一些非必需但仍有用的對象。在內(nèi)存足夠的時(shí)候,軟引用對象不會(huì)被回收,只有在內(nèi)存不足時(shí),系統(tǒng)則會(huì)回收軟引用對象,如果回收了軟引用對象之后仍然沒有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。
弱引用 WeakReference
弱引用的引用強(qiáng)度比軟引用要更弱一些,無論內(nèi)存是否足夠,只要 JVM 開始進(jìn)行垃圾回收,那些被弱引用關(guān)聯(lián)的對象都會(huì)被回收。
虛引用 PhantomReference
虛引用是最弱的一種引用關(guān)系,如果一個(gè)對象僅持有虛引用,那么它就和沒有任何引用一樣,它隨時(shí)可能會(huì)被回收,在 JDK1.2 之后,用 PhantomReference 類來表示,通過查看這個(gè)類的源碼,發(fā)現(xiàn)它只有一個(gè)構(gòu)造函數(shù)和一個(gè) get() 方法,而且它的 get() 方法僅僅是返回一個(gè)null,也就是說將永遠(yuǎn)無法通過虛引用來獲取對象,虛引用必須要和 ReferenceQueue 引用隊(duì)列一起使用,NIO 的堆外內(nèi)存就是靠其管理。
32.深拷貝、淺拷貝是什么?
淺拷貝并不是真的拷貝,只是復(fù)制指向某個(gè)對象的指針,而不復(fù)制對象本身,新舊對象還是共享同一塊內(nèi)存。 深拷貝會(huì)另外創(chuàng)造一個(gè)一模一樣的對象,新對象跟原對象不共享內(nèi)存,修改新對象不會(huì)改到原對象。
33.聊聊 ThreadLocal 吧
ThreadLocal其實(shí)就是線程本地變量,他會(huì)在每個(gè)線程都創(chuàng)建一個(gè)副本,那么在線程之間訪問內(nèi)部副本變量就行了,做到了線程之間互相隔離。 
ThreadLocal 有一個(gè)靜態(tài)內(nèi)部類 ThreadLocalMap,ThreadLocalMap 又包含了一個(gè) Entry 數(shù)組,Entry 本身是一個(gè)弱引用,他的 key 是指向 ThreadLocal 的弱引用,弱引用的目的是為了防止內(nèi)存泄露,如果是強(qiáng)引用那么除非線程結(jié)束,否則無法終止,可能會(huì)有內(nèi)存泄漏的風(fēng)險(xiǎn)。 但是這樣還是會(huì)存在內(nèi)存泄露的問題,假如 key 和 ThreadLocal 對象被回收之后,entry 中就存在 key 為 null ,但是 value 有值的 entry 對象,但是永遠(yuǎn)沒辦法被訪問到,同樣除非線程結(jié)束運(yùn)行。解決方法就是調(diào)用 remove 方法刪除 entry 對象。
34.一個(gè)對象的內(nèi)存布局是怎么樣的?

1.對象頭: 對象頭又分為 MarkWord 和 Class Pointer 兩部分。 MarkWord:包含一系列的標(biāo)記位,比如輕量級(jí)鎖的標(biāo)記位,偏向鎖標(biāo)記位,gc記錄信息等等。 ClassPointer:用來指向?qū)ο髮?yīng)的 Class 對象(其對應(yīng)的元數(shù)據(jù)對象)的內(nèi)存地址。在 32 位系統(tǒng)占 4 字節(jié),在 64 位系統(tǒng)中占 8 字節(jié)。 2.Length:只在數(shù)組對象中存在,用來記錄數(shù)組的長度,占用 4 字節(jié) 3.Instance data: 對象實(shí)際數(shù)據(jù),對象實(shí)際數(shù)據(jù)包括了對象的所有成員變量,其大小由各個(gè)成員變量的大小決定。(這里不包括靜態(tài)成員變量,因?yàn)槠涫窃诜椒▍^(qū)維護(hù)的) 4.Padding:Java 對象占用空間是 8 字節(jié)對齊的,即所有 Java 對象占用 bytes 數(shù)必須是 8 的倍數(shù),是因?yàn)楫?dāng)我們從磁盤中取一個(gè)數(shù)據(jù)時(shí),不會(huì)說我想取一個(gè)字節(jié)就是一個(gè)字節(jié),都是按照一塊兒一塊兒來取的,這一塊大小是 8 個(gè)字節(jié),所以為了完整,padding 的作用就是補(bǔ)充字節(jié),保證對象是 8 字節(jié)的整數(shù)倍。
對了,本篇已經(jīng)收錄到《Java 程序員進(jìn)階之路》專欄的「并發(fā)編程」篇,該專欄在 GitHub 上已經(jīng)收獲 837?枚星標(biāo):
https://github.com/itwanger/toBeBetterJavaer
希望越來越多的 Java 愛好者能因?yàn)檫@個(gè)開源項(xiàng)目而受益,而越來越多人的 star,也會(huì)激勵(lì)我創(chuàng)作出更優(yōu)質(zhì)的內(nèi)容~
這里簡單聊一下我對 Java 八股文的看法吧。
在面試中,一個(gè)知識(shí)點(diǎn),如果你能有條理地把使用以及原理說出來,我稱之為八股。如果你能在此基礎(chǔ)上,理解底層的原理,換句話說能用思維導(dǎo)圖或者手繪的形式向他人解釋明白,我稱之為能力。
面試造火箭,工作擰螺絲已經(jīng)變成了一個(gè)老生常談的話題,網(wǎng)上鋪天蓋地的面試八股文也有很多。那這些八股真的就那么臭那么不可取吧。
在我看來,恰恰相反。
記得上學(xué)那會(huì),老師經(jīng)常要求我們背課文,我很不理解,尤其是古詩詞,背它有什么卵用呢,和人聊天說話的時(shí)候,總不能切換到古文吧?
但為了考試,還是得硬著頭皮去背。但仍然不知道到底有什么實(shí)際用處。
直到后來有一次和一個(gè)朋友出去閑逛,他背了一篇范仲淹的《岳陽樓記》,通篇全部都背了下來,我當(dāng)時(shí)為之驚嘆啊!自從上了大學(xué)后,就再也沒有背過古詩詞,但這一次,我被朋友折服了——我只記得“先天下之憂而憂,后天下之樂而樂”。
八股文的背后蘊(yùn)藏的是對知識(shí)的總結(jié)和提煉,也只有在完全理解的基礎(chǔ)上才能記得住,并且過很久很久都不會(huì)忘,還能在實(shí)際的工作場景中用得到!
因此,我希望大家該背則背,并且不要只是在找工作之前短時(shí)間內(nèi)突擊,而是每天背一點(diǎn),真正把知識(shí)儲(chǔ)備起來~

沒有什么使我停留——除了目的,縱然岸旁有玫瑰、有綠蔭、有寧靜的港灣,我是不系之舟。
推薦閱讀:
