2018最新 java 面試題總結(一)

Q:面向對象編程的四大特性及其含義?

封裝:將某事物的屬性和行爲包裝到對象中,構成一個不可分割的獨立實體,數據被保護在抽象數據類型的內部,並且儘可能地隱藏內部的細節,只保留一些對外接口使之與外部發生聯繫。通俗點即隱藏信息 提供使用接口給別人使用,不讓別人直接操作屬性或方法

繼承:子類繼承父類,不僅可以有父類原有的方法和屬性,也可以增加自己的或者重寫父類的方法及屬性

多態:多態的概念即“一個接口,多個方法” ,允許不同類的對象對同一消息做出各自的響應

抽象 :面向對象的領域一切都是對象,同時所有的對象都是通過類來描述的,但是並不是所有的類都是來描述對象的。如果一個類沒有足夠的信息來描述一個具體的對象,而需要其他具體的類來支撐它,那麼這樣的類我們稱它爲抽象類。比如new Animal(),我們都知道這個是產生一個動物Animal對象,但是這個Animal具體長成什麼樣子我們並不知道,它沒有一個具體動物的概念,所以他就是一個抽象類,需要一個具體的動物,如狗、貓來對它進行特定的描述,我們才知道它長成啥樣。抽象類提供了繼承的概念,它的出發點就是爲了繼承,否則它沒有存在的任何意義。所以說定義的抽象類一定是用來繼承的,同時在一個以抽象類爲節點的繼承關係等級鏈中,葉子節點一定是具體的實現類。

接口和抽象類的區別:

(1)相同點

A,兩者都是抽象類,都不能實例化。

B,interface實現類及abstrct class的子類都必須要實現已經聲明的抽象方法。 

(2)不同點

A,interface實現,要用implements,而abstract class的實現,要用extends。

 B,一個類可以實現多個interface,但一個類只能繼承一個abstract class。

 C,interface強調特定功能的實現,而abstract class強調所屬關係。 

D,儘管interface實現類及abstrct class的子類都必須要實現相應的抽象方法,但實現的形式不同。

 E,interface是完全抽象的,只能聲明方法,而且只能聲明pulic的方法,不能聲明private及protected的方法,不能定義方法體,也不能聲明實例變量。

Q:String、StringBuffer和StringBuilder的區別?

String是字符串常量,而StringBuffer、StringBuilder都是字符串變量,即String對象一創建後不可更改,而後兩者的對象是可更改的:

StringBuffer是線程安全的,而StringBuilder是非線程安全的,這是由於StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖

String更適用於少量的字符串操作的情況,StringBuilder適用於單線程下在字符緩衝區進行大量操作的情況,StringBuffer適用於多線程下在字符緩衝區進行大量操作的情況

Q:String a=""和String a=new String("")的的關係和異同?

通過String a=""直接賦值的方式得到的是一個字符串常量,存在於常量池;注意,相同內容的字符串在常量池中只有一個,即如果池已包含內容相等的字符串會返回池中的字符串,反之會將該字符串放入池中

通過new String("")創建的字符串不是常量是實例對象,會在內存開闢空間並存放數據,且每個實例對象都有自己的地址空間

引申:對於用String a=""和String a=new String("")兩種方式定義的字符串,判斷使用equals()、"=="比較結果是什麼

Q:Object的equal()和==的區別?

1.==是判斷兩個變量或實例是不是指向同一個內存空間 equals是判斷兩個變量或實例所指向的內存空間的值是不是相同

2.==是指對內存地址進行比較 equals()是對字符串的內容進行比較

3.==指引用是否相同 equals()指的是值是否相同  


Q:裝箱、拆箱什麼含義?

裝箱就是自動將基本數據類型轉換爲包裝器類型,拆箱就是自動將包裝器類型轉換爲基本數據類型

Integer i = 10;  //裝箱

int n = i;   //拆箱  

Q:int和Integer的區別?

Integer是int的包裝類,int則是java的一種基本數據類型

Integer變量必須實例化後才能使用,而int變量不需要

Integer實際是對象的引用,當new一個Integer時,實際上是生成一個指針指向此對象;而int則是直接存儲數據值

Integer的默認值是null,int的默認值是0

