瞭解了這些,輕鬆拿offer——Java面試之道

~wangweijun發表於2020-01-28

本篇文章將講述Java基礎中的面試題及其解答,希望對大家有所幫助。

synchronized和Lock的區別

synchronized是JVM層面實現的,java提供的關鍵字,Lock是API層面的鎖。

  • synchronized不需要手動釋放鎖,底層會自動釋放;Lock則需要手動釋放鎖,否則有可能導致死鎖
  • synchronized等待不可中斷,除非丟擲異常或者執行完成;Lock可以中斷,通過interrupt()可中斷
  • synchronized是非公平鎖;Lock是預設非公平鎖,當傳入true時是公平鎖
  • synchronized不可繫結多個條件;Lock可實現分組喚醒需要喚醒的鎖

"=="和equals方法的區別

對於"==":在基本型別下,"== "比較的是值,而在引用型別下,"=="比較的是地址。
而對於equals方法:如果呼叫的類中沒有實現equals方法,將會呼叫父類的equals方法,此時比較的是地址;而很多api中的類均已實現equals方法,它們比較的是值。

&和&&的區別

&和&&都可以用作邏輯與的運算子,當運算子兩邊的表示式的結果都為true時,整個運算結果才為true,否則,只要有一方為false,則結果為false。
&&還具有短路的功能,即如果第一個表示式為false,則不再計算第二個表示式,直接得出結果為false。
&還可以用作位運算子,當&操作符兩邊的表示式不是boolean型別時,&表示按位與操作,我們通常使用0x0f來與一個整數進行&運算,來獲取該整數的最低4個bit位。

什麼是可變引數

可變參數列示需要接收的引數個數可以不確定,例如:

public void show(int... nums){

}

此時該方法可以接收任意個int型別的引數。
想要獲得所有引數,就可以對nums進行遍歷,此時的nums為引數陣列。

final,finally,finalize的區別

final,意為最終的,用於修飾類,方法和變數。
如果一個類被final修飾,意味著這個類為最終類,它將不能再派生出新的子類,不能被繼承,否則出錯,因此在宣告類時,final和abstract無法同時出現,因為abstract修飾的類必定要有具體的子類實現,而final不允許子類的繼承,此時陷入矛盾。
如果一個變數被final修飾,則它在程式中將不能被修改,而且必須在宣告變數時就賦值。
如果一個方法被final修飾,它將無法被子類重寫。
finally,是異常處理中的關鍵字,它表示無論異常是否被捕獲,程式都將執行finally中的程式碼塊。
finalize,是Object中的方法,它的目的是保證物件在被垃圾收集前完成特定資源的回收,而事實上,這個方法具有不確定性,它並不能保證你在呼叫了該方法之後就會完成資源回收。

String s = new String(“abc”);建立了幾個String 物件

建立了兩個,第一個:“abc”,這種方式稱為顯式宣告字串,會在常量池中建立一個物件;第二個:new關鍵字會在堆中建立指向常量池字串的引用。所以一共建立了兩個String物件。

GC是什麼?為什麼要有GC?

GC是垃圾收集的意思,記憶體處理是程式設計人員容易出現問題的地方,忘記或者錯誤的記憶體回收會導致程式或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測物件是否超過作用域從而達到自動回收記憶體的目的,Java語言沒有提供釋放已分配記憶體的顯式操作方法。Java程式設計師不用擔心記憶體管理,因為垃圾收集器會自動進行管理。要請求垃圾收集,可以呼叫下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以遮蔽掉顯式的垃圾回收呼叫。
垃圾回收可以有效的防止記憶體洩露,有效地使用的記憶體。垃圾回收器通常是作為一個單獨的低優先順序的執行緒執行,不可預知的情況下對記憶體堆中已經死亡的或者長時間沒有使用的物件進行清除和回收,程式設計師不能實時地呼叫垃圾回收器對某個物件或所有物件進行垃圾回收。

length()和length的區別

Java中的length屬性是針對陣列來說的,假設宣告一個陣列,則可以通過length屬性直接獲得陣列的長度;
Java中的length()方法是針對字串String的,如果想得到字串長度,則可以使用length()方法。

Java 建立物件的幾種方式

  1. 通過new關鍵字
  2. 通過反射
  3. 採用clone
  4. 通過序列化

throw和throws的區別

throw用於主動丟擲java.lang.Throwable 類的一個例項化物件,意思是說你可以通過關鍵字 throw 丟擲一個 Error 或者 一個Exception,如:throw new IllegalArgumentException(“″), 而throws 的作用是作為方法宣告和簽名的一部分,方法被丟擲相應的異常以便呼叫者能處理。Java 中,任何未處理的受檢查異常強制在 throws 子句中宣告。

char型變數中能不能存貯一箇中文漢字

