java-se部分

一.JavaSE 部分

基礎(chǔ)部分

Java中基本數(shù)據(jù)類型有哪些?

byte:8位,最大存儲數(shù)據(jù)量是255,存放的數(shù)據(jù)范圍是-128~127之間。

short:16位,

int:32位,最大數(shù)據(jù)存儲容量是2的32次方減1,數(shù)據(jù)范圍是負的2的31次方到正的2的31次方減1。

long:64位,最大數(shù)據(jù)存儲容量是2的64次方減1,數(shù)據(jù)范圍為負的2的63次方到正的2的63次方減1。

float:32位,

double:64位,

boolean:只有true和false兩個取值。

char:16位,存儲Unicode碼,用單引號賦值。

Integer 和 int的區(qū)別

int是基本數(shù)據(jù)類型,變量中直接存放數(shù)值,變量初始化時值是0

Integer是引用數(shù)據(jù)類型,變量中存放的是該對象的引用,變量初始化時值時null

Integer是int類型的包裝類,將int封裝成Integer,符合java面向?qū)ο蟮奶匦?,可以使用各種方法比如和其他數(shù)據(jù)類型間的轉(zhuǎn)換

Integer和int的深入對比:

  1. 兩個通過new生成的Integer對象,由于在堆中地址不同,所以永遠不相等

  2. int和Integer比較時,只要數(shù)值相等,結(jié)果就相等,因為包裝類和基本數(shù)據(jù)類型比較時,會自動拆箱,將Integer轉(zhuǎn)化為int

  3. 通過new生成的Integer對象和非通過new生成的Integer對象相比較時,由于前者存放在堆中,后者存放在Java常量池中,所以永遠不相等

  4. 兩個非通過new生成的Integer對象比較時,如果兩個變量的數(shù)值相等且在-128到127之間,結(jié)果就相等。這是因為給Integer對象賦一個int值,java在編譯時,會自動調(diào)用靜態(tài)方法valueOf(),根據(jù)java api中對Integer類型的valueOf的定義,對于-128到127之間的整數(shù),會進行緩存,如果下次再賦相同的值會直接從緩存中取,即享元模式

String和StringBuilder和StringBuffer區(qū)別

三者底層都是char[]存儲數(shù)據(jù),JDK1.9之后使用的是byte[] ,因為往往我們存儲都是短字符串,使用byte[]這樣更節(jié)約空間。

由于String底層的char[]有final修飾,因此每次對String的操作都會在內(nèi)存中開辟空間,生成新的對象,所以String不可變

StringBuilder和StringBuffer是可變字符串,沒有final修飾,適合字符串拼接,另外StringBuffer是線程安全的,方法有synchronized修飾,但是性能較低,StringBuilder是線程不安全的,方法沒有synchronized修飾,性能較高

String a = "A" 和 String a = new String("A") 創(chuàng)建字符串的區(qū)別

String c = "A" 首先去常量池找 “A”,如果有,會把a指向這個對象的地址 ,如果沒有則在棧中創(chuàng)建三個char型的值'A',堆中創(chuàng)建一個String對象object,值為"A",接著object會被存放進字符串常量池中,最后將a指向這個對象的的地址

new String("A") : 如果常量池中么有“A”就會走上面相同的流程先創(chuàng)建“A”,然后在堆中創(chuàng)建一個String對象,它的值共享棧中已有的char值“A”。

下面代碼創(chuàng)建了幾個對象

  • String s = "a" +"b" + "c" + "d";這條語句創(chuàng)建了幾個對象?

創(chuàng)建了一個對象,因為相對于字符串常量相加的表達式,編譯器會在編譯期間進行優(yōu)化,直接將其編譯成常量相加的結(jié)果。

  • String s; 創(chuàng)建幾個對象?
    沒有創(chuàng)建對象。

  • String a = "abc"; String b = "abc"; 創(chuàng)建了幾個對象

    創(chuàng)建了一個對象,只是在第一條語句中創(chuàng)建了一個對象,a和b都指向相同的對象"abc",引用不是對象