Q:遇見過哪些運行時異常?異常處理機制知道哪些?

對Throwable異常進行分類說明每種異常的特點和常見問題,簡述幾種常見異常處理機制,詳見Java基礎之異常機制

(1) Throwable繼承層次結構,可見分成兩大類Error和Exception:

Error(錯誤):指程序無法恢復的異常情況,表示運行應用程序中較嚴重的問題;發生於虛擬機自身、或者在虛擬機試圖執行應用時,如Virtual MachineError(Java虛擬機運行錯誤)、NoClassDefFoundError(類定義錯誤);屬於不可查異常,即不強制程序員必須處理,即使不處理也不會出現語法錯誤。

Exception(異常):指程序有可能恢復的異常情況,表示程序本身可以處理的異常。又分兩大類:

RuntimeException(運行時異常):由程序自身的問題導致產生的異常;如NullPointerException(空指針異常)、IndexOutOfBoundsException(下標越界異常);屬於不可查異常。

非運行時異常:由程序外部的問題引起的異常;除了RuntimeException以外的異常,如FileNotFoundException(文件不存在異常);屬於可查異常,即強制程序員必須進行處理,如果不進行處理則會出現語法錯誤。

(2)常見的異常處理機制有:

捕捉異常:由系統自動拋出異常,即try捕獲異常->catch處理異常->finally 最終處理

拋出異常:在方法中將異常對象顯性地拋出,之後異常會沿着調用層次向上拋出,交由調用它的方法來處理。配合throws聲明拋出的異常和throw拋出異常

自定義異常:繼承Execption類或其子類

Q:什麼是反射,有什麼作用和應用?Java基礎之泛型&反射

我們可以在運行時獲得程序或程序集中每一個類型的成員和成員的信息。程序中一般的對象的類型都是在編譯期就確定下來的,而 Java 反射機制可以動態地創建對象並調用其屬性,這樣的對象的類型在編譯期是未知的。所以我們可以通過反射機制直接創建對象,即使這個對象的類型在編譯期是未知的。

引申:是否在項目中使用過反射機制,有什麼優缺點

反射提供了一種運行期獲取對象元信息的手段。寫框架代碼用的比較多,因爲需要運行時動態獲取和操作對象的屬性和方法。儘量少用反射,會有性能開銷,大多數場景下可以用設計模式代替。

舉個具體應用場景:你用第三方jar,比如spring/imageload,你可以在配置文件修改各種參數,但你不可能會去直接修改裏面的代碼吧,這些代碼就是獲取配置文件裏的數據再通過反射來操作,降低耦合。

Q:什麼是內部類?有什麼作用?靜態內部類和非靜態內部類的區別?

內部類就是定義在另外一個類裏面的類。它隱藏在外部類中,封裝性更強,不允許除外部類外的其他類訪問它;但它可直接訪問外部類的成員。靜態內部類和非靜態內部類的區別有:

靜態內部類是指被聲明爲static的內部類,可不依賴外部類實例化;而非靜態內部類需要通過生成外部類來間接生成。

靜態內部類只能訪問外部類的靜態成員變量和靜態方法,而非靜態內部類由於持有對外部類的引用,可以訪問外部類的所用成員

靜態內部類和非靜態內部類的區別

Q:Java中的final關鍵字

方法前面加上final關鍵字,代表這個方法不可以被子類的方法重寫。如果你認爲一個方法的功能已經足夠完整了,子類中不需要改變的話,你可以聲明此方法爲final。final方法比非final方法要快,因爲在編譯的時候已經靜態綁定了,不需要在運行時再動態綁定。

你不能夠對final變量再次賦值。本地變量必須在聲明時賦值。在匿名類中所有變量都必須是final變量。 

final關鍵字的好處:

final關鍵字提高了性能。JVM和Java應用都會緩存final變量。

final變量可以安全的在多線程環境下進行共享,而不需要額外的同步開銷。

使用final關鍵字,JVM會對方法、變量及類進行優化。  

Q:重寫和重載的區別?

參考回答:重寫表示子類重寫父類的方法;重載表示有多個同名函數同時存在,區別在於有不同的參數個數或類型

引申:談談動態分派和靜態分派

Q:抽象類和接口的異同?

