1. String類為什么是final的生蚁。
答:申明為final的類是不能被繼承的,這防止了String類被子類修改戏自。由于String類是被設(shè)計為Immutable的邦投,也就是不可變的,用final修飾擅笔,能防止String類被子類修改成可變的志衣。
*題外話:*
關(guān)于String不可變的原因,收集了以下幾個點:
1.由于String常量池的設(shè)計猛们,當需要創(chuàng)建String時念脯,會先從常量池中找是否已經(jīng)存在該值,如果有則返回其引用弯淘,而不是創(chuàng)建一個相同值的String對象绿店。如果String可變,那改變一個值會將所有引用這個值的變量都改了庐橙。
2.String大量作為參數(shù)傳遞假勿,如果String值可變,那在方法內(nèi)部就可以改變String的值态鳖,這會非常麻煩转培,以及可能造成安全上的問題。
3.Because immutable objects can not be changed, they can be shared among multiple threads freely. This eliminates the requirements of doing synchronization.
關(guān)于如何創(chuàng)建不可變的類的規(guī)則:
1.immutable對象的狀態(tài)在創(chuàng)建之后就不能發(fā)生改變浆竭,任何對它的改變都應(yīng)該產(chǎn)生一個新的對象浸须。
2.Immutable類的所有的屬性都應(yīng)該是final的惨寿。
3.對象必須被正確的創(chuàng)建,比如:對象引用在對象創(chuàng)建過程中不能泄露(leak)删窒。
4.類應(yīng)該是final的缤沦,以此來限制子類繼承父類,以避免子類改變了父類的immutable特性易稠。
5.如果類中包含mutable類對象缸废,那么返回給客戶端的時候,返回該對象的一個拷貝驶社,而不是該對象本身(該條可以歸為第一條中的一個特例)
(final關(guān)鍵字可以參考Java編程思想140企量,java核心技術(shù)卷一160)
2. Java集合類:list、set亡电、queue届巩、map實現(xiàn)類
HashSet, linkedHashSet,HashMap,linkedHashMap:都是基于散列表實現(xiàn)的份乒,即鏈表數(shù)組恕汇。
LinkedHashMap繼承了HashMap,并在node上多加了兩個指針或辖,來維護每個node之間的順序瘾英,原有的hashmap的結(jié)構(gòu)是沒變的。
HashSet只是封裝下HashMap颂暇,實際就是一個hashMap缺谴,只是這個map只能對鍵進行操作,set里的元素被當做是Map的key來存的耳鸯,map的value存的是一個static final new object()湿蛔,循環(huán)這個set其實就是取得HashMap的keySet來循環(huán)。
LinkedHashSet繼承了HashSet县爬,調(diào)用的是HashSet里構(gòu)造linkedHashMap的構(gòu)造方法阳啥,實際就是個linkedHashMap,然后只能對map的key進行操作财喳。
TreeSet察迟,TreeMap: 都是基于紅黑樹實現(xiàn)的,特點是所得到的結(jié)果是經(jīng)過排序的纲缓,次序由元素實現(xiàn)的Comparable或者Comparator決定卷拘。將元素加到TreeSet比HashSet慢,不過TreeSet是排序的祝高。
PriorityQueue:是接口Queue的實現(xiàn)栗弟。?使用堆數(shù)據(jù)結(jié)構(gòu),堆是一個可以自我調(diào)整的二叉樹工闺,對樹執(zhí)行添加和刪除操作乍赫,可以讓最小的元素移動到根瓣蛀,而不必花費時間對元素進行排序。優(yōu)先級隊列可以按任意的順序插入元素雷厂,卻總是按照排序的順序進行檢索惋增,也就是無論何時調(diào)用remove方法,總會獲得當前優(yōu)先級隊列中最小的元素(即優(yōu)先級最高的元素改鲫,習慣上將1設(shè)為最高優(yōu)先級)诈皿。默認是按元素的自然順序排序,如存儲Integer像棘,char稽亏,String類型的元素,也可以通過自定義Comparator來排序缕题。但是與TreeSet不同截歉,如果僅迭代這些元素,是不會對元素進行排序的烟零,PriorityQueue可以確保調(diào)用peek瘪松,poll,remove方法時锨阿,獲取的元素是隊列中優(yōu)先級最高的元素宵睦。
DelayQueue:無界阻塞隊列,用于放置實現(xiàn)了Delayed接口的對象群井,其中對象只能在到期時才能從隊列中取出状飞。DelayQueue其實就是在每次往優(yōu)先級隊列中添加元素,然后以元素的delay/過期值作為排序的因素,以此來達到先過期的元素會拍在隊首,每次從隊列里取出來都是最先要過期的元素毫胜。
(集合相關(guān)參考java編程思想第17章书斜,java核心技術(shù)卷一第十三章)
3. ArrayList和LinkedList各自實現(xiàn)和區(qū)別
ArrayList是采用的是數(shù)組形式來保存對象的,這種方式將對象放在連續(xù)的位置中酵使,所以最大的缺點就是插入刪除時很慢荐吉,因為每次操作都需要移動其他元素的位置。優(yōu)點是隨機訪問很快口渔,可以通過查看該集合是否實現(xiàn)了RandomAccess接口來檢測一個特定的集合是否支持高效的隨機訪問样屠。
LinkedList是采用鏈表來存儲數(shù)據(jù),鏈表將對象存儲在獨立的節(jié)點中缺脉,每個節(jié)點還存放著序列中下一個節(jié)點的引用痪欲。鏈表增刪快,但是查詢慢攻礼,每次查詢需要迭代整個列表业踢。
4. HashMap的源碼,實現(xiàn)原理礁扮,底層結(jié)構(gòu)知举。
Hashmap底層是以鏈表數(shù)組實現(xiàn)的瞬沦,數(shù)組并不保存鍵本身,也不直接保存值雇锡,而是保存key和value節(jié)點的鏈表逛钻。原理是通過鍵對象生成一個數(shù)字,將其作為數(shù)組的下標锰提,這個數(shù)字就是散列碼(哈希值)(哈希值還需對map的size取余曙痘,才是最終下標,保證計算出來的下標都在size內(nèi))立肘。也就是key和Value存儲在key的散列碼對應(yīng)的那個數(shù)組下標的鏈表里面屡江。由于不同的key可以產(chǎn)生相同的散列碼,這個沖突由鏈表解決赛不,如果key的equals()也相同惩嘉,那直接替換oldVaue;如果不相同踢故,那將value add到鏈表的最后文黎。取值也是一樣,先查詢key的散列碼對應(yīng)的鏈表元素是否存在殿较,存在就循環(huán)鏈表里每一個元素耸峭,然后取出與key相同(equals())的那個元素×芨伲可以嘗試用LinkedList實現(xiàn)一個簡單的hashmap劳闹。源碼里是用的Node[] table。
(hashmap相關(guān)參考java編程思想483~495洽瞬,java核心技術(shù)卷一576~598)
5. 如何自己實現(xiàn)一個Map本涕,描述
可以直接用LinkedList數(shù)組來存儲,list里存包含鍵值對的Entry對象伙窃。put方法存放數(shù)據(jù)菩颖,get方法取數(shù)據(jù)。還需要一個entrySet方法可以遍歷整個map为障。
Put方法的基本思路是:先根據(jù)key的hash值與size取余來確定該key-value在數(shù)組中存放的位置晦闰。如果數(shù)組中這個位置沒有元素,那直接new一個entry對象鳍怨,存到LinkedList里呻右,再把linkedList設(shè)置到數(shù)組這個位置上。如果數(shù)組中已有元素鞋喇,將這個linkedList取出來声滥,循環(huán)list并將新的key與list里所有key比較,如果找到相同的key确徙,直接覆蓋value醒串。如果沒有相同的key执桌,那也new一個entry對象加到這個linkedlist末尾。
Get方法跟put方法用相同的算法查找key芜赌,即根據(jù)key的hash值與size取余來確定該key-value在數(shù)組中存放的位置仰挣,沒有就返回null。有就循環(huán)比較是否有相同的key缠沈,有就取出對應(yīng)的value膘壶。
EntrySet方法主要是要返回一個有迭代器的集合,可以遍歷洲愤,所以可以直接new一個hashSet颓芭,然后遍歷整個鏈表數(shù)組,將每個entry都加到HashSet里柬赐。
6. Hash沖突怎么辦亡问?哪些解決散列沖突的方法,rehash肛宋?
以下內(nèi)容網(wǎng)上查的州藕,都能查到就不貼過來了。個人覺得得看點書理解下酝陈,推薦看下算法導論-11章 散列表〈膊#現(xiàn)在hashMap里是用的鏈表處理沖突。
一般解決hash沖突有四種方式:開放定址法沉帮,再哈希锈死,鏈接法,建立公共溢出區(qū)穆壕。
7. HashMap沖突很厲害待牵,最差性能,你會怎么解決?
HashMap使用鏈接法解決沖突粱檀,最差性能就是所有元素全部存放在同一個地址洲敢,形成一個單鏈表,這樣查找茄蚯、刪除元素性能就是O(n)∧烙牛可以考慮在節(jié)點過多時將單鏈表轉(zhuǎn)成紅黑樹渗常,雙向鏈表刪除元素是O(1),紅黑樹查找性能是O(logN)汗盘,jdk1.8的hashmap已經(jīng)實現(xiàn)了皱碘。?
8. Hashtable,HashMap,ConcurrentHashMap 底層實現(xiàn)原理與線程安全問題
HashTable是用同步關(guān)鍵字保證線程安全,在線程競爭激烈的情況下隐孽,效率非常低癌椿。如線程1使用put方法添加元素健蕊,線程2不但不能使用put添加元素,也不能使用get獲取元素踢俄。
HashMap是非線程安全的缩功。在多線程環(huán)境下使用put操作會引起死循環(huán),導致CPU利用率接近100%都办,因為多線程會導致HashMap的Entry鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu)嫡锌,Entry的next節(jié)點永遠不為空,就會產(chǎn)生死循環(huán)獲取Entry琳钉。
JDK1.7中ConcurrentHashMap實現(xiàn):使用分段鎖技術(shù)控制并發(fā)訪問势木,首先將數(shù)據(jù)分成一段一段地存儲,然后給每一段數(shù)據(jù)配一把鎖歌懒,當一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候啦桌,其他段的數(shù)據(jù)也能被其他線程訪問。因此當多個線程訪問不同數(shù)據(jù)段的數(shù)據(jù)時及皂,線程間不存在鎖競爭震蒋,從而有效提高并發(fā)訪問效率。
JDK1.7里ConcurrentHashMap數(shù)據(jù)結(jié)構(gòu):之前了解過HashMap是以鏈表數(shù)組存儲的躲庄,數(shù)組下標表示通過Key的hash值計算得到位置查剖,key和value就存在該數(shù)組位置的Entry鏈表里,一個Entry節(jié)點包含key噪窘,value笋庄,和next節(jié)點。
ConcurrentHashMap是由Segment數(shù)組和Entry數(shù)組構(gòu)成(Entry數(shù)組可以理解成一個HashMap)倔监。Segment是一種可重入鎖(ReetrantLock)直砂,在ConcurrentHashMap里扮演鎖的角色。Entry用于存鍵值對數(shù)據(jù)浩习。一個ConcurrentHashMap包含一個Segment數(shù)組静暂,segment的結(jié)構(gòu)和HashMap類似,是鏈表數(shù)組結(jié)構(gòu)谱秽。一個segment包含一個Entry數(shù)組洽蛀,每個Entry是一個鏈表結(jié)構(gòu)的元素。每個Segment守護著一個Entry數(shù)組里的元素疟赊,當對Entry數(shù)組的數(shù)據(jù)進行修改時郊供,必須首先獲得與它對應(yīng)的Segment鎖。
下圖來自網(wǎng)絡(luò):
9. 反射中近哟,Class.forName和classloader的區(qū)別
Class.forName默認加載并初始化class驮审,即會執(zhí)行靜態(tài)代碼塊和初始化靜態(tài)變量。
ClassLoader默認只加載類進JVM,并不初始化疯淫。
如JDBC鏈接地来,就需要使用Class.forName,因為需要執(zhí)行靜態(tài)代碼塊注冊驅(qū)動到DriverManager上熙掺,之后才能通過DriverManager獲取連接未斑。
?Class.forName("com.mysql.jdbc.Driver");//通過這種方式將驅(qū)動注冊到驅(qū)動管理器上。
題外話:
Proxy.newProxyInstance()是在類的字節(jié)碼文件不存在适掰,也就是沒有xx.class文件的時候颂碧,先根據(jù)該類的接口創(chuàng)建一個代理類的實例即xx.class文件,然后通過classLoader加載進JVM,最后根據(jù)JVM里的字節(jié)碼文件創(chuàng)建實例并返回。
Class.forName()是在類的實例存在即XX.class文件存在类浪,調(diào)用該方法讓JVM將該文件加載進JVM载城,成為JVM里的一份字節(jié)碼文件,也就是Class對象费就。不同于ClassLoader的是诉瓦,Class.forName默認會初始化類,也就是load進JVM后力细,還會執(zhí)行類里的靜態(tài)方法和靜態(tài)成員變量的賦值睬澡。
ClassLoader,也是在類的實例存在即XX.class文件存在眠蚂,調(diào)用該方法讓JVM將該文件加載進JVM煞聪,成為JVM里的一份字節(jié)碼文件,也就是class對象逝慧。
(類加載機制昔脯,參考深入理解Java虛擬機第7章)
10. Java7、Java8的新特性
Java7:(只關(guān)注過這兩個笛臣。云稚。)
1.switch中可以使用字串了
2.運用List tempList = new ArrayList<>(); 即泛型實例化類型自動推斷
Java8:
1.Lambda 表達式?? Lambda允許把函數(shù)作為一個方法的參數(shù)(函數(shù)作為參數(shù)傳遞進方法中。
2.方法引用?? 方法引用提供了非常有用的語法沈堡,可以直接引用已有Java類或?qū)ο螅▽嵗┑姆椒ɑ驑?gòu)造器静陈。與lambda聯(lián)合使用,方法引用可以使語言的構(gòu)造更緊湊簡潔诞丽,減少冗余代碼鲸拥。
3.默認方法?? 默認方法就是一個在接口里面有了一個實現(xiàn)的方法。
4.Stream API ?新添加的Stream API(java.util.stream)把真正的函數(shù)式編程風格引入到Java中率拒。
5.Date Time API ? 加強對日期與時間的處理崩泡。
6.Optional 類?? Optional 類已經(jīng)成為Java 8 類庫的一部分,用來解決空指針異常猬膨。
Lambda,方法引用,Stream具體語法可參考:
https://blog.csdn.net/jinzhencs/article/details/50748202
11. JDK1.8 ConcurrentHashMap原理及實現(xiàn)
Jdk1.8里已經(jīng)不用分段鎖了勃痴,沒有了segment數(shù)組谒所。整體實現(xiàn)其實和HashMap是差不多的,只是加了線程安全相關(guān)的代碼沛申,結(jié)合了Synchronized劣领、volitale關(guān)鍵字,以及CAS的方式铁材,去實現(xiàn)線程安全尖淘。
此外HashMap在1.8中數(shù)據(jù)結(jié)構(gòu)有改動,如下圖2-4描述:
12. string著觉、stringbuilder村生、stringbuffer區(qū)別
String對象是不可變的,String類里每一個看起來能修改String值的方法饼丘,實際上都創(chuàng)建了一個新的String對象趁桃,用來包含修改后的值,原始的String對象并沒有改變肄鸽。
由于String對象不可變卫病,用+拼接字符串,會產(chǎn)生一大堆需要垃圾回收的中間對象典徘,可以簡單理解為每調(diào)用一次+就生成一個新的String對象蟀苛。
StringBuilder的append方法拼接字符串,只會生成一個StringBuilder對象逮诲,效率更高帜平。
StringBuffer是線程安全的,方法上都加了同步關(guān)鍵字汛骂,開銷更大罕模。
13 .異常的結(jié)構(gòu),運行時異常和非運行時異常帘瞭,各舉個例子
Throwable類是java中所有異常的超類淑掌,有Exception和Error兩個子類。
Error是程序無法處理的錯誤蝶念,如OutOfMemoryError抛腕,stackOverFlowError,NoClassDefFoundError媒殉。
Exception是程序本身可以處理的異常担敌。RuntimeException是Exception的一個子類,其子類如ArrayIndexOutOfBoundsException廷蓉,NullPointerException全封,ClassCastException等。非運行時異常如IOException,F(xiàn)ileNotFoundException刹悴。
14. String 類的常用方法
Length(),replace(),split(),indexOf(),CharAt(),trim(),toUpperCase(),subString(),equals().....
15. Java 的引用類型有哪幾種
強引用行楞,軟引用,弱引用土匀,虛引用子房。
強引用:直接new一個對像并賦值給一個變量,這個變量就是對這個對象的一個強引用就轧。只要這個強引用沒被釋放证杭,那么這個對象就不會被回收。
軟引用:用SoftReference包裹的對象妒御,如果一個對象只存在一個軟引用解愤,在內(nèi)存溢出之前携丁,將會被回收梦鉴。軟引用主要用于實現(xiàn)內(nèi)存敏感的高速緩存李茫。當內(nèi)存不夠用時魄宏,sf對象會被回收,sf.get()會返回null。
弱引用:用WeakReference包裹的對象,如果一個對象只存在一個弱引用烁焙,在垃圾回收時這個對象會被回收。WeakHashMap就是用的WeakReference來保存對象。WeakReference的Entry繼承了WeakReference類吃既。
虛引用:?Phantomreference,垃圾回收時回收,永遠無法通過引用取到對象值。
pf.get();//永遠返回null
pf.isEnQueued();//返回對象是否已被回收
虛引用主要用于檢測對象是否已經(jīng)從內(nèi)存中刪除,可以通過這個通知機制來做額外的清場工作。?因此有些情況可以用PhantomReference 代替finalize()夺颤,做資源釋放更明智寥裂。
(引用相關(guān)參考java編程思想518褐啡,深入理解JVM虛擬機65頁,博客資料)
?16. 抽象類和接口的區(qū)別
一般說來抽象類都包含一個或多個抽象方法,此外還可以包含具體數(shù)據(jù)和具體方法翼岁。不過也可以將沒有包含抽象方法的類申明為抽象類榆俺,抽象類不能被實例化回窘。
Jdk1.8之前接口只能有空方法酒觅,jdk1.8里接口可以有多個default的具體方法掂榔。
一個類可以實現(xiàn)多個接口继效,但是只能繼承一個超類。
17. java的基礎(chǔ)類型和字節(jié)大小。
Int: 4字節(jié)(32位穴豫,最大值2^31-1)
byte:1 字節(jié)(8位)
short:2 字節(jié)(16位)
Long:8 字節(jié)(64位)
double:8 字節(jié)(64位)
float:4字節(jié)(32位)
boolean:1位
char:2 字節(jié)(16位)
18. hashCode() 與?equals() 生成算法司抱、方法怎么重寫
Jdk1.7中烈炭,hashCode的定義可以調(diào)用Objects.hash()符隙,可傳入多個需要散列的屬性作為參數(shù),hashcode方法可以簡單的寫成:Objects.hash(name, id);
Equals就是必須和hashCode的定義一致卑惜,如果x.equals(y),那么x和y的hashcode必須相同。也就是定義equals和hashcode的域要相同欺栗。
(hashcode和equals參考Java核心技術(shù)卷一169-175)
19. Java類加載器包括幾種毫痕?他們之間的父子關(guān)系是怎么樣的?雙親委派機制是什么意思迟几?有什么好處消请?
? ? 啟動類加載器:加載java_home/lib下面jvm識別的jar
? ? 擴展類加載器:加載java_home/lib/ext下面的jar
? ? 應(yīng)用程序類加載器:加載用戶類路徑也就是classpath上的jar
? ? 自定義加載器:
? ? 雙親委派模型:先讓父類加載,如果沒有父類类腮,就到頂層的啟動類加載器臊泰,如果都沒有加載出來,那再自己去加載蚜枢。
(參考深入理解JVM虛擬機第7章)
20.講講NIO
BIO就是同步阻塞式IO缸逃,當一個客戶端向server端發(fā)起請求,必須等到Server端把這個請求處理完成厂抽,才能接收到server端的返回消息需频。同時,如果有其他請求發(fā)送到server端筷凤,server端是不會對新請求做出響應(yīng)的昭殉,必須等到之前的請求處理完。這就是同步阻塞式藐守。
NIO是同步非阻塞式IO挪丢,NIO面向Channel和Buffer,而不是傳統(tǒng)的IO流卢厂∏睿客戶端發(fā)送的連接請求被作為一個channel注冊到Selector上,Selector不斷輪詢各個channel的狀態(tài)足淆,只有在channel狀態(tài)是可連接或者可讀或者可寫的時候巢块,才會進行IO操作礁阁。因此NIO不用開啟一個線程等待讀或者寫數(shù)據(jù),而BIO則會一直等族奢。
21. String編碼UTF-8?和GBK的區(qū)別?
GBK包含全部中文字符姥闭;UTF-8則包含全世界所有國家需要用到的字符。GBK編碼專門用來解決中文編碼的越走,是雙字節(jié)的棚品。不論中英文都是雙字節(jié)的。UTF-8 編碼是用以解決國際上字符的一種多字節(jié)編碼廊敌,它對英文使用8位(即一個字節(jié))铜跑,中文使用24位(三個字節(jié))來編碼。
所以如果是UTF-8骡澈,英文是1字節(jié)锅纺,中文是三字節(jié)。
GBK肋殴,英文中文都是2字節(jié)囤锉。
22. 什么時候使用字節(jié)流、什么時候使用字符流?
使用字符流的應(yīng)用場景:如果是讀寫數(shù)據(jù)的時候需要轉(zhuǎn)換成字符护锤,比如處理文字則使用字符流官地。
使用字節(jié)流的應(yīng)用場景:如果讀寫的數(shù)據(jù)都不需要轉(zhuǎn)換成字符的時候,比如處理圖片則使用字節(jié)流烙懦。