== 和 equals 的區(qū)別是什么

==比較對象比較的是地址,對于Object對象中的equals 方法使用的也是 == ,比較的是對象的地址,默認情況下使用對象的equals比較Object中的equals方法,也就是比較地址,如果要實現(xiàn)自己的比較方式需要復(fù)寫equals 方法。

對于包裝類比如:Integer都是復(fù)寫過equals方法,比較的是int 值。

final 和 finally 和 finalize 的區(qū)別

當(dāng)用final修飾類的時,表明該類不能被其他類所繼承。當(dāng)我們需要讓一個類永遠不被繼承,此時就可以用final修飾

finally作為異常處理的一部分,它只能用在try/catch語句中,并且附帶一個語句塊,表示這段語句最終一定會被執(zhí)行(不管有沒有拋出異常),經(jīng)常被用在需要釋放資源的情況下

finalize()是在java.lang.Object里定義的,也就是說每一個對象都有這么個方法。這個方法在gc啟動,該對象被回收的時候被調(diào)用。其實gc可以回收大部分的對象(凡是new出來的對象,gc都能搞定,一般情況下我們又不會用new以外的方式去創(chuàng)建對象),所以一般是不需要程序員去實現(xiàn)finalize的。

JDK 和 JRE 有什么區(qū)別?

JRE(Java Runtime Enviroment) :是Java的運行環(huán)境,JRE是運行Java程序所必須環(huán)境的集合,包含JVM標準實現(xiàn)及 Java核心類庫

JDK(Java Development Kit) :是Java開發(fā)工具包,它提供了Java的開發(fā)環(huán)境(提供了編譯器javac等工具,用于將java文件編譯為class文件)和運行環(huán)境(提 供了JVM和Runtime輔助包,用于解析class文件使其得到運行)。JDK是整個Java的核心,包括了Java運行環(huán)境(JRE),一堆Java工具tools.jar和Java標準類庫 (rt.jar)。

面向?qū)ο笏拇筇匦?/h4>

抽象 : 是將一類對象的共同特征總結(jié)出來構(gòu)造類的過程,包括數(shù)據(jù)抽象和行為抽象兩方面,抽象只關(guān)注對象的哪些屬性和行為,并不關(guān)注這此行為的細節(jié)是什么 - 舉例:定義一個persion類,了就是對這種事物的抽象

封裝:對數(shù)據(jù)的訪問只能通過已定義的接口,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口,比如在Java中,把不需要暴露的內(nèi)容和實現(xiàn)細節(jié)隱藏起來,或者private修飾,然后提供專門的訪問方法,如JavaBean。 - 生活舉例:電腦主機就是把主板等封裝到機殼,提供USB接口,網(wǎng)卡接口,電源接口等。 JavaBean就是一種封裝。

繼承:新類(子類,派生類)繼承了原始類的特性,子類可以從它的父類哪里繼承方法和實例變量,并且類可以修改或增加新的方法使之更適合特殊的需要。

多態(tài):多態(tài)是指允許不同類的對象對同一消息做出響應(yīng)。對象的多種形態(tài),當(dāng)編譯時類型和運行時類型不一樣,就是多態(tài),意義在于屏蔽子類差異

方法覆蓋和重載

方法的覆蓋是子類和父類之間的關(guān)系,方法的重載是同一個類中方法之間的關(guān)系。
覆蓋只能由一個方法,或只能由一對方法產(chǎn)生關(guān)系;方法的重載是多個方法之間的關(guān)系。
覆蓋要求參數(shù)列表相同;重載要求參數(shù)列表不同。

普通類和抽象類

抽象類不能被實例化, 需要通過子類實例化
抽象類可以有構(gòu)造函數(shù),被繼承時子類必須繼承父類一個構(gòu)造方法,抽象方法不能被聲明為靜態(tài)。
抽象方法只需申明,而無需實現(xiàn),抽象類中可以允許普通方法有主體
含有抽象方法的類必須申明為抽象類
抽象的子類必須實現(xiàn)抽象類中所有抽象方法,否則這個子類也是抽象類