使用上的區別:一個類只能繼承一個抽象類卻可以實現多個接口

設計上的區別:接口是對行爲的抽象,無需有子類的前提,是自上而下的設計理念;抽象類是對類的抽象,建立於相似子類之上,是自下而上的設計理念

Q:爲什麼匿名內部類中使用局部變量要用final修飾?

一方面,由於方法中的局部變量的生命週期很短,

一旦方法結束變量就要被銷燬,爲了保證在內部類中能找到外部局部變量,通過final關鍵字可得到一個外部變量的引用;

另一方面,通過final關鍵字也不會在內部類去做修改該變量的值,保護了數據的一致性。

匿名內部類

Q:Object有哪些公有方法?

equals(): 和==作用相似

hashCode():用於哈希查找,重寫了equals()一般都要重寫該方法

getClass(): 獲取Class對象

wait():讓當前線程進入等待狀態,並釋放它所持有的鎖

notify()&notifyAll(): 喚醒一個(所有)正處於等待狀態的線程

toString():轉換成字符串

引申:equals()和==的不同、在synchronized 同步代碼塊裏wait()和notify()&notifyAll()如何配合、hashCode()和equals()的關係、獲取Class對象還有什麼方法

Q:Java集合框架中有哪些類?都有什麼特點

可將Java集合框架大致可分爲Set、List、Queue 和Map四種體系

Set:代表無序、不可重複的集合,常見的類如HashSet、TreeSet

List:代表有序、可重複的集合,常見的類如動態數組ArrayList、雙向鏈表LinkedList、可變數組Vector

Map: 無序  , 不可重複的集合 ,代表具有映射關係的集合,常見的類如HashMap、LinkedHashMap、TreeMap

Queue:代表一種隊列集合

有序,無序:比如:‘zs’,‘zs’ 這兩個 是不同的人是同一個名字,沒順序你是無法判斷是誰的list他是有順序的,所以你只要知道他們的順序就完全可以判斷是哪個人了,

可重複:key和value都可以重複,  不可重複:key不可以重複,value可以重複

Q:集合、數組、泛型的關係,並比較

(1)集合和數組的區別:

數組元素可以是基本類型,也可以是對象;數組長度限定;數組只能存儲一種類型的數據元素

集合元素只能是對象;集合長度可變;集合可存儲不同種的數據元素

(2)泛型相比與集合的好處在於它安全簡單。具體體現在提供編譯時的強類型檢查,而不用等到運行;可避免類類型強制轉換

Java 泛型

Q:ArrayList和LinkList的區別?

ArrayList的底層結構是數組, 它允許對元素進行快速隨機訪問。數組的缺點是每個元素之間不能有間隔,當數組大小不滿足時需要增加存儲能力,就要講已經有數組的數據複製到新的存儲空間中。當從ArrayList的中間位置插入或者刪除元素時,需要對數組進行復制、移動、代價比較高。因此,它適合隨機查找和遍歷,不適合插入和刪除。

LinkedList是用鏈表結構存儲數據 ,增刪速度快;是一個雙向循環鏈表,也可以被當作堆棧、隊列或雙端隊列

Vector與ArrayList一樣,也是通過數組實現的

Q:ArrayList和Vector的區別?

安全,建議在單線程中才使用ArrayList,而在多線程中可以選擇Vector或者CopyOnWriteArrayList;

Vector使用了synchronized關鍵字,是線程安全的,比ArrayList開銷更大,訪問更慢;默認初始容量爲10,默認每次擴容爲原來的2倍,可通過capacityIncrement屬性設置

ArrayList在內存不夠時默認是擴展50% + 1個,Vector是默認擴展1倍。                                                                                                    Vector提供indexOf(obj, start)接口,ArrayList沒有。                                                                                                                                Vector屬於線程安全級別的,但是大多數情況下不使用Vector,因爲線程安全需要更大的系統開銷。 

Q:HashSet和TreeSet的區別?

HashSet不能保證元素的排列順序;使用Hash算法來存儲集合中的元素,有良好的存取和查找性能;通過equal()判斷兩個元素是否相等,並兩個元素的hashCode()返回值也相等