char型變數是用來儲存Unicode編碼字元的,Unicode編碼字符集中包含了漢字,所以,char型變數可以用於儲存中文漢字。不過,如果某個特殊的漢字沒有被包含在Unicode編碼字符集中,那麼,這個char型變數中就不能儲存這個特殊漢字。

switch語句中的表示式能否使用byte型別、short型別、long型別、String型別

在switch中,例如switch(x),表示式x規定只能是一個整數表示式或者列舉型別,這裡的整數表示式可以是int型別,也可以是Integer型別,而byte、short都可以隱式轉換為int,所以可以使用,那麼很顯然,由於long無法隱式轉換為int,所以long型別無法作用在switch語句中。從Java7開始,switch語句支援字串作為表示式。

Integer與int的區別

int是Java提供的8種原始資料型別之一。Java為每個原始型別都提供了封裝類,Integer即為int的封裝類。int的預設值為0,而Integer的預設值為null,即Integer可以區分出未賦值和值為0的區別,int則無法表達出未賦值的情況,例如,要想表達出沒有參加考試和考試成績為0的區別,則只能使用Integer。在JSP開發中,Integer的預設為null,所以用el表示式在文字框中顯示時,值為空白字串,而int預設的預設值為0,所以用el表示式在文字框中顯示時,結果為0,所以,在web開發中的POJO中,你會發現int型別基本不會出現,int型別不適合作為web開發的資料展示。

http協議和https協議的區別

HTTP:是網際網路上應用最為廣泛的一種網路協議,是一個客戶端和伺服器端請求和應答的標準(TCP),用於從WWW伺服器傳輸超文字到本地瀏覽器的傳輸協議,它可以使瀏覽器更加高效,使網路傳輸減少。
HTTPS:是以安全為目標的HTTP通道,簡單講是HTTP的安全版,即HTTP下加入SSL層,HTTPS的安全基礎是SSL,因此加密的詳細內容就需要SSL。
區別:

  • https協議需要到ca申請證照,一般免費證照較少,因而需要一定費用
  • http是超文字傳輸協議,資訊是明文傳輸,https則是具有安全性的ssl加密傳輸協議
  • http和https使用的是完全不同的連線方式,用的埠也不一樣,前者是80,後者是443
  • http的連線很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網路協議,比http協議安全

環境變數path和classpath的作用分別是什麼

path是配置Windows可執行檔案的搜尋路徑,即副檔名為.exe的程式檔案所在的目錄,用於指定DOS視窗命令的路徑。
Classpath是配置class檔案所在的目錄,用於指定類搜尋路徑,JVM就是通過它來尋找該類的class類檔案的。

float f=1.1;是否正確

答案是錯誤,在Java中浮點數預設為double型別,所以這裡的1.1其實是double型別,將double型別賦值給float型別會造成精度丟失,所以必須強制型別轉換才行,否則報錯。

List的三個子類的特點

ArrayList 底層結構是陣列,底層查詢快,增刪慢
LinkedList 底層結構是連結串列,增刪快,查詢慢
Voctor 底層結構是陣列,執行緒安全的,增刪慢,查詢慢

靜態變數和例項變數的區別

靜態變數屬於類,例項變數依存於某一例項
靜態變數:是被static修飾符修飾的變數,也稱為類變數,它屬於類,不屬於類的任何一個物件,一個類不管建立多少個物件,靜態變數在記憶體中有且僅有一個拷貝;
例項變數:必須依存於某一例項,需要先建立物件然後通過物件才能訪問到它。靜態變數可以實現讓多個物件共享記憶體。

如何理解執行緒的優先順序

在Java中,每個執行緒都有相應的優先順序,如果沒有手動設定,則程式會有一個預設的執行緒優先順序。一般來說,高優先順序的執行緒在執行時會具有優先權,但這依賴於執行緒排程的實現,這個實現是和作業系統相關的。我們可以定義執行緒的優先順序,但是這並不能保證高優先順序的執行緒會在低優先順序的執行緒前執行。執行緒優先順序是一個int變數(從1-10),1代表最低優先順序,10代表最高優先順序。

String類可以被繼承嗎

String類無法被繼承,因為String類被final關鍵字修飾

為什麼要將String設計為無法被繼承

因為只有當字串是不可變的,字串池才有可能實現。字串池的實現可以在執行時節約很多堆空間,因為不同的字串變數都指向池中的同一個字串。但如果字串是可變的,那麼字串駐留將不能實現,因為這樣的話,如果變數改變了它的值,那麼其它指向這個值的變數的值也會一起改變。如果字串是可變的,那麼會引起很嚴重的安全問題。譬如,資料庫的使用者名稱、密碼都是以字串的形式傳入來獲得資料庫的連線,或者在socket程式設計中,主機名和埠都是以字串的形式傳入。因為字串是不可變的,所以它的值是不可改變的,否則黑客們可以鑽到空子,改變字串指向的物件的值,造成安全漏洞。
因為字串是不可變的,所以是多執行緒安全的,同一個字串例項可以被多個執行緒共享。這樣便不用因為執行緒安全問題而使用同步。字串自己便是執行緒安全的。