接口和抽象類

定義接口使用interface,定義抽象類使用abstract class

接口由全局常量,抽象方法,(java8后:靜態(tài)方法,默認方法)

抽象類由構(gòu)造方法,抽象方法,普通方法

接口和類是實現(xiàn)關(guān)系,抽象類和類是繼承關(guān)系


IO流

你知道BIO,NIO,AIO么?講一下你的理解

BIO (Blocking I/O):同步阻塞I/O 模式,以流的方式處理數(shù)據(jù),數(shù)據(jù)的讀取寫入必須阻塞在一個線程內(nèi)等待其完成。適用于連接數(shù)目比較小且固定的架構(gòu)

NIO (New I/O):同時支持阻塞與非阻塞模式,以塊的方式處理數(shù)據(jù),適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu),比如聊天器

AIO ( Asynchronous I/O):異步非阻塞I/O 模型,適用于連接數(shù)目多且連接比較長(重操作)的架構(gòu)

java 中四大基礎(chǔ)流

InputStream : 輸入字節(jié)流, 也就是說它既屬于輸入流, 也屬于字節(jié)流 ,

OutputStream: 輸出字節(jié)流, 既屬于輸出流, 也屬于字節(jié)流

Reader: 輸入字符流, 既屬于輸入流, 又屬于字符流

Writer: 輸出字符流, 既屬于輸出流, 又屬于字符流

讀文本用什么流,讀圖片用什么流

文本用字符輸入流,讀圖片用字節(jié)輸入流

字符流和字節(jié)流有什么區(qū)別

字符流適用于讀文本,字節(jié)流適用于讀圖片,視頻,文件等。

字節(jié)流操作的基本單元為字節(jié);字符流操作的基本單元為Unicode碼元。

字節(jié)流默認不使用緩沖區(qū);字符流使用緩沖區(qū)。

字節(jié)流通常用于處理二進制數(shù)據(jù),實際上它可以處理任意類型的數(shù)據(jù),但它不支持直接寫入或讀取Unicode碼元;字符流通常處理文本數(shù)據(jù),它支持寫入及讀取Unicode碼元

BufferedInputStream 用到什么設(shè)計模式

主要運用了倆個設(shè)計模式,適配器和裝飾者模式

帶緩沖區(qū)的流

BufferedInputStream 帶緩沖區(qū)的字節(jié)輸入

BufferedOutputStream 帶緩沖區(qū)的輸出流

BufferedReader : 帶緩沖區(qū)的字符輸入流

BufferedWriter : 帶緩沖區(qū)的字符輸出流


集合篇

說一下Java中的集合體系

Collection接口

List:

  • ArrayList:底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,查詢性能高,增刪性能低

  • Vector:底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,查詢性能高,增刪性能低

  • LinkedList:底層數(shù)據(jù)結(jié)構(gòu)是雙向鏈表,查詢性能低,增刪性能高

Set:

  • HashSet:無序不重復(fù)的,使用HashMap的key存儲元素,判斷重復(fù)依據(jù)是hashCode()和equals()

  • TreeSet:有序不重復(fù)的,底層使用TreeMap的key存儲元素,排序方式分為自然排序,比較器排序

Map接口

  • HashMap:key的值沒有順序,線程不安
  • TreeMap:key的值可以自然排序,線程不安全
  • HashTable:它的key和value都不允許為null,線程安全
  • Properties:它的key和value都是String類型的,線程安全

HashMap和HashTable的區(qū)別

HashMap和HashTable都是實現(xiàn)了Map接口的集合框架,他們的區(qū)別

  • HashTable是線程安全的,它的實現(xiàn)方法都加了synchronized關(guān)鍵字,因此它的性能較低

  • HashMap是線程不安全的,它實現(xiàn)方法沒有加synchronized,因此它的性能較高

  • HashMap的key和value都允許為null,HashTable中的key和value都不能為null,如果不考慮線程安全,建議使用HashMap,如果需要考慮線程安全的高并發(fā)實現(xiàn),建議使用ConcurrentHashMap