TreeSet是SortedSet接口的實現類,根據元素實際值的大小進行排序;採用紅黑樹的數據結構來存儲集合元素;支持兩種排序方法:自然排序(默認情況)和定製排序。前者通過實現Comparable接口中的compareTo()比較兩個元素之間大小關係,然後按升序排列;後者通過實現Comparator接口中的compare()比較兩個元素之間大小關係,實現定製排列

Q:HashMap和Hashtable的區別?

HashMap基於AbstractMap類,實現了Map、Cloneable(能被克隆)、Serializable(支持序列化)接口;非線程安全;允許存在一個爲null的key和任意個爲null的value;採用鏈表散列的數據結構,即數組和鏈表的結合;初始容量爲16,填充因子默認爲0.75,擴容時是當前容量翻倍,即2 capacity

Hashtable基於Map接口和Dictionary類;線程安全,開銷比HashMap大,如果多線程訪問一個Map對象,使用Hashtable更好;不允許使用null作爲key和value;底層基於哈希表結構;初始容量爲11,填充因子默認爲0.75,擴容時是容量翻倍+1,即2capacity+1

Q:HashMap在put、get元素的過程?體現了什麼數據結構?

向HashMap中put元素時,首先判斷key是否爲空,爲空則直接調用putForNullKey(),不爲空則計算key的hash值得到該元素在數組中的下標值;如果數組在該位置處沒有元素,就直接保存;如果有,還要比較是否存在相同的key,存在的話就覆蓋原來key的value,否則將該元素保存在鏈頭,先保存的在鏈尾。

從Hashmap中get元素時,計算key的hash值找到在數組中的對應的下標值,返回該key對應的value即可,如果有衝突就遍歷該位置鏈表尋找key相同的元素並返回對應的value

由此可看出HashMap採用鏈表散列的數據結構,即數組和鏈表的結合,在Java8後又結合了紅黑樹,當鏈表元素超過8個將鏈表轉換爲紅黑樹

Q:HashMap是如何擴容的?如何避免擴容?

HashMap幾個默認值,初始容量爲16、填充因子默認爲0.75、擴容時容量翻倍。也就是說當HashMap中元素個數超過16*0.75=12時會把數組的大小擴展爲2*16=32,然後重新計算每個元素在數組中的位置.

由於每次擴容還需要重新計算元素Hash值,損耗性能,所以建議在使用HashMap時,最好先估算Map的大小,設置初始值,避免頻繁擴容

Q:hashcode()的作用,與equal()有什麼區別?

hashCode()用於計算對象的Hash值,確認對象在散列存儲結構中的存儲地址。和equal()的區別:

equals()比較兩個對象的地址值是否相等 ;hashCode()得到的是對象的存儲位置,可能不同對象會得到相同值

有兩個對象,若equals()相等,則hashcode()一定相等;hashcode()不等,則equals()一定不相等;hashcode()相等,equals()可能相等、可能不等

使用equals()比較兩個對象是否相等效率較低,最快辦法是先用hashCode()比較,如果hashCode()不相等,則這兩個對象肯定不相等;如果hashCode()相等,此時再用equal()比較,如果equal()也相等,則這兩個對象的確相等,反之併發

Q:同步和非同步、阻塞和非阻塞,併發,並行的概念

同步和異步體現的是消息的通知機制:所謂同步,方法A調用方法B後必須等到方法B返回結果才能繼續後面的操作;所謂異步,方法A調用方法B後可讓方法B在調用結束後通過回調等方式通知方法A

阻塞和非阻塞:側重於等待消息時的狀態:所謂阻塞,就是在結果返回之前讓當前線程掛起;所謂非阻塞,就是在等待時可做其他事情,通過輪詢去詢問是否已返回結果

併發,並行 :併發是指一個時間段內,有幾個程序都在同一個CPU上運行,但任意一個時刻點上只有一個程序在處理機上運行。並行是指一個時間段內,有幾個程序都在幾個CPU上運行,任意一個時刻點上,有多個程序在同時運行,並且多道程序之間互不干擾。 兩者區別如下圖

Q:Thread的join()有什麼作用?

參考回答:Thread的join()的含義是等待該線程終止,即將掛起調用線程的執行,直到被調用的對象完成它的執行。比如存在兩個線程t1和t2,下述代碼表示先啓動t1,直到t1的任務結束,才輪到t2啓動。

