Java經(jīng)典面試題最全匯總208道(一)
前言
短時(shí)間提升自己最快的手段就是背面試題,最近總結(jié)了Java常用的面試題,分享給大家,希望大家都能圓夢(mèng)大廠,加油,我命由我不由天。
1、JDK 和 JRE 有什么區(qū)別?
JDK(Java Development Kit),Java開發(fā)工具包
JRE(Java Runtime Environment),Java運(yùn)行環(huán)境
JDK中包含JRE,JDK中有一個(gè)名為jre的目錄,里面包含兩個(gè)文件夾bin和lib,bin就是JVM,lib就是JVM工作所需要的類庫。
2、== 和 equals 的區(qū)別是什么?
- 對(duì)于基本類型,==比較的是值;
- 對(duì)于引用類型,==比較的是地址;
- equals不能用于基本類型的比較;
- 如果沒有重寫equals,equals就相當(dāng)于==;
- 如果重寫了equals方法,equals比較的是對(duì)象的內(nèi)容;
3、final 在 java 中有什么作用?
(1)用來修飾一個(gè)引用
- 如果引用為基本數(shù)據(jù)類型,則該引用為常量,該值無法修改;
- 如果引用為引用數(shù)據(jù)類型,比如對(duì)象、數(shù)組,則該對(duì)象、數(shù)組本身可以修改,但指向該對(duì)象或數(shù)組的地址的引用不能修改。
- 如果引用時(shí)類的成員變量,則必須當(dāng)場(chǎng)賦值,否則編譯會(huì)報(bào)錯(cuò)。
(2)用來修飾一個(gè)方法
當(dāng)使用final修飾方法時(shí),這個(gè)方法將成為最終方法,無法被子類重寫。
但是,該方法仍然可以被繼承。
(3)用來修飾類
當(dāng)用final修改類時(shí),該類成為最終類,無法被繼承。
比如常用的String類就是最終類。
4、java 中的 Math.round(-1.5) 等于多少?
Math提供了三個(gè)與取整有關(guān)的方法:ceil、floor、round
(1)ceil:向上取整;
Math.ceil(11.3) = 12;
Math.ceil(-11.3) = 11;
(2)floor:向下取整;
Math.floor(11.3) = 11;
Math.floor(-11.3) = -12;
(3)round:四舍五入;
加0.5然后向下取整。
Math.round(11.3) = 11;
Math.round(11.8) = 12;
Math.round(-11.3) = -11;
Math.round(-11.8) = -12;
5、String 屬于基礎(chǔ)的數(shù)據(jù)類型嗎?
不屬于。
八種基本數(shù)據(jù)類型:byte、short、char、int、long、double、float、boolean。
6、String str="i"與 String str=new String(“i”)一樣嗎?
String str="i"會(huì)將起分配到常量池中,常量池中沒有重復(fù)的元素,如果常量池中存中i,就將i的地址賦給變量,如果沒有就創(chuàng)建一個(gè)再賦給變量。
String str=new String(“i”)會(huì)將對(duì)象分配到堆中,即使內(nèi)存一樣,還是會(huì)重新創(chuàng)建一個(gè)新的對(duì)象。
7、如何將字符串反轉(zhuǎn)?
將對(duì)象封裝到stringBuilder中,調(diào)用reverse方法反轉(zhuǎn)。
8、String 類的常用方法都有那些?
(1)常見String類的獲取功能
length:獲取字符串長(zhǎng)度;
charAt(int index):獲取指定索引位置的字符;
indexOf(int ch):返回指定字符在此字符串中第一次出現(xiàn)處的索引;
substring(int start):從指定位置開始截取字符串,默認(rèn)到末尾;
substring(int start,int end):從指定位置開始到指定位置結(jié)束截取字符串;
(2)常見String類的判斷功能
equals(Object obj): 比較字符串的內(nèi)容是否相同,區(qū)分大小寫;
contains(String str): 判斷字符串中是否包含傳遞進(jìn)來的字符串;
startsWith(String str): 判斷字符串是否以傳遞進(jìn)來的字符串開頭;
endsWith(String str): 判斷字符串是否以傳遞進(jìn)來的字符串結(jié)尾;
isEmpty(): 判斷字符串的內(nèi)容是否為空串"";
(3)常見String類的轉(zhuǎn)換功能
byte[] getBytes(): 把字符串轉(zhuǎn)換為字節(jié)數(shù)組;
char[] toCharArray(): 把字符串轉(zhuǎn)換為字符數(shù)組;
String valueOf(char[] chs): 把字符數(shù)組轉(zhuǎn)成字符串。
valueOf可以將任意類型轉(zhuǎn)為字符串;
toLowerCase(): 把字符串轉(zhuǎn)成小寫;
toUpperCase(): 把字符串轉(zhuǎn)成大寫;
concat(String str): 把字符串拼接;
(4)常見String類的其他常用功能
replace(char old,char new) 將指定字符進(jìn)行互換
replace(String old,String new) 將指定字符串進(jìn)行互換
trim() 去除兩端空格
int compareTo(String str) 會(huì)對(duì)照ASCII 碼表 從第一個(gè)字母進(jìn)行減法運(yùn)算 返回的就是這個(gè)減法的結(jié)果。
如果前面幾個(gè)字母一樣會(huì)根據(jù)兩個(gè)字符串的長(zhǎng)度進(jìn)行減法運(yùn)算返回的就是這個(gè)減法的結(jié)果,如果連個(gè)字符串一摸一樣 返回的就是0。
9、new String("a") + new String("b") 會(huì)創(chuàng)建幾個(gè)對(duì)象?
對(duì)象1:new StringBuilder()
對(duì)象2:new String("a")
對(duì)象3:常量池中的"a"
對(duì)象4:new String("b")
對(duì)象5:常量池中的"b"
深入剖析:StringBuilder中的toString():
對(duì)象6:new String("ab")
強(qiáng)調(diào)一下,toString()的調(diào)用,在字符串常量池中,沒有生成"ab"
附加題
String s1 = new String("1") + new String("1");//s1變量記錄的地址為:new String
s1.intern();//在字符串常量池中生成"11"。如何理解:jdk6:創(chuàng)建了一個(gè)新的對(duì)象"11",也就有新的地址;jdk7:此時(shí)常量池中并沒有創(chuàng)建"11",而是創(chuàng)建了一個(gè)指向堆空間中new String("11")的地址;
String s2 = "11";
System.out.println(s1 == s2);//jdk6:false;jdk7:true
10、如何將字符串反轉(zhuǎn)?
添加到StringBuilder中,然后調(diào)用reverse()。
11、String 類的常用方法都有那些?
equals、length、contains、replace、split、hashcode、indexof、substring、trim、toUpperCase、toLowerCase、isEmpty等等。
12、普通類和抽象類有哪些區(qū)別?
抽象類不能被實(shí)例化;
抽象類可以有抽象方法,只需申明,無須實(shí)現(xiàn);
有抽象方法的類一定是抽象類;
抽象類的子類必須實(shí)現(xiàn)抽象類中的所有抽象方法,否則子類仍然是抽象類;
抽象方法不能聲明為靜態(tài)、不能被static、final修飾。
13、接口和抽象類有什么區(qū)別?
(1)接口
接口使用interface修飾;
接口不能實(shí)例化;
類可以實(shí)現(xiàn)多個(gè)接口;
①java8之前,接口中的方法都是抽象方法,省略了public abstract。
②java8之后;接口中可以定義靜態(tài)方法,靜態(tài)方法必須有方法體,普通方法沒有方法體,需要被實(shí)現(xiàn);
(2)抽象類
抽象類使用abstract修飾;
抽象類不能被實(shí)例化;
抽象類只能單繼承;
抽象類中可以包含抽象方法和非抽象方法,非抽象方法需要有方法體;
如果一個(gè)類繼承了抽象類,
①如果實(shí)現(xiàn)了所有的抽象方法,子類可以不是抽象類;
②如果沒有實(shí)現(xiàn)所有的抽象方法,子類仍然是抽象類。
14、java 中 IO 流分為幾種?
(1)按流劃分,可以分為輸入流和輸出流;
(2)按單位劃分,可以分為字節(jié)流和字符流;
字節(jié)流:inputStream、outputStream;
字符流:reader、writer;
15、BIO、NIO、AIO 有什么區(qū)別?
(1)同步阻塞BIO
一個(gè)連接一個(gè)線程。
JDK1.4之前,建立網(wǎng)絡(luò)連接的時(shí)候采用BIO模式,先在啟動(dòng)服務(wù)端socket,然后啟動(dòng)客戶端socket,對(duì)服務(wù)端通信,客戶端發(fā)送請(qǐng)求后,先判斷服務(wù)端是否有線程響應(yīng)。
如果沒有則會(huì)一直等待或者遭到拒絕請(qǐng)求,如果有的話會(huì)等待請(qǐng)求結(jié)束后才繼續(xù)執(zhí)行。
(2)同步非阻塞NIO
NIO主要是想解決BIO的大并發(fā)問題,BIO是每一個(gè)請(qǐng)求分配一個(gè)線程,當(dāng)請(qǐng)求過多時(shí),每個(gè)線程占用一定的內(nèi)存空間,服務(wù)器癱瘓了。
JDK1.4開始支持NIO,適用于連接數(shù)目多且連接比較短的架構(gòu),比如聊天 服務(wù)器,并發(fā)局限于應(yīng)用中。
一個(gè)請(qǐng)求一個(gè)線程。
(3)異步非阻塞AIO
一個(gè)有效請(qǐng)求一個(gè)線程。
JDK1.7開始支持AIO,適用于連接數(shù)目多且連接比較長(zhǎng)的結(jié)構(gòu),比如相冊(cè)服務(wù)器,充分調(diào)用OS參與并發(fā)操作。
16、Files的常用方法都有哪些?
exist
createFile
createDirectory
write
read
copy
size
delete
move
17、什么是反射?
所謂反射,是java在運(yùn)行時(shí)進(jìn)行自我觀察的能力,通過class、constructor、field、method四個(gè)方法獲取一個(gè)類的各個(gè)組成部分。
在Java運(yùn)行時(shí)環(huán)境中,對(duì)任意一個(gè)類,可以知道類有哪些屬性和方法。
這種動(dòng)態(tài)獲取類的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能來自于反射機(jī)制。
18、什么是 java 序列化?什么情況下需要序列化?
序列化就是一種用來處理對(duì)象流的機(jī)制。
將對(duì)象的內(nèi)容流化,將流化后的對(duì)象傳輸于網(wǎng)絡(luò)之間。
序列化是通過實(shí)現(xiàn)serializable接口,該接口沒有需要實(shí)現(xiàn)的方法,implement Serializable只是為了標(biāo)注該對(duì)象是可被序列化的,使用一個(gè)輸出流(FileOutputStream)來構(gòu)造一個(gè)ObjectOutputStream對(duì)象,接著使用ObjectOutputStream對(duì)象的writeObejct(Object object)方法就可以將參數(shù)的obj對(duì)象到磁盤,需要恢復(fù)的時(shí)候使用輸入流。
序列化是將對(duì)象轉(zhuǎn)換為容易傳輸?shù)母袷降倪^程。
例如,可以序列化一個(gè)對(duì)象,然后通過HTTP通過Internet在客戶端和服務(wù)器之間傳輸該對(duì)象。
在另一端,反序列化將從流中心構(gòu)造成對(duì)象。
一般程序在運(yùn)行時(shí),產(chǎn)生對(duì)象,這些對(duì)象隨著程序的停止而消失,但我們想將某些對(duì)象保存下來。
這時(shí),我們就可以通過序列化將對(duì)象保存在磁盤,需要使用的時(shí)候通過反序列化獲取到。
對(duì)象序列化的最主要目的就是傳遞和保存對(duì)象,保存對(duì)象的完整性和可傳遞性。
譬如通過網(wǎng)絡(luò)傳輸或者把一個(gè)對(duì)象保存成本地一個(gè)文件的時(shí)候,需要使用序列化。
19、為什么要使用克隆?如何實(shí)現(xiàn)對(duì)象克隆?深拷貝和淺拷貝區(qū)別是什么?
(1)什么要使用克?。?/p>
想對(duì)一個(gè)對(duì)象進(jìn)行復(fù)制,又想保留原有的對(duì)象進(jìn)行接下來的操作,這個(gè)時(shí)候就需要克隆了。
(2)如何實(shí)現(xiàn)對(duì)象克???
實(shí)現(xiàn)Cloneable接口,重寫clone方法;
實(shí)現(xiàn)Serializable接口,通過對(duì)象的序列化和反序列化實(shí)現(xiàn)克隆,可以實(shí)現(xiàn)真正的深克隆。
BeanUtils,apache和Spring都提供了bean工具,只是這都是淺克隆。
(3)深拷貝和淺拷貝區(qū)別是什么?
淺拷貝:僅僅克隆基本類型變量,不克隆引用類型變量;
深克隆:既克隆基本類型變量,又克隆引用類型變量;
(4)代碼實(shí)例
20、throw 和 throws 的區(qū)別?
(1)throw
作用在方法內(nèi),表示拋出具體異常,由方法體內(nèi)的語句處理;
一定拋出了異常;
(2)throws
作用在方法的聲明上,表示拋出異常,由調(diào)用者來進(jìn)行異常處理;
可能出現(xiàn)異常,不一定會(huì)發(fā)生異常;
21、final、finally、finalize 有什么區(qū)別?
final可以修飾類,變量,方法,修飾的類不能被繼承,修飾的變量不能重新賦值,修飾的方法不能被重寫
finally用于拋異常,finally代碼塊內(nèi)語句無論是否發(fā)生異常,都會(huì)在執(zhí)行finally,常用于一些流的關(guān)閉。
finalize方法用于垃圾回收。
一般情況下不需要我們實(shí)現(xiàn)finalize,當(dāng)對(duì)象被回收的時(shí)候需要釋放一些資源,比如socket鏈接,在對(duì)象初始化時(shí)創(chuàng)建,整個(gè)生命周期內(nèi)有效,那么需要實(shí)現(xiàn)finalize方法,關(guān)閉這個(gè)鏈接。
但是當(dāng)調(diào)用finalize方法后,并不意味著gc會(huì)立即回收該對(duì)象,所以有可能真正調(diào)用的時(shí)候,對(duì)象又不需要回收了,然后到了真正要回收的時(shí)候。
因?yàn)橹罢{(diào)用過一次,這次又不會(huì)調(diào)用了,產(chǎn)生問題。
所以,不推薦使用finalize方法。
22、try-catch-finally 中,如果 catch 中 return 了,finally 還會(huì)執(zhí)行嗎?
23、常見的異常類有哪些?
- NullPointerException:空指針異常;
- SQLException:數(shù)據(jù)庫相關(guān)的異常;
- IndexOutOfBoundsException:數(shù)組下角標(biāo)越界異常;
- FileNotFoundException:打開文件失敗時(shí)拋出;
- IOException:當(dāng)發(fā)生某種IO異常時(shí)拋出;
- ClassCastException:當(dāng)試圖將對(duì)象強(qiáng)制轉(zhuǎn)換為不是實(shí)例的子類時(shí),拋出此異常;
- NoSuchMethodException:無法找到某一方法時(shí),拋出;
- ArrayStoreException:試圖將錯(cuò)誤類型的對(duì)象存儲(chǔ)到一個(gè)對(duì)象數(shù)組時(shí)拋出的異常;
- NumberFormatException:當(dāng)試圖將字符串轉(zhuǎn)換成數(shù)字時(shí),失敗了,拋出;
- IllegalArgumentException 拋出的異常表明向方法傳遞了一個(gè)不合法或不正確的參數(shù)。
- ArithmeticException當(dāng)出現(xiàn)異常的運(yùn)算條件時(shí),拋出此異常。例如,一個(gè)整數(shù)“除以零”時(shí),拋出此類的一個(gè)實(shí)例。
24、hashcode是什么?有什么作用?
Java中Object有一個(gè)方法:
public native int hashcode();
(1)hashcode()方法的作用
hashcode()方法主要配合基于散列的集合一起使用,比如HashSet、HashMap、HashTable。
當(dāng)集合需要添加新的對(duì)象時(shí),先調(diào)用這個(gè)對(duì)象的hashcode()方法,得到對(duì)應(yīng)的hashcode值,實(shí)際上hashmap中會(huì)有一個(gè)table保存已經(jīng)存進(jìn)去的對(duì)象的hashcode值
如果table中沒有改hashcode值,則直接存入,如果有,就調(diào)用equals方法與新元素進(jìn)行比較,相同就不存了,不同就存入。
(2)equals和hashcode的關(guān)系
如果equals為true,hashcode一定相等;
如果equals為false,hashcode不一定不相等;
如果hashcode值相等,equals不一定相等;
如果hashcode值不等,equals一定不等;
(3)重寫equals方法時(shí),一定要重寫hashcode方法
(4)百度百科
hashcode方法返回該對(duì)象的哈希碼值。支持該方法是為哈希表提供一些優(yōu)點(diǎn),例如,java.util.Hashtable 提供的哈希表。
hashCode 的常規(guī)協(xié)定是:
在 Java 應(yīng)用程序執(zhí)行期間,在同一對(duì)象上多次調(diào)用 hashCode 方法時(shí),必須一致地返回相同的整數(shù),前提是對(duì)象上 equals 比較中所用的信息沒有被修改。從某一應(yīng)用程序的一次執(zhí)行到同一應(yīng)用程序的另
一次執(zhí)行,該整數(shù)無需保持一致。
如果根據(jù) equals(Object) 方法,兩個(gè)對(duì)象是相等的,那么在兩個(gè)對(duì)象中的每個(gè)對(duì)象上調(diào)用 hashCode 方法都必須生成相同的整數(shù)結(jié)果。
以下情況不 是必需的:如果根據(jù) equals(java.lang.Object) 方法,兩個(gè)對(duì)象不相等,那么在兩個(gè)對(duì)象中的任一對(duì)象上調(diào)用 hashCode 方法必定會(huì)生成不同的整數(shù)結(jié)果。
但是,程序員應(yīng)該知道,為不相等的對(duì)象生成不同整數(shù)結(jié)果可以提高哈希表的性能。
實(shí)際上,由 Object 類定義的 hashCode 方法確實(shí)會(huì)針對(duì)不同的對(duì)象返回不同的整數(shù)。(這一般是通過將該對(duì)象的內(nèi)部地址轉(zhuǎn)換成一個(gè)整數(shù)來實(shí)現(xiàn)的,但是 JavaTM 編程語言不需要這種實(shí)現(xiàn)技巧。)
當(dāng)equals方法被重寫時(shí),通常有必要重寫 hashCode 方法,以維護(hù) hashCode 方法的常規(guī)協(xié)定,該協(xié)定聲明相等對(duì)象必須具有相等的哈希碼。
(5)小白解釋
1.hashcode是用來查找的,如果你學(xué)過數(shù)據(jù)結(jié)構(gòu)就應(yīng)該知道,在查找和排序這一章有
例如內(nèi)存中有這樣的位置
0 1 2 3 4 5 6 7
而我有個(gè)類,這個(gè)類有個(gè)字段叫ID,我要把這個(gè)類存放在以上8個(gè)位置之一,如果不用hashcode而任意存放,那么當(dāng)查找時(shí)就需要到這八個(gè)位置里挨個(gè)去找,或者用二分法一類的算法。
但如果用hashcode那就會(huì)使效率提高很多。
我們這個(gè)類中有個(gè)字段叫ID,那么我們就定義我們的hashcode為ID%8,然后把我們的類存放在取得得余數(shù)那個(gè)位置。比如我們的ID為9,9除8的余數(shù)為1,那么我們就把該類存在1這個(gè)位置
如果ID是13,求得的余數(shù)是5,那么我們就把該類放在5這個(gè)位置。這樣,以后在查找該類時(shí)就可以通過ID除 8求余數(shù)直接找到存放的位置了。
2.但是如果兩個(gè)類有相同的hashcode怎么辦那(我們假設(shè)上面的類的ID不是唯一的),例如9除以8和17除以8的余數(shù)都是1,那么這是不是合法的,回答是:可以這樣。那么如何判斷呢?在這個(gè)時(shí)候就需
要定義 equals了。
也就是說,我們先通過 hashcode來判斷兩個(gè)類是否存放某個(gè)桶里,但這個(gè)桶里可能有很多類,那么我們就需要再通過 equals 來在這個(gè)桶里找到我們要的類。
那么。重寫了equals(),為什么還要重寫hashCode()呢?
想想,你要在一個(gè)桶里找東西,你必須先要找到這個(gè)桶啊,你不通過重寫hashcode()來找到桶,光重寫equals()有什么用啊。
25、java 中操作字符串都有哪些類?它們之間有什么區(qū)別?
(1)String
String是不可變對(duì)象,每次對(duì)String類型的改變時(shí)都會(huì)生成一個(gè)新的對(duì)象。
(2)StringBuilder
線程不安全,效率高,多用于單線程。
(3)StringBuffer
線程安全,由于加鎖的原因,效率不如StringBuilder,多用于多線程。
不頻繁的字符串操作使用String,操作頻繁的情況不建議使用String。
StringBuilder > StringBuffer > String。
26、java 中都有哪些引用類型?
(1)強(qiáng)引用
Java中默認(rèn)聲明的就是強(qiáng)引用,比如:
Object obj = new Object(); obj = null;
只要強(qiáng)引用存在,垃圾回收器將永遠(yuǎn)不會(huì)回收被引用的對(duì)象。如果想被回收,可以將對(duì)象置為null;
(2)軟引用(SoftReference)
在內(nèi)存足夠的時(shí)候,軟引用不會(huì)被回收,只有在內(nèi)存不足時(shí),系統(tǒng)才會(huì)回收軟引用對(duì)象,如果回收了軟引用對(duì)象之后仍然沒有足夠的內(nèi)存,才會(huì)跑出內(nèi)存溢出異常。
byte[] buff = new byte[1024 * 1024];
SoftReference<byte[]> sr = new SoftReference<>(buff);
(3)弱引用(WeakReference)
進(jìn)行垃圾回收時(shí),弱引用就會(huì)被回收。
(4)虛引用(PhantomReference)
(5)引用隊(duì)列(ReferenceQueue)
引用隊(duì)列可以與軟引用、弱引用、虛引用一起配合使用。
當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有引用,就會(huì)在回收對(duì)象之前,把這個(gè)引用加入到引用隊(duì)列中。
程序可以通過判斷引用隊(duì)列中是否加入了引用,來判斷被引用的對(duì)象是否將要被垃圾回收,這樣可以在對(duì)象被回收之前采取一些必要的措施。
27、在 Java 中,為什么不允許從靜態(tài)方法中訪問非靜態(tài)變量?
- 靜態(tài)變量屬于類本身,在類加載的時(shí)候就會(huì)分配內(nèi)存,可以通過類名直接訪問;
- 非靜態(tài)變量屬于類的對(duì)象,只有在類的對(duì)象產(chǎn)生時(shí),才會(huì)分配內(nèi)存,通過類的實(shí)例去訪問;
- 靜態(tài)方法也屬于類本身,但是此時(shí)沒有類的實(shí)例,內(nèi)存中沒有非靜態(tài)變量,所以無法調(diào)用。
28、說說Java Bean的命名規(guī)范
1、JavaBean 類必須是一個(gè)公共類,并將其訪問屬性設(shè)置為 public
2、JavaBean 類必須有一個(gè)空的構(gòu)造函數(shù):類中必須有一個(gè)不帶參數(shù)的公用構(gòu)造器,此構(gòu)造器也應(yīng)該通過調(diào)用各個(gè)特性的設(shè)置方法來設(shè)置特性的缺省值。
3、一個(gè)javaBean類不應(yīng)有公共實(shí)例變量,類變量都為private
4、持有值應(yīng)該通過一組存取方法(getXxx 和 setXxx)來訪問:對(duì)于每個(gè)特性,應(yīng)該有一個(gè)帶匹配公用 getter 和 setter 方法的專用實(shí)例變量。
屬性為布爾類型,可以使用 isXxx() 方法代替 getXxx() 方法。
通常屬性名是要和 包名、類名、方法名、字段名、常量名作出區(qū)別的:
首先:必須用英文,不要用漢語拼音
(1)包(package)
用于將完成不同功能的類分門別類,放在不同的目錄(包)下,包的命名規(guī)則:將公司域名反轉(zhuǎn)作為包名。
比如www.sohu.com 對(duì)于包名:每個(gè)字母都需要小寫。比如:com.sohu.test;該包下的Test類的全名是:com.sohu.Test.Java 。
如果定義類的時(shí)候沒有使用package,那么java就認(rèn)為我們所定義的類位于默認(rèn)包里面(default package)。
(2)類
首字母大寫,如果一個(gè)類由多個(gè)單詞構(gòu)成,那么每個(gè)單詞的首字母都大寫,而且中間不使用任何的連接符。盡量使用英文。如ConnectionFactory
(3)方法
首單詞全部小寫,如果一個(gè)方法由多個(gè)單詞構(gòu)成,那么從第二個(gè)單詞開始首字母大寫,不使用連接符。addPerson
(4)字段
與方法相同。如ageOfPerson
(5)常量
所有單詞的字母都是大寫,如果有多個(gè)單詞,那么使用下劃線鏈接即可。
如:public static final int AGE_OF_PERSON = 20; //通常加上static
29、Java Bean 屬性命名規(guī)范問題分析
public class User { private String busName; private String pCount; private Boolean isRunning; //正確的命名方式,駝峰式的 public String getBusName() { return busName; } public void setBusName(String busName) { this.busName = busName; } //這是什么? public String getpCount() { return pCount; } public void setpCount(String pCount) { this.pCount = pCount; } //這個(gè)也是不允許的 public Boolean getIsRunning() { return isRunning; } public void setIsRunning(Boolean isRunning) { this.isRunning = isRunning; } }
1. javabean屬性命名盡量使用常規(guī)的駝峰式命名規(guī)則
2. 屬性名第一個(gè)單詞盡量避免使用一個(gè)字母:如eBook, eMail。
3. boolean屬性名避免使用 “is” 開頭的名稱
4. 隨著jdk, eclipse, spring 等軟件版本的不斷提高, 底版本的出現(xiàn)的問題可能在高版本中解決了, 低版本原來正常的代碼可能在高版本環(huán)境下不再支持。
30、什么是 Java 的內(nèi)存模型?
在了解什么是 Java 內(nèi)存模型之前,先了解一下為什么要提出 Java 內(nèi)存模型。
之前提到過并發(fā)編程有三大問題
CPU 緩存,在多核 CPU 的情況下,帶來了可見性問題
操作系統(tǒng)對(duì)當(dāng)前執(zhí)行線程的切換,帶來了原子性問題
譯器指令重排優(yōu)化,帶來了有序性問題
為了解決并發(fā)編程的三大問題,提出了 JSR-133,新的 Java 內(nèi)存模型,JDK 5 開始使用。
簡(jiǎn)單總結(jié)下
- Java 內(nèi)存模型是 JVM 的一種規(guī)范
- 定義了共享內(nèi)存在多線程程序中讀寫操作行為的規(guī)范
- 屏蔽了各種硬件和操作系統(tǒng)的訪問差異,保證了 Java 程序在各種平臺(tái)下對(duì)內(nèi)存的訪問效果一致
- 解決并發(fā)問題采用的方式:限制處理器優(yōu)化和使用內(nèi)存屏障
- 增強(qiáng)了三個(gè)同步原語(synchronized、volatile、final)的內(nèi)存語義
- 定義了 happens-before 規(guī)則
31、在 Java 中,什么時(shí)候用重載,什么時(shí)候用重寫?
(1)重載是多態(tài)的集中體現(xiàn),在類中,要以統(tǒng)一的方式處理不同類型數(shù)據(jù)的時(shí)候,可以用重載。
(2)重寫的使用是建立在繼承關(guān)系上的,子類在繼承父類的基礎(chǔ)上,增加新的功能,可以用重寫。
(3)簡(jiǎn)單總結(jié):
- 重載是多樣性,重寫是增強(qiáng)劑;
- 目的是提高程序的多樣性和健壯性,以適配不同場(chǎng)景使用時(shí),使用重載進(jìn)行擴(kuò)展;
- 目的是在不修改原方法及源代碼的基礎(chǔ)上對(duì)方法進(jìn)行擴(kuò)展或增強(qiáng)時(shí),使用重寫;
生活例子:
你想吃一碗面,我給你提供了拉面,炒面,刀削面,擔(dān)擔(dān)面供你選擇,這是重載;
你想吃一碗面,我不但給你端來了面,還給你加了青菜,加了雞蛋,這個(gè)是重寫;
設(shè)計(jì)模式:
cglib實(shí)現(xiàn)動(dòng)態(tài)代理,核心原理用的就是方法的重寫;
詳細(xì)解答:
Java的重載(overload) 最重要的應(yīng)用場(chǎng)景就是構(gòu)造器的重載,構(gòu)造器重載后,提供多種形參形式的構(gòu)造器,可以應(yīng)對(duì)不同的業(yè)務(wù)需求,加強(qiáng)程序的健壯性和可擴(kuò)展性
比如我們最近學(xué)習(xí)的Spring源碼中的ClassPathXmlApplicationContext,它的構(gòu)造函數(shù)使用重載一共提供了10個(gè)構(gòu)造函數(shù),這樣就為業(yè)務(wù)的選擇提供了多選擇性。
在應(yīng)用到方法中時(shí),主要是為了增強(qiáng)方法的健壯性和可擴(kuò)展性,比如我們?cè)陂_發(fā)中常用的各種工具類
比如我目前工作中的短信工具類SMSUtil, 發(fā)短信的方法就會(huì)使用重載,針對(duì)不同業(yè)務(wù)場(chǎng)景下的不同形參,提供短信發(fā)送方法,這樣提高了工具類的擴(kuò)展性和健壯性。
總結(jié):重載必須要修改方法(構(gòu)造器)的形參列表,可以修改方法的返回值類型,也可以修改方法的異常信息即訪問權(quán)限;
使用范圍是在同一個(gè)類中,目的是對(duì)方法(構(gòu)造器)進(jìn)行功能擴(kuò)展,以應(yīng)對(duì)多業(yè)務(wù)場(chǎng)景的不同使用需求。提高程序的健壯性和擴(kuò)展性。
java的重寫(override) 只要用于子類對(duì)父類方法的擴(kuò)展或修改,但是在我們開發(fā)中,為了避免程序混亂,重寫一般都是為了方法的擴(kuò)展,比如在cglib方式實(shí)現(xiàn)的動(dòng)態(tài)代理中,代理類就是繼承了目標(biāo)類,對(duì)目標(biāo)類的方法進(jìn)行重寫,同時(shí)在方法前后進(jìn)行切面織入。
- 總結(jié):方法重寫時(shí),參數(shù)列表,返回值得類型是一定不能修改的,異常可以減少或者刪除,但是不能拋出新的異?;蛘吒鼜V的異常,方法的訪問權(quán)限可以降低限制,但是不能做更嚴(yán)格的限制。
(4)在里氏替換原則中,子類對(duì)父類的方法盡量不要重寫和重載。(我們可以采用final的手段強(qiáng)制來遵循)
32、舉例說明什么情況下會(huì)更傾向于使用抽象類而不是接口?
接口和抽象類都遵循”面向接口而不是實(shí)現(xiàn)編碼”設(shè)計(jì)原則,它可以增加代碼的靈活性,可以適應(yīng)不斷變化的需求。
下面有幾個(gè)點(diǎn)可以幫助你回答這個(gè)問題:在 Java 中,你只能繼承一個(gè)類,但可以實(shí)現(xiàn)多個(gè)接口。
所以一旦你繼承了一個(gè)類,你就失去了繼承其他類的機(jī)會(huì)了。
接口通常被用來表示附屬描述或行為如: Runnable 、 Clonable 、 Serializable 等等,因此當(dāng)你使用抽象類來表示行為時(shí),你的類就不能同時(shí)是 Runnable 和 Clonable
( 注:這里的意思是指如果把 Runnable 等實(shí)現(xiàn)為抽象類的情況 )
因?yàn)樵?Java 中你不能繼承兩個(gè)類,但當(dāng)你使用接口時(shí),你的類就可以同時(shí)擁有多個(gè)不同的行為。
在一些對(duì)時(shí)間要求比較高的應(yīng)用中,傾向于使用抽象類,它會(huì)比接口稍快一點(diǎn)。
如果希望把一系列行為都規(guī)范在類繼承層次內(nèi),并且可以更好地在同一個(gè)地方進(jìn)行編碼,那么抽象類是一個(gè)更好的選擇。
有時(shí),接口和抽象類可以一起使用,接口中定義函數(shù),而在抽象類中定義默認(rèn)的實(shí)現(xiàn)。
33、實(shí)例化對(duì)象有哪幾種方式
- new
- clone()
- 通過反射機(jī)制創(chuàng)建
//用 Class.forName方法獲取類,在調(diào)用類的newinstance()方法 Class<?> cls = Class.forName("com.dao.User"); User u = (User)cls.newInstance();
- 序列化反序列化
//將一個(gè)對(duì)象實(shí)例化后,進(jìn)行序列化,再反序列化,也可以獲得一個(gè)對(duì)象(遠(yuǎn)程通信的場(chǎng)景下使用) ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream("D:/data.txt")); //序列化對(duì)象 out.writeObject(user1); out.close(); //反序列化對(duì)象 ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/data.txt")); User user2 = (User) in.readObject(); System.out.println("反序列化user:" + user2); in.close();
34、byte類型127+1等于多少
byte的范圍是-128~127。
字節(jié)長(zhǎng)度為8位,最左邊的是符號(hào)位,而127的二進(jìn)制為01111111,所以執(zhí)行+1操作時(shí),01111111變?yōu)?0000000。
大家知道,計(jì)算機(jī)中存儲(chǔ)負(fù)數(shù),存的是補(bǔ)碼的興衰。左邊第一位為符號(hào)位。
那么負(fù)數(shù)的補(bǔ)碼轉(zhuǎn)換成十進(jìn)制如下:
一個(gè)數(shù)如果為正,則它的原碼、反碼、補(bǔ)碼相同;一個(gè)正數(shù)的補(bǔ)碼,將其轉(zhuǎn)化為十進(jìn)制,可以直接轉(zhuǎn)換。
已知一個(gè)負(fù)數(shù)的補(bǔ)碼,將其轉(zhuǎn)換為十進(jìn)制數(shù),步驟如下:
- 先對(duì)各位取反;
- 將其轉(zhuǎn)換為十進(jìn)制數(shù);
- 加上負(fù)號(hào),再減去1;
例如10000000,最高位是1,是負(fù)數(shù),①對(duì)各位取反得01111111,轉(zhuǎn)換為十進(jìn)制就是127,加上負(fù)號(hào)得-127,再減去1得-128;
35、Java 容器都有哪些?
(1)Collection
① set
HashSet、TreeSet
② list
ArrayList、LinkedList、Vector
(2)Map
HashMap、HashTable、TreeMap
36、Collection 和 Collections 有什么區(qū)別?
(1)Collection是最基本的集合接口,Collection派生了兩個(gè)子接口list和set,分別定義了兩種不同的存儲(chǔ)方式。
(2)Collections是一個(gè)包裝類,它包含各種有關(guān)集合操作的靜態(tài)方法(對(duì)集合的搜索、排序、線程安全化等)。
此類不能實(shí)例化,就像一個(gè)工具類,服務(wù)于Collection框架。
37、list與Set區(qū)別
(1)List簡(jiǎn)介
實(shí)際上有兩種List:
一種是基本的ArrayList,其優(yōu)點(diǎn)在于隨機(jī)訪問元素,另一種是LinkedList,它并不是為快速隨機(jī)訪問設(shè)計(jì)的,而是快速的插入或刪除。
ArrayList:由數(shù)組實(shí)現(xiàn)的List。允許對(duì)元素進(jìn)行快速隨機(jī)訪問,但是向List中間插入與移除元素的速度很慢。
LinkedList :對(duì)順序訪問進(jìn)行了優(yōu)化,向List中間插入與刪除的開銷并不大。隨機(jī)訪問則相對(duì)較慢。
還具有下列方 法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 這些方法 (沒有在任何接口或基類中定義過)
使得LinkedList可以當(dāng)作堆棧、隊(duì)列和雙向隊(duì)列使用。
(2)Set簡(jiǎn)介
Set具有與Collection完全一樣的接口,因此沒有任何額外的功能。
實(shí)際上Set就是Collection,只是行為不同。
這是繼承與多態(tài)思想的典型應(yīng)用:表現(xiàn)不同的行為。
Set不保存重復(fù)的元素(至于如何判斷元素相同則較為負(fù)責(zé))
Set : 存入Set的每個(gè)元素都必須是唯一的,因?yàn)镾et不保存重復(fù)元素。
加入Set的元素必須定義equals()方法以確保對(duì)象的唯一性。
Set與Collection有完全一樣的接口。Set接口不保證維護(hù)元素的次序。
HashSet:為快速查找設(shè)計(jì)的Set。存入HashSet的對(duì)象必須定義hashCode()。
TreeSet: 保存次序的Set, 底層為樹結(jié)構(gòu)。使用它可以從Set中提取有序的序列。
(3)list與Set區(qū)別
① List,Set都是繼承自Collection接口
② List特點(diǎn):元素有放入順序,元素可重復(fù)
Set特點(diǎn):元素?zé)o放入順序,元素不可重復(fù),重復(fù)元素會(huì)覆蓋掉,(元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實(shí)是固定的,加入Set 的Object必須定義equals()方法 ,另外list支持for循環(huán),也就是通過下標(biāo)來遍歷,也可以用迭代器,但是set只能用迭代,因?yàn)樗麩o序,無法用下標(biāo)來取得想要的值。)
③ Set和List對(duì)比:
Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會(huì)引起元素位置改變。
List:和數(shù)組類似,List可以動(dòng)態(tài)增長(zhǎng),查找元素效率高,插入刪除元素效率低,因?yàn)闀?huì)引起其他元素位置改變。
38、HashMap 和 Hashtable 有什么區(qū)別?
- HashMap是線程不安全的,HashTable是線程安全的;
- HashMap中允許鍵和值為null,HashTable不允許;
- HashMap的默認(rèn)容器是16,為2倍擴(kuò)容,HashTable默認(rèn)是11,為2倍+1擴(kuò)容;
39、說一下 HashMap 的實(shí)現(xiàn)原理?
(1)簡(jiǎn)介
HashMap基于map接口,元素以鍵值對(duì)方式存儲(chǔ),允許有null值,HashMap是線程不安全的。
(2)基本屬性
初始化大小,默認(rèn)16,2倍擴(kuò)容;
負(fù)載因子0.75;
初始化的默認(rèn)數(shù)組;
size
threshold。判斷是否需要調(diào)整hashmap容量
(3)HashMap的存儲(chǔ)結(jié)構(gòu)
JDK1.7中采用數(shù)組+鏈表的存儲(chǔ)形式。
HashMap采取Entry數(shù)組來存儲(chǔ)key-value,每一個(gè)鍵值對(duì)組成了一個(gè)Entry實(shí)體,Entry類時(shí)機(jī)上是一個(gè)單向的鏈表結(jié)構(gòu)
它具有next指針,指向下一個(gè)Entry實(shí)體,以此來解決Hash沖突的問題。
HashMap實(shí)現(xiàn)一個(gè)內(nèi)部類Entry,重要的屬性有hash、key、value、next。
JDK1.8中采用數(shù)據(jù)+鏈表+紅黑樹的存儲(chǔ)形式。當(dāng)鏈表長(zhǎng)度超過閾值(8)時(shí),將鏈表轉(zhuǎn)換為紅黑樹。在性能上進(jìn)一步得到提升。
40、set有哪些實(shí)現(xiàn)類?
(1)HashSet
HashSet是set接口的實(shí)現(xiàn)類,set下面最主要的實(shí)現(xiàn)類就是HashSet(也就是用的最多的),此外還有LinkedHashSet和TreeSet。
HashSet是無序的、不可重復(fù)的。通過對(duì)象的hashCode和equals方法保證對(duì)象的唯一性。
HashSet內(nèi)部的存儲(chǔ)結(jié)構(gòu)是哈希表,是線程不安全的。
(2)TreeSet
TreeSet對(duì)元素進(jìn)行排序的方式:
元素自身具備比較功能,需要實(shí)現(xiàn)Comparable接口,并覆蓋compareTo方法。
元素自身不具備比較功能,需要實(shí)現(xiàn)Comparator接口,并覆蓋compare方法。
(3)LinkedHashSet
LinkedHashSet是一種有序的Set集合,即其元素的存入和輸出的順序是相同的。
41、說一下 HashSet 的實(shí)現(xiàn)原理?
HashSet實(shí)際上是一個(gè)HashMap實(shí)例,數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)都是數(shù)組+鏈表。
HashSet是基于HashMap實(shí)現(xiàn)的,HashSet中的元素都存放在HashMap的key上面,而value都是一個(gè)統(tǒng)一的對(duì)象PRESENT。
private static final Object PRESENT = new Object();
HashSet中add方法調(diào)用的是底層HashMap中的put方法,put方法要判斷插入值是否存在
而HashSet的add方法,首先判斷元素是否存在,如果存在則插入,如果不存在則不插入,這樣就保證了HashSet中不存在重復(fù)值。
通過對(duì)象的hashCode和equals方法保證對(duì)象的唯一性。
42、ArrayList 和 LinkedList 的區(qū)別是什么?
ArrayList是動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),查找和遍歷的效率較高;
LinkedList 是雙向鏈表的數(shù)據(jù)結(jié)構(gòu),增加和刪除的效率較高;
43、如何實(shí)現(xiàn)數(shù)組和 List 之間的轉(zhuǎn)換?
String[] arr = {"zs","ls","ww"}; List<String> list = Arrays.asList(arr); System.out.println(list); ArrayList<String> list1 = new ArrayList<String>(); list1.add("張三"); list1.add("李四"); list1.add("王五"); String[] arr1 = list1.toArray(new String[list1.size()]); System.out.println(arr1); for(int i = 0; i < arr1.length; i++){ System.out.println(arr1[i]); }
44、在 Queue 中 poll()和 remove()有什么區(qū)別?
(1)offer()和add()區(qū)別:
增加新項(xiàng)時(shí),如果隊(duì)列滿了,add會(huì)拋出異常,offer返回false。
(2)poll()和remove()區(qū)別:
poll()和remove()都是從隊(duì)列中刪除第一個(gè)元素,remove拋出異常,poll返回null。
(3)peek()和element()區(qū)別:
peek()和element()用于查詢隊(duì)列頭部元素,為空時(shí)element拋出異常,peek返回null。
45、哪些集合類是線程安全的
Vector:就比Arraylist多了個(gè)同步化機(jī)制(線程安全)。
Stack:棧,也是線程安全的,繼承于Vector。
Hashtable:就比Hashmap多了個(gè)線程安全。
ConcurrentHashMap:是一種高效但是線程安全的集合。
46、迭代器 Iterator 是什么?
為了方便的處理集合中的元素,Java中出現(xiàn)了一個(gè)對(duì)象,該對(duì)象提供了一些方法專門處理集合中的元素.例如刪除和獲取集合中的元素.該對(duì)象就叫做迭代器(Iterator)。
47、Iterator 怎么使用?有什么特點(diǎn)?
Iterator 接口源碼中的方法:
- java.lang.Iterable 接口被 java.util.Collection 接口繼承,java.util.Collection 接口的 iterator() 方法返回一個(gè) Iterator 對(duì)象
- next() 方法獲得集合中的下一個(gè)元素
- hasNext() 檢查集合中是否還有元素
- remove() 方法將迭代器新返回的元素刪除
48、Iterator 和 ListIterator 有什么區(qū)別?
(1)ListIterator 繼承 Iterator
(2)ListIterator 比 Iterator多方法
- add(E e) 將指定的元素插入列表,插入位置為迭代器當(dāng)前位置之前
- set(E e) 迭代器返回的最后一個(gè)元素替換參數(shù)
- ehasPrevious() 迭代器當(dāng)前位置,反向遍歷集合是否含有元素
- previous() 迭代器當(dāng)前位置,反向遍歷集合,下一個(gè)元素
- previousIndex() 迭代器當(dāng)前位置,反向遍歷集合,返回下一個(gè)元素的下標(biāo)
- nextIndex() 迭代器當(dāng)前位置,返回下一個(gè)元素的下標(biāo)
(3)使用范圍不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子類
- ListIterator 有 add 方法,可以向 List 中添加對(duì)象;
- Iterator 不能ListIterator 有 hasPrevious() 和 previous() 方法,可以實(shí)現(xiàn)逆向遍歷;
- Iterator不可以ListIterator 有 nextIndex() 和previousIndex() 方法,可定位當(dāng)前索引的位置;
- Iterator不可以ListIterator 有 set()方法,可以實(shí)現(xiàn)對(duì) List 的修改;Iterator 僅能遍歷,不能修改。
49、怎么確保一個(gè)集合不能被修改?
我們很容易想到用final關(guān)鍵字進(jìn)行修飾,我們都知道
final關(guān)鍵字可以修飾類,方法,成員變量,final修飾的類不能被繼承,final修飾的方法不能被重寫,final修飾的成員變量必須初始化值
如果這個(gè)成員變量是基本數(shù)據(jù)類型,表示這個(gè)變量的值是不可改變的
如果說這個(gè)成員變量是引用類型,則表示這個(gè)引用的地址值是不能改變的,但是這個(gè)引用所指向的對(duì)象里面的內(nèi)容還是可以改變的。
那么,我們?cè)趺创_保一個(gè)集合不能被修改?首先我們要清楚,集合(map,set,list…)都是引用類型,所以我們?nèi)绻胒inal修飾的話,集合里面的內(nèi)容還是可以修改的。
我們可以做一個(gè)實(shí)驗(yàn):
可以看到:我們用final關(guān)鍵字定義了一個(gè)map集合,這時(shí)候我們往集合里面?zhèn)髦?,第一個(gè)鍵值對(duì)1,1;我們?cè)傩薷暮?,可以把鍵為1的值改為100,說明我們是可以修改map集合的值的。
那我們應(yīng)該怎么做才能確保集合不被修改呢?
我們可以采用Collections包下的unmodifiableMap方法,通過這個(gè)方法返回的map,是不可以修改的。
他會(huì)報(bào) java.lang.UnsupportedOperationException錯(cuò)。
同理:Collections包也提供了對(duì)list和set集合的方法。
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)
50、隊(duì)列和棧是什么?有什么區(qū)別?
(1)隊(duì)列先進(jìn)先出,棧先進(jìn)后出。
(2)遍歷數(shù)據(jù)速度不同。
棧只能從頭部取數(shù)據(jù) 也就最先放入的需要遍歷整個(gè)棧最后才能取出來,而且在遍歷數(shù)據(jù)的時(shí)候還得為數(shù)據(jù)開辟臨時(shí)空間,保持?jǐn)?shù)據(jù)在遍歷前的一致性;
隊(duì)列則不同,他基于地址指針進(jìn)行遍歷,而且可以從頭或尾部開始遍歷,但不能同時(shí)遍歷,無需開辟臨時(shí)空間,因?yàn)樵诒闅v的過程中不影像數(shù)據(jù)結(jié)構(gòu),速度要快的多。
51、Java8開始ConcurrentHashMap,為什么舍棄分段鎖?
ConcurrentHashMap的原理是引用了內(nèi)部的 Segment ( ReentrantLock ) 分段鎖,保證在操作不同段 map 的時(shí)候, 可以并發(fā)執(zhí)行, 操作同段 map 的時(shí)候,進(jìn)行鎖的競(jìng)爭(zhēng)和等待。
從而達(dá)到線程安全, 且效率大于 synchronized。
但是在 Java 8 之后, JDK 卻棄用了這個(gè)策略,重新使用了 synchronized+CAS。
棄用原因
通過 JDK 的源碼和官方文檔看來, 他們認(rèn)為的棄用分段鎖的原因由以下幾點(diǎn):
加入多個(gè)分段鎖浪費(fèi)內(nèi)存空間。
生產(chǎn)環(huán)境中, map 在放入時(shí)競(jìng)爭(zhēng)同一個(gè)鎖的概率非常小,分段鎖反而會(huì)造成更新等操作的長(zhǎng)時(shí)間等待。
為了提高 GC 的效率
新的同步方案
既然棄用了分段鎖, 那么一定由新的線程安全方案, 我們來看看源碼是怎么解決線程安全的呢?(源碼保留了segment 代碼, 但并沒有使用)。
52、ConcurrentHashMap(JDK1.8)為什么要使用synchronized而不是如ReentranLock這樣的可重入鎖?
我想從下面幾個(gè)角度討論這個(gè)問題:
(1)鎖的粒度
首先鎖的粒度并沒有變粗,甚至變得更細(xì)了。每當(dāng)擴(kuò)容一次,ConcurrentHashMap的并發(fā)度就擴(kuò)大一倍。
(2)Hash沖突
JDK1.7中,ConcurrentHashMap從過二次hash的方式(Segment -> HashEntry)能夠快速的找到查找的元素。
在1.8中通過鏈表加紅黑樹的形式彌補(bǔ)了put、get時(shí)的性能差距。
JDK1.8中,在ConcurrentHashmap進(jìn)行擴(kuò)容時(shí),其他線程可以通過檢測(cè)數(shù)組中的節(jié)點(diǎn)決定是否對(duì)這條鏈表(紅黑樹)進(jìn)行擴(kuò)容,減小了擴(kuò)容的粒度,提高了擴(kuò)容的效率。
下面是我對(duì)面試中的那個(gè)問題的一下看法。
為什么是synchronized,而不是ReentranLock
(1)減少內(nèi)存開銷
假設(shè)使用可重入鎖來獲得同步支持,那么每個(gè)節(jié)點(diǎn)都需要通過繼承AQS來獲得同步支持。
但并不是每個(gè)節(jié)點(diǎn)都需要獲得同步支持的,只有鏈表的頭節(jié)點(diǎn)(紅黑樹的根節(jié)點(diǎn))需要同步,這無疑帶來了巨大內(nèi)存浪費(fèi)。
(2)獲得JVM的支持
可重入鎖畢竟是API這個(gè)級(jí)別的,后續(xù)的性能優(yōu)化空間很小。
synchronized則是JVM直接支持的,JVM能夠在運(yùn)行時(shí)作出相應(yīng)的優(yōu)化措施:鎖粗化、鎖消除、鎖自旋等等。
這就使得synchronized能夠隨著JDK版本的升級(jí)而不改動(dòng)代碼的前提下獲得性能上的提升。
到此這篇關(guān)于Java經(jīng)典面試題最全匯總208道(一)的文章就介紹到這了,更多相關(guān)Java面試題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題
這篇文章主要介紹了解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06SpringBoot實(shí)現(xiàn)熱部署Community的示例代碼
本文主要介紹了SpringBoot實(shí)現(xiàn)熱部署Community的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06使用MUI框架構(gòu)建App請(qǐng)求http接口實(shí)例代碼
下面小編就為大家分享一篇使用MUI框架構(gòu)建App請(qǐng)求http接口實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01詳解SpringCloud是如何動(dòng)態(tài)更新配置的
spring cloud在config配置管理的基礎(chǔ)上,提供了consul config的配置管理和動(dòng)態(tài)監(jiān)聽,那么這里面到底是怎樣實(shí)現(xiàn)的,本文將為你揭秘,感興趣的小伙伴可以跟著小伙伴一起來學(xué)習(xí)2023-06-06如何使用SpringBootCondition更自由地定義條件化配置
這篇文章主要介紹了如何使用SpringBootCondition更自由地定義條件化配置,幫助大家更好的理解和學(xué)習(xí)使用springboot框架,感興趣的朋友可以了解下2021-04-04spring-boot報(bào)錯(cuò)java: 程序包javax.servlet.http不存在
當(dāng)springboot項(xiàng)目從2.7.x的升級(jí)到3.0.x的時(shí)候,會(huì)遇到一個(gè)問題java: 程序包javax.servlet.http不存在,下面就來具體介紹一下,感興趣的可以了解一下2024-08-08