ArrayList和LinkedList區(qū)別

都屬于線性結(jié)構(gòu),ArrayList是基于數(shù)組實現(xiàn)的,開辟的內(nèi)存空間要求聯(lián)系,可以根據(jù)索引隨機訪問元素性能高,但是插入和刪除元素性能差,因為這會涉及到移位操作

LinkedList是基于雙鏈表實現(xiàn)的,開配的內(nèi)存空間不要求連續(xù),因此不支持索引,查找元素需要從頭查找,因此性能差,但是添加刪除只需要改變指針指向即可,性能高. LinkedList會增加內(nèi)存碎片化,增加內(nèi)存管理難度

根據(jù)實際需要,如果項目中使用查找較多,使用ArrayList,如果使用增刪較多,請使用LinkedList

ArrayList和Vector區(qū)別

ArrayList是線程不安全的,Vector相反是線程安全的,方法加了同步鎖,線程安全但是性能差,ArrayList底層數(shù)組容量不足時,會自動擴容0.5倍,Vector會自動擴容1倍

一個User的List集合,如何實現(xiàn)根據(jù)年齡排序

第一種方式,讓User類實現(xiàn)Comparable接口,覆寫compareTo方法,方法中自定義根據(jù)年齡比較的算法

第二種方式,調(diào)用Collections.sort方法,傳入一個比較器,覆寫compare方法,方法中自定義根據(jù)年齡比較的算法

HashMap底層用到了那些數(shù)據(jù)結(jié)構(gòu)?

JDK1.7及其之前:數(shù)組,鏈表 ; JDK1.8開始:數(shù)組,鏈表,紅黑樹

什么是Hash沖突

哈希沖突,也叫哈希碰撞,指的是兩個不同的值,計算出了相同的hash,也就是兩個不同的數(shù)據(jù)計算出同一個下標,通常解決方案有:

  • 拉鏈法,把哈希碰撞的元素指向一個鏈表

  • 開放尋址法,把產(chǎn)生沖突的哈希值作為值,再進行哈希運算,直到不沖突

  • 再散列法,就是換一種哈希算法重來一次

  • 建立公共溢出區(qū),把哈希表分為基本表和溢出表,將產(chǎn)生哈希沖突的元素移到溢出表

HashMap為什么要用到鏈表結(jié)構(gòu)

當(dāng)我們向HashMap中添加元素時,會先根據(jù)key盡心哈希運算,把hash值模與數(shù)組長度得到一個下標,然后將該元素添加進去。但是如果產(chǎn)生了哈希碰撞,也就是不同的key計算出了相同的hash值,這就出問題了,因此它采用了拉鏈法來解決這個問題,將產(chǎn)生hash碰撞的元素,掛載到鏈表中

HashMap為什么要用到紅黑樹

當(dāng)HashMap中同一個索引位置出現(xiàn)哈希碰撞的元素多了,鏈表會變得越來越長,查詢效率會變得越來越慢。因此在JDK1.8之后,當(dāng)鏈表長度超過8個,會將鏈表轉(zhuǎn)壞為紅黑樹來提高查詢

HashMap鏈表和紅黑樹在什么情況下轉(zhuǎn)換的?

當(dāng)鏈表的長度大于等于8,同時數(shù)組的長度大于64,鏈表會自動轉(zhuǎn)化為紅黑樹,當(dāng)樹中的節(jié)點數(shù)小于等于6,紅黑樹會自動轉(zhuǎn)化為鏈表

HashMap在什么情況下擴容?HashMap如何擴容的?

HashMap的數(shù)組初始容量是16,負載因子是0.75,也就是說當(dāng)數(shù)組中的元素個數(shù)大于12個,會成倍擴容