t1.start();

t1.join();

t2.start();

Q:線程的有哪些狀態?

參考回答:在任意一個時間點,一個線程只能有且只有其中的一種狀態:

線程狀態

1.新建 (New) new語句創建的線程對象處於新建狀態,此時它和其他java對象一樣,僅被分配了內存。

2.等待 (Runable) 當線程在new之後,並且在調用start方法前,線程處於等待狀態。

3.就緒 (Waiting) 當一個線程對象創建後,其他線程調用它的start()方法,該線程就進入就緒狀態。處於這個狀態的線程位於Java虛擬機的可運行池中,等待cpu的使用權。

4.運行狀態(running)  處於這個狀態的線程佔用CPU,執行程序代碼。在併發運行環境中,如果計算機只有一個CPU,那麼任何時刻只會有一個線程處於這個狀態。 只有處於就緒狀態的線程纔有機會轉到運行狀態。

5.阻塞狀態 (Blocked) 阻塞狀態是指線程因爲某些原因放棄CPU,暫時停止運行。當線程處於阻塞狀態時,Java虛擬機不會給線程分配CPU,直到線程重新進入就緒狀態,它纔會有機會獲得運行狀態。

阻塞狀態分爲三種:

1、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。

2、同步阻塞:運行的線程在獲取對象同步鎖時,若該同步鎖被別的線程佔用,則JVM會把線程放入鎖池中。

3、其他阻塞:運行的線程執行Sleep()方法,或者發出I/O請求時,JVM會把線程設爲阻塞狀態。當Sleep()狀態超時、或者I/O處理完畢時,線程重新轉入就緒狀態。

6.死亡狀態 (dead)  當線程執行完run()方法中的代碼,或者遇到了未捕獲的異常,就會退出run()方法,此時就進入死亡狀態,該線程結束生命週期。


Q:死鎖產生條件和應用場景

產生死鎖的主要原因有三點:死鎖產生的原因、必要條件和場景解析

(一)系統資源不足;

(二)進程運行推進的順序不對;

(三)系統資源分配不當。

Q:sleep()和wait()的區別?

sleep()來自Thread類;wait()來自Object類

sleep()用於線程控制自身流程;而wait()用於線程間通信,配合notify()/notifyAll()在同步代碼塊或同步方法裏使用

sleep()的線程不會釋放對象鎖;wait()會釋放對象鎖進入等待狀態,使得其他線程能使用同步代碼塊或同步方法

Q: 異常處理流程,何時必須要catch:Java異常的捕獲及處理

異常指程序運行過程中出現的非正常現象,例如用戶輸入錯誤、除數爲零、需要處理的文件不存在、數組下標越界等。所謂異常處理,就是指程序在出現問題時依然可以正確的執行完。

異常是Java的一個重大特色,合理的使用異常處理,可以讓我們程序更加的健壯。

Q: 無序數組建立二叉搜索樹

二叉樹遍歷(前序、中序、後序、層次遍歷、深度優先、廣度優先)

Q: 四大引用區別和引用場景:Java中的四種引用介紹和使用場景

強引用(Strong Reference):一個對象如果具有強引用,那麼垃圾回收器絕不會回收它,即使當內存不足時,VM寧願拋出內存不足的異常,也不會去回收這些對象。

軟引用 (Soft Reference): 如果一個對象只具有軟引用,則內存空間足夠時,垃圾回收器就不會去回收它;如果內存空間不足時,就會回收這些對象的內存。 

弱引用(Weak Reference):弱引用和軟引用的區別在於,只具有弱引用的對象擁有更短暫的生命週期,在垃圾回收器線程掃描它管轄的內存區域的過程中,一旦發現對象只具有弱引用,不管當前內存空間是否足夠,都會回收他的內存。

它比軟引用的生命週期更短,和軟引用相似,它同樣可以和引用隊列關聯,如果被垃圾回收了,就會加入到這個關聯隊列中。

虛引用(Phantom Reference)幾種引用都不同,虛引用並不會決定對象的生命週期,如果一個對象僅持有虛引用的話,那麼它就和沒有任何的引用一樣,在任何時候都可能被垃圾回收器回收。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章