亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java經(jīng)典面試題最全匯總208道(一)

 更新時(shí)間:2023年01月17日 15:55:23   作者:哪?吒  
這篇文章主要介紹了Java經(jīng)典面試題最全匯總208道(一),本文章內(nèi)容詳細(xì),該模塊分為了六個(gè)部分,本次為第一部分,需要的朋友可以參考下

前言 

短時(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ū)別是什么?

  1. 對(duì)于基本類型,==比較的是值;
  2. 對(duì)于引用類型,==比較的是地址;
  3. equals不能用于基本類型的比較;
  4. 如果沒有重寫equals,equals就相當(dāng)于==;
  5. 如果重寫了equals方法,equals比較的是對(duì)象的內(nèi)容;

3、final 在 java 中有什么作用?

(1)用來修飾一個(gè)引用

  1.  如果引用為基本數(shù)據(jù)類型,則該引用為常量,該值無法修改;
  2.  如果引用為引用數(shù)據(jù)類型,比如對(duì)象、數(shù)組,則該對(duì)象、數(shù)組本身可以修改,但指向該對(duì)象或數(shù)組的地址的引用不能修改。
  3.  如果引用時(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、常見的異常類有哪些?

  1. NullPointerException:空指針異常;
  2. SQLException:數(shù)據(jù)庫相關(guān)的異常;
  3. IndexOutOfBoundsException:數(shù)組下角標(biāo)越界異常;
  4. FileNotFoundException:打開文件失敗時(shí)拋出;
  5. IOException:當(dāng)發(fā)生某種IO異常時(shí)拋出;
  6. ClassCastException:當(dāng)試圖將對(duì)象強(qiáng)制轉(zhuǎn)換為不是實(shí)例的子類時(shí),拋出此異常;
  7. NoSuchMethodException:無法找到某一方法時(shí),拋出;
  8. ArrayStoreException:試圖將錯(cuò)誤類型的對(duì)象存儲(chǔ)到一個(gè)對(duì)象數(shù)組時(shí)拋出的異常;
  9. NumberFormatException:當(dāng)試圖將字符串轉(zhuǎn)換成數(shù)字時(shí),失敗了,拋出;
  10. IllegalArgumentException 拋出的異常表明向方法傳遞了一個(gè)不合法或不正確的參數(shù)。
  11. 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)變量?

  1. 靜態(tài)變量屬于類本身,在類加載的時(shí)候就會(huì)分配內(nèi)存,可以通過類名直接訪問;
  2. 非靜態(tài)變量屬于類的對(duì)象,只有在類的對(duì)象產(chǎn)生時(shí),才會(huì)分配內(nèi)存,通過類的實(shí)例去訪問;
  3. 靜態(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ù),步驟如下:

  1. 先對(duì)各位取反;
  2. 將其轉(zhuǎn)換為十進(jìn)制數(shù);
  3. 加上負(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ū)別?

  1. HashMap是線程不安全的,HashTable是線程安全的;
  2. HashMap中允許鍵和值為null,HashTable不允許;
  3. 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 接口源碼中的方法:

  1. java.lang.Iterable 接口被 java.util.Collection 接口繼承,java.util.Collection 接口的 iterator() 方法返回一個(gè) Iterator 對(duì)象
  2. next() 方法獲得集合中的下一個(gè)元素
  3. hasNext() 檢查集合中是否還有元素
  4. remove() 方法將迭代器新返回的元素刪除

48、Iterator 和 ListIterator 有什么區(qū)別?

(1)ListIterator 繼承 Iterator

(2)ListIterator 比 Iterator多方法

  1. add(E e)  將指定的元素插入列表,插入位置為迭代器當(dāng)前位置之前
  2. set(E e)  迭代器返回的最后一個(gè)元素替換參數(shù)
  3. ehasPrevious()  迭代器當(dāng)前位置,反向遍歷集合是否含有元素
  4. previous()  迭代器當(dāng)前位置,反向遍歷集合,下一個(gè)元素
  5. previousIndex()  迭代器當(dāng)前位置,反向遍歷集合,返回下一個(gè)元素的下標(biāo)
  6. nextIndex()  迭代器當(dāng)前位置,返回下一個(gè)元素的下標(biāo)

(3)使用范圍不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子類

  1. ListIterator 有 add 方法,可以向 List 中添加對(duì)象;
  2. Iterator 不能ListIterator 有 hasPrevious() 和 previous() 方法,可以實(shí)現(xiàn)逆向遍歷;
  3. Iterator不可以ListIterator 有 nextIndex() 和previousIndex() 方法,可定位當(dāng)前索引的位置;
  4. 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ù)返回亂碼問題

    這篇文章主要介紹了解決OkHttp接收gzip壓縮數(shù)據(jù)返回亂碼問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 教你怎么用Java獲取國(guó)家法定節(jié)假日

    教你怎么用Java獲取國(guó)家法定節(jié)假日

    這篇文章主要介紹了教你怎么用Java獲取國(guó)家法定節(jié)假日,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • SpringBoot實(shí)現(xiàn)熱部署Community的示例代碼

    SpringBoot實(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í)例代碼

    下面小編就為大家分享一篇使用MUI框架構(gòu)建App請(qǐng)求http接口實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • java如何消除太多的if else判斷示例代碼

    java如何消除太多的if else判斷示例代碼

    這篇文章主要介紹了java如何消除太多的if else判斷,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • 詳解Java如何跨平臺(tái)獲取MAC地址

    詳解Java如何跨平臺(tái)獲取MAC地址

    有時(shí)我們因?yàn)檐浖跈?quán)或者其它需要獲取主機(jī)唯一標(biāo)識(shí)而需要獲取用戶主機(jī)的MAC地址,而本文則將介紹如何通過Java來實(shí)現(xiàn)跨平臺(tái)獲取MAC地址的兩種方法,需要的朋友可以參考下
    2021-06-06
  • CentOS 7快速安裝jdk

    CentOS 7快速安裝jdk

    這篇文章主要為大家詳細(xì)介紹了CentOS 7快速安裝jdk的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • 詳解SpringCloud是如何動(dòng)態(tài)更新配置的

    詳解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更自由地定義條件化配置

    這篇文章主要介紹了如何使用SpringBootCondition更自由地定義條件化配置,幫助大家更好的理解和學(xué)習(xí)使用springboot框架,感興趣的朋友可以了解下
    2021-04-04
  • spring-boot報(bào)錯(cuò)java: 程序包javax.servlet.http不存在

    spring-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

最新評(píng)論