如何跳出當前的多重巢狀迴圈

在最外成迴圈前加一個標記,然後在需要跳出迴圈的地方編寫break + 標記 即可跳出多重迴圈,例如:

public static void main(String[] args){
	x:for(int i = 0;i < 3;i++){
		for(int j = 0;j < 3;j++){
			for(int k = 0;k < 3;k++){
				break x;
			}
		}
	}
}

break和continue的區別

break和continue 都是用來控制迴圈的語句。
break 用於完全結束一個迴圈,跳出迴圈體執行迴圈後面的語句。

String s = “Hello”;s = s + " world!";這兩行程式碼執行後,原始的 String 物件中的內容有沒有被改變

前面就已經說過了,String是被final修飾的,是無法修改的,那麼輸出s的內容為什麼又是Helloworld!呢?是因為此時的s已經不再指向原始的字串了,它指向了新拼接的字串Helloworld!,而原始的字串Hello其實並沒有被改變。

同步和非同步有什麼區別

如果資料將線上程間共享。例如正在寫的資料以後可能被另一個執行緒讀到,或者正在讀的資料可能已經被另一個執行緒寫過了,那麼這些資料就是共享資料,必須進行同步存取。
當應用程式在物件上呼叫了一個需要花費很長時間來執行的方法,並且不希望讓程式等待方法的返回時,就應該使用非同步程式設計,在很多情況下采用非同步途徑往往更有效率。

static方法內部可以呼叫非static方法嗎

這是絕對不可以的,因為非static方法是要與物件關聯在一起的,必須建立一個物件後,才可以在該物件上進行方法呼叫,而static方法呼叫時不需要建立物件,可以直接呼叫。也就是說,當一個static方法被呼叫時,可能還沒有建立任何例項物件,如果從一個static方法中發出對非static方法的呼叫,那個非static方法無法知曉它將關聯的物件是哪個,所以不能。

使用final關鍵字修飾一個變數時,是引用不能變,還是引用的物件不能變

使用final關鍵字修飾一個變數時,是指引用變數不能變,引用變數所指向的物件中的內容還是可以改變的。

List、Map、Set三個介面存取元素時,各有什麼特點

List以特定索引來存取元素,可以有重複元素。Set不能存放重複元素(用物件的equals()方法來區分元素是否重複)。
Map儲存鍵值對對映,對映關係可以是一對一或多對一。
Set和Map容器都有基於雜湊儲存和排序樹的兩種實現版本,基於雜湊儲存的版本理論存取時間複雜度為O(1),而基於排序樹版本的實現在插入或刪除元素時會按照元素或元素的鍵(key)構成排序樹從而達到排序和去重的效果。

編譯時異常和執行時異常的區別

編譯時異常在函式內被丟擲,函式必須宣告,否則編譯失敗。
宣告的原因:是需要呼叫者對該異常進行處理。必須進行處理,否則無法編譯通過。
執行時異常如果在函式內被丟擲,在函式上不需要宣告。
不宣告的原因:不需要呼叫者處理,執行時異常發生,已經無法再讓程式繼續執行,所以,不讓呼叫處理的,直接讓程式停止,由呼叫者對程式碼進行修正。

wait( )和sleep( )的區別

  • sleep( )沒有釋放同步鎖,而wait( )釋放了同步鎖
  • sleep( )必須制定時間,而wait( )不用
  • sleep()可以在任何地方使用,而wait( )、notify( )、notifyAll( )只能在同步方法或同步程式碼塊中使用;
  • sleep()必須捕獲異常,而wait( )、notify( )、notifyAll( )不用

Java中如何實現序列化,有什麼意義?

序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化。可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間。序列化是為了解決物件流讀寫操作時可能引發的問題(如果不進行序列化可能會存在資料亂序的問題)。

要實現序列化,需要讓一個類實現Serializable介面,該介面是一個標識性介面,標註該類物件是可被序列化的,然後使用一個輸出流來構造一個物件輸出流並通過writeObject(Object)方法就可以將實現物件寫出(即儲存其狀態);如果需要反序列化則可以用一個輸入流建立物件輸入流,然後通過readObject方法從流中讀取物件。

例如,在web開發中,如果物件被儲存在了Session中,tomcat在重啟時要把Session物件序列化到硬碟,這個物件就必須實現Serializable介面。如果物件要經過分散式系統進行網路傳輸或通過rmi等遠端呼叫,這就需要在網路上傳輸物件,被傳輸的物件就必須實現Serializable介面。

相關文章