tips:為啥子是0.75:負載因子過小容易浪費空間,過大容易造成更多的哈希碰撞,產(chǎn)生更多的鏈表和樹,因此折衷考慮采用了0.75

為啥子是成倍擴容:需要保證數(shù)組的長度是2的整數(shù)次冪

為嘛數(shù)組的長度必須是2的整數(shù)次冪:我們在存儲元素到數(shù)組中的時候,是通過hash值模與數(shù)組的長度,計算出下標的。但是由于計算機的運算效率,加減法>乘法>除法>取模,取模的效率是最低的。開發(fā)者們?yōu)榱俗屇阌玫拈_心,也是嘔心瀝血。將取模運算轉(zhuǎn)化成了與運算,即數(shù)組長度減1的值和hash值的與運算,以此來優(yōu)化性能。但是這個轉(zhuǎn)化有一個前提,就是數(shù)組的長度必須為2的整數(shù)次冪

HashMap是如何Put一個元素的

首先,將key進行hash運算,將這個hash值與上當(dāng)前數(shù)組長度減1的值,計算出索引。此時判斷該索引位置是否已經(jīng)有元素了,如果沒有,就直接放到這個位置

如果這個位置已經(jīng)有元素了,也就是產(chǎn)生了哈希碰撞,那么判斷舊元素的key和新元素的key的hash值是否相同,并且將他們進行equals比較,如果相同證明是同一個key,就覆蓋舊數(shù)據(jù),并將舊數(shù)據(jù)返回,如果不相同的話

再判斷當(dāng)前桶是鏈表還是紅黑樹,如果是紅黑樹,就按紅黑樹的方式,寫入該數(shù)據(jù),

如果是鏈表,就依次遍歷并比較當(dāng)前節(jié)點的key和新元素的key是否相同,如果相同就覆蓋,如果不同就接著往下找,直到找到空節(jié)點并把數(shù)據(jù)封裝成新節(jié)點掛到鏈表尾部。然后需要判斷,當(dāng)前鏈表的長度是否大于轉(zhuǎn)化紅黑樹的閾值,如果大于就轉(zhuǎn)化紅黑樹,最后判斷數(shù)組長度是否需要擴容。

HashMap是如何Get一個元素的

首先將key進行哈希運算,計算出數(shù)組中的索引位置,判斷該索引位置是否有元素,如果沒有,就返回null,如果有值,判斷該數(shù)據(jù)的key是否為查詢的key,如果是就返回當(dāng)前值的value

如果第一個元素的key不匹配,判斷是紅黑樹還是鏈表,如果是紅黑樹,就就按照紅黑樹的查詢方式查找元素并返回,如果是鏈表,就遍歷并匹配key,讓后返回value值

你知道HahsMap死循環(huán)問題嗎

HashMap在擴容數(shù)組的時候,會將舊數(shù)據(jù)遷徙到新數(shù)組中,這個操作會將原來鏈表中的數(shù)據(jù)顛倒,比如a->b->null,轉(zhuǎn)換成b->a->null

這個過程單線程是沒有問題的,但是在多線程環(huán)境,就可能會出現(xiàn)a->b->a->b....,這就是死循環(huán)

在JDK1.8后,做了改進保證了轉(zhuǎn)換后鏈表順序一致,死循環(huán)問題得到了解決。但還是會出現(xiàn)高并發(fā)時數(shù)據(jù)丟失的問題,因此在多線程情況下還是建議使用ConcurrentHashMap來保證線程安全問題

說一下你對ConcurrentHashMap的理解

ConcurrentHashMap,它是HashMap的線程安全,支持高并發(fā)的版本

在jdk1.7中,它是通過分段鎖的方式來實現(xiàn)線程安全的。意思是將哈希表分成許多片段Segment,而Segment本質(zhì)是一個可重入的互斥鎖,所以叫做分段鎖。

在jdk1.8中,它是采用了CAS操作和synchronized來實現(xiàn)的,而且每個Node節(jié)點的value和next都用了volatile關(guān)鍵字修飾,保證了可見性


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容