大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節(jié)點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 Java高級面試題整理(附答案)

Java高級面試題整理(附答案)

更新時間:2021-05-26 09:55:25 來源:動力節(jié)點 瀏覽3114次

1.為什么等待和通知是在 Object 類而不是 Thread 中聲明的?

一個棘手的 Java 問題,如果 Java編程語言不是你設計的,你怎么能回答這個問題呢。Java編程的常識和深入了解有助于回答這種棘手的 Java 核心方面的面試問題。

為什么 wait,notify 和 notifyAll 是在 Object 類中定義的而不是在 Thread 類中定義

這是有名的 Java 面試問題,招2~4年經(jīng)驗的到高級 Java 開發(fā)人員面試都可能碰到。

這個問題的好在它能反映了面試者對等待通知機制的了解, 以及他對此主題的理解是否明確。就像為什么 Java 中不支持多繼承或者為什么 String 在 Java 中是 final 的問題一樣,這個問題也可能有多個答案。

為什么在 Object 類中定義 wait 和 notify 方法,每個人都能說出一些理由。 從我的面試經(jīng)驗來看, wait 和 nofity 仍然是大多數(shù)Java 程序員最困惑的,特別是2到3年的開發(fā)人員,如果他們要求使用 wait 和 notify, 他們會很困惑。因此,如果你去參加 Java 面試,請確保對 wait 和 notify 機制有充分的了解,并且可以輕松地使用 wait 來編寫代碼,并通過生產(chǎn)者-消費者問題或?qū)崿F(xiàn)阻塞隊列等了解通知的機制。

為什么等待和通知需要從同步塊或方法中調(diào)用, 以及 Java 中的 wait,sleep 和 yield 方法之間的差異,如果你還沒有讀過,你會覺得有趣。為何 wait,notify 和 notifyAll 屬于 Object 類? 為什么它們不應該在 Thread 類中? 以下是我認為有意義的一些想法:

(1)wait 和 notify 不僅僅是普通方法或同步工具,更重要的是它們是 Java 中兩個線程之間的通信機制。對語言設計者而言, 如果不能通過 Java 關鍵字(例如 synchronized)實現(xiàn)通信此機制,同時又要確保這個機制對每個對象可用, 那么 Object 類則是的正確聲明位置。記住同步和等待通知是兩個不同的領域,不要把它們看成是相同的或相關的。同步是提供互斥并確保 Java 類的線程安全,而 wait 和 notify 是兩個線程之間的通信機制。

(2)每個對象都可上鎖,這是在 Object 類而不是 Thread 類中聲明 wait 和 notify 的另一個原因。

(3)在 Java 中為了進入代碼的臨界區(qū),線程需要鎖定并等待鎖定,他們不知道哪些線程持有鎖,而只是知道鎖被某個線程持有, 并且他們應該等待取得鎖, 而不是去了解哪個線程在同步塊內(nèi),并請求它們釋放鎖定。

(4)Java 是基于 Hoare 的監(jiān)視器的思想(http://en.wikipedia.org/wiki/...。在Java中,所有對象都有一個監(jiān)視器。

線程在監(jiān)視器上等待,為執(zhí)行等待,我們需要2個參數(shù):

一個線程

一個監(jiān)視器(任何對象)

在 Java 設計中,線程不能被指定,它總是運行當前代碼的線程。但是,我們可以指定監(jiān)視器(這是我們稱之為等待的對象)。這是一個很好的設計,因為如果我們可以讓任何其他線程在所需的監(jiān)視器上等待,這將導致“入侵”,導致在設計并發(fā)程序時會遇到困難。請記住,在 Java 中,所有在另一個線程的執(zhí)行中侵入的操作都被棄用了(例如 stop 方法)。

2.為什么Java中不支持多重繼承?

發(fā)現(xiàn)這個 Java 核心問題很難回答,因為你的答案可能不會讓面試官滿意,在大多數(shù)情況下,面試官正在尋找答案中的關鍵點,如果你提到這些關鍵點,面試官會很高興。在 Java 中回答這種棘手問題的關鍵是準備好相關主題, 以應對后續(xù)的各種可能的問題。

這是非常經(jīng)典的問題,與為什么 String 在 Java 中是不可變的很類似; 這兩個問題之間的相似之處在于它們主要是由 Java 創(chuàng)作者的設計決策使然。

為什么Java不支持多重繼承, 可以考慮以下兩點:

(1)第一個原因是圍繞鉆石形繼承問題產(chǎn)生的歧義,考慮一個類 A 有 foo() 方法, 然后 B 和 C 派生自 A, 并且有自己的 foo() 實現(xiàn),現(xiàn)在 D 類使用多個繼承派生自 B 和C,如果我們只引用 foo(), 編譯器將無法決定它應該調(diào)用哪個 foo()。這也稱為 Diamond 問題,因為這個繼承方案的結構類似于菱形,見下圖:

A foo()    
               / \    
             /     \    
 foo() B     C foo()    
             \     /    
               \ /    
               D  foo()

即使我們刪除鉆石的頂部 A 類并允許多重繼承,我們也將看到這個問題含糊性的一面。如果你把這個理由告訴面試官,他會問為什么 C++ 可以支持多重繼承而 Java不行。嗯,在這種情況下,我會試著向他解釋我下面給出的第二個原因,它不是因為技術難度, 而是更多的可維護和更清晰的設計是驅(qū)動因素, 雖然這只能由 Java 言語設計師確認,我們只是推測。維基百科鏈接有一些很好的解釋,說明在使用多重繼承時,由于鉆石問題,不同的語言地址問題是如何產(chǎn)生的。

(2)對我來說第二個也是更有說服力的理由是,多重繼承確實使設計復雜化并在轉換、構造函數(shù)鏈接等過程中產(chǎn)生問題。假設你需要多重繼承的情況并不多,簡單起見,明智的決定是省略它。此外,Java 可以通過使用接口支持單繼承來避免這種歧義。由于接口只有方法聲明而且沒有提供任何實現(xiàn),因此只有一個特定方法的實現(xiàn),因此不會有任何歧義。

3. 為什么Java不支持運算符重載?

另一個類似棘手的Java問題。為什么 C++ 支持運算符重載而 Java 不支持? 有人可能會說+運算符在 Java 中已被重載用于字符串連接,不要被這些論據(jù)所欺騙。

與 C++ 不同,Java 不支持運算符重載。Java 不能為程序員提供自由的標準算術運算符重載,例如+, - ,*和/等。如果你以前用過 C++,那么 Java 與 C++ 相比少了很多功能,例如 Java 不支持多重繼承,Java中沒有指針,Java中沒有引用傳遞。另一個類似的問題是關于 Java 通過引用傳遞,這主要表現(xiàn)為 Java 是通過值還是引用傳參。雖然我不知道背后的真正原因,但我認為以下說法有些道理,為什么 Java 不支持運算符重載。

(1)簡單性和清晰性。清晰性是Java設計者的目標之一。設計者不是只想復制語言,而是希望擁有一種清晰,真正面向?qū)ο蟮恼Z言。添加運算符重載比沒有它肯定會使設計更復雜,并且它可能導致更復雜的編譯器, 或減慢 JVM,因為它需要做額外的工作來識別運算符的實際含義,并減少優(yōu)化的機會, 以保證 Java 中運算符的行為。

(2)避免編程錯誤。Java 不允許用戶定義的運算符重載,因為如果允許程序員進行運算符重載,將為同一運算符賦予多種含義,這將使任何開發(fā)人員的學習曲線變得陡峭,事情變得更加混亂。據(jù)觀察,當語言支持運算符重載時,編程錯誤會增加,從而增加了開發(fā)和交付時間。由于 Java 和 JVM 已經(jīng)承擔了大多數(shù)開發(fā)人員的責任,如在通過提供垃圾收集器進行內(nèi)存管理時,因為這個功能增加污染代碼的機會, 成為編程錯誤之源, 因此沒有多大意義。

(3)JVM復雜性。從JVM的角度來看,支持運算符重載使問題變得更加困難。通過更直觀,更干凈的方式使用方法重載也能實現(xiàn)同樣的事情,因此不支持 Java 中的運算符重載是有意義的。與相對簡單的 JVM 相比,復雜的 JVM 可能導致 JVM 更慢,并為保證在 Java 中運算符行為的確定性從而減少了優(yōu)化代碼的機會。

(4)讓開發(fā)工具處理更容易。這是在 Java 中不支持運算符重載的另一個好處。省略運算符重載使語言更容易處理,這反過來又更容易開發(fā)處理語言的工具,例如 IDE 或重構工具。Java 中的重構工具遠勝于 C++。

4. 為什么 String 在 Java 中是不可變的?

我最喜歡的 Java 面試問題,很棘手,但同時也非常有用。一些面試者也常問這個問題,為什么 String 在 Java 中是 final 的。

字符串在 Java 中是不可變的,因為 String 對象緩存在 String 池中。由于緩存的字符串在多個客戶之間共享,因此始終存在風險,其中一個客戶的操作會影響所有其他客戶。例如,如果一段代碼將 String “Test” 的值更改為 “TEST”,則所有其他客戶也將看到該值。由于 String 對象的緩存性能是很重要的一方面,因此通過使 String 類不可變來避免這種風險。

同時,String 是 final 的,因此沒有人可以通過擴展和覆蓋行為來破壞 String 類的不變性、緩存、散列值的計算等。String 類不可變的另一個原因可能是由于 HashMap。

由于把字符串作為 HashMap 鍵很受歡迎。對于鍵值來說,重要的是它們是不可變的,以便用它們檢索存儲在 HashMap 中的值對象。由于 HashMap 的工作原理是散列,因此需要具有相同的值才能正常運行。如果在插入后修改了 String 的內(nèi)容,可變的 String將在插入和檢索時生成兩個不同的哈希碼,可能會丟失 Map 中的值對象。

如果你是印度板球迷,你可能能夠與我的下一句話聯(lián)系起來。字符串是Java的 VVS Laxman,即非常特殊的類。我還沒有看到一個沒有使用 String 編寫的 Java 程序。這就是為什么對 String 的充分理解對于 Java 開發(fā)人員來說非常重要。

String 作為數(shù)據(jù)類型,傳輸對象和中間人角色的重要性和流行性也使這個問題在 Java 面試中很常見。

為什么 String 在 Java 中是不可變的是 Java 中最常被問到的字符串訪問問題之一,它首先討論了什么是 String,Java 中的 String 如何與 C 和 C++ 中的 String 不同,然后轉向在Java中什么是不可變對象,不可變對象有什么好處,為什么要使用它們以及應該使用哪些場景。這個問題有時也會問:“為什么 String 在 Java 中是 final 的”。在類似的說明中,如果你正在準備Java 面試,我建議你看看Java編程面試公開書,這是高級和中級Java程序員的優(yōu)秀資源。它包含來自所有重要 Java 主題的問題,包括多線程,集合,GC,JVM內(nèi)部以及 Spring和 Hibernate 框架等。

正如我所說,這個問題可能有很多可能的答案,而 String 類的唯一設計者可以放心地回答它。我在 Joshua Bloch 的 Effective Java 書中期待一些線索,但他也沒有提到它。我認為以下幾點解釋了為什么 String 類在 Java 中是不可變的或 final 的:

(1)想象字符串池沒有使字符串不可變,它根本不可能,因為在字符串池的情況下,一個字符串對象/文字,例如 “Test” 已被許多參考變量引用,因此如果其中任何一個更改了值,其他參數(shù)將自動受到影響,即假設

String A="Test";
String B="Test";

現(xiàn)在字符串 B 調(diào)用 "Test".toUpperCase(), 將同一個對象改為“TEST”,所以 A 也是 “TEST”,這不是期望的結果。

下圖顯示了如何在堆內(nèi)存和字符串池中創(chuàng)建字符串。

(2)字符串已被廣泛用作許多 Java 類的參數(shù),例如,為了打開網(wǎng)絡連接,你可以將主機名和端口號作為字符串傳遞,你可以將數(shù)據(jù)庫 URL 作為字符串傳遞, 以打開數(shù)據(jù)庫連接,你可以通過將文件名作為參數(shù)傳遞給 File I/O 類來打開 Java 中的任何文件。如果 String 不是不可變的,這將導致嚴重的安全威脅,我的意思是有人可以訪問他有權授權的任何文件,然后可以故意或意外地更改文件名并獲得對該文件的訪問權限。由于不變性,你無需擔心這種威脅。這個原因也說明了,為什么 String 在 Java 中是最終的,通過使 java.lang.String final,Java設計者確保沒有人覆蓋 String 類的任何行為。

(3)由于 String 是不可變的,它可以安全地共享許多線程,這對于多線程編程非常重要. 并且避免了 Java 中的同步問題,不變性也使得String 實例在 Java 中是線程安全的,這意味著你不需要從外部同步 String 操作。關于 String 的另一個要點是由截取字符串 SubString 引起的內(nèi)存泄漏,這不是與線程相關的問題,但也是需要注意的。

(4)為什么 String 在 Java 中是不可變的另一個原因是允許 String 緩存其哈希碼,Java 中的不可變 String 緩存其哈希碼,并且不會在每次調(diào)用 String 的 hashcode 方法時重新計算,這使得它在 Java 中的 HashMap 中使用的 HashMap 鍵非常快。簡而言之,因為 String 是不可變的,所以沒有人可以在創(chuàng)建后更改其內(nèi)容,這保證了 String 的 hashCode 在多次調(diào)用時是相同的。

(5)String 不可變的絕對最重要的原因是它被類加載機制使用,因此具有深刻和基本的安全考慮。如果 String 是可變的,加載“java.io.Writer” 的請求可能已被更改為加載 “mil.vogoon.DiskErasingWriter”. 安全性和字符串池是使字符串不可變的主要原因。順便說一句,上面的理由很好回答另一個Java面試問題: “為什么String在Java中是最終的”。要想是不可變的,你必須是最終的,這樣你的子類不會破壞不變性。你怎么看?

5.為什么 char 數(shù)組比 Java 中的 String 更適合存儲密碼?

另一個基于 String 的棘手 Java 問題,相信我只有很少的 Java 程序員可以正確回答這個問題。這是一個真正艱難的核心Java面試問題,并且需要對 String 的扎實知識才能回答這個問題。

這是最近在 Java 面試中向我的一位朋友詢問的問題。他正在接受技術主管職位的面試,并且有超過6年的經(jīng)驗。如果你還沒有遇到過這種情況,那么字符數(shù)組和字符串可以用來存儲文本數(shù)據(jù),但是選擇一個而不是另一個很難。但正如我的朋友所說,任何與 String 相關的問題都必須對字符串的特殊屬性有一些線索,比如不變性,他用它來說服訪提問的人。在這里,我們將探討為什么你應該使用char[]存儲密碼而不是String的一些原因。

字符串:

(1)由于字符串在 Java 中是不可變的,如果你將密碼存儲為純文本,它將在內(nèi)存中可用,直到垃圾收集器清除它. 并且為了可重用性,會存在 String 在字符串池中, 它很可能會保留在內(nèi)存中持續(xù)很長時間,從而構成安全威脅。

由于任何有權訪問內(nèi)存轉儲的人都可以以明文形式找到密碼,這是另一個原因,你應該始終使用加密密碼而不是純文本。由于字符串是不可變的,所以不能更改字符串的內(nèi)容,因為任何更改都會產(chǎn)生新的字符串,而如果你使用char[],你就可以將所有元素設置為空白或零。因此,在字符數(shù)組中存儲密碼可以明顯降低竊取密碼的安全風險。

(2)Java 本身建議使用 JPasswordField 的 getPassword() 方法,該方法返回一個 char[] 和不推薦使用的getTex() 方法,該方法以明文形式返回密碼,由于安全原因。應遵循 Java 團隊的建議, 堅持標準而不是反對它。

(3)使用 String 時,總是存在在日志文件或控制臺中打印純文本的風險,但如果使用 Array,則不會打印數(shù)組的內(nèi)容而是打印其內(nèi)存位置。雖然不是一個真正的原因,但仍然有道理。

    String strPassword =“Unknown”; 
    char [] charPassword = new char [] {'U','n','k','w','o','n'}; 
    System.out.println(“字符密碼:”+ strPassword);
    System.out.println(“字符密碼:”+ charPassword);

輸出

字符串密碼:Unknown

字符密碼:[C @110b053

我還建議使用散列或加密的密碼而不是純文本,并在驗證完成后立即從內(nèi)存中清除它。因此,在Java中,用字符數(shù)組用存儲密碼比字符串是更好的選擇。雖然僅使用char[]還不夠,還你需要擦除內(nèi)容才能更安全。

6. 如何使用雙重檢查鎖定在 Java 中創(chuàng)建線程安全的單例?

艱難的核心 Java 面試問題.這個 Java 問題也常被問: 什么是線程安全的單例,你怎么創(chuàng)建它。好吧,在Java 5之前的版本, 使用雙重檢查鎖定創(chuàng)建單例 Singleton 時,如果多個線程試圖同時創(chuàng)建 Singleton 實例,則可能有多個 Singleton 實例被創(chuàng)建。從 Java 5 開始,使用 Enum 創(chuàng)建線程安全的Singleton很容易。但如果面試官堅持雙重檢查鎖定,那么你必須為他們編寫代碼。記得使用volatile變量。

為什么枚舉單例在 Java 中更好

枚舉單例是使用一個實例在 Java 中實現(xiàn)單例模式的新方法。雖然Java中的單例模式存在很長時間,但枚舉單例是相對較新的概念,在引入Enum作為關鍵字和功能之后,從Java5開始在實踐中。本文與之前關于 Singleton 的內(nèi)容有些相關, 其中討論了有關 Singleton 模式的面試中的常見問題, 以及 10 個 Java 枚舉示例, 其中我們看到了如何通用枚舉可以。這篇文章是關于為什么我們應該使用Eeame作為Java中的單例,它比傳統(tǒng)的單例方法相比有什么好處等等。

Java 枚舉和單例模式

Java 中的枚舉單例模式是使用枚舉在 Java 中實現(xiàn)單例模式。單例模式在 Java 中早有應用, 但使用枚舉類型創(chuàng)建單例模式時間卻不長. 如果感興趣, 你可以了解下構建者設計模式和裝飾器設計模式。

(1)枚舉單例易于書寫

這是迄今為止最大的優(yōu)勢,如果你在Java 5之前一直在編寫單例, 你知道, 即使雙檢查鎖定, 你仍可以有多個實例。雖然這個問題通過 Java 內(nèi)存模型的改進已經(jīng)解決了, 從 Java 5 開始的 volatile 類型變量提供了保證, 但是對于許多初學者來說, 編寫起來仍然很棘手。與同步雙檢查鎖定相比,枚舉單例實在是太簡單了。如果你不相信, 那就比較一下下面的傳統(tǒng)雙檢查鎖定單例和枚舉單例的代碼:

在 Java 中使用枚舉的單例

這是我們通常聲明枚舉的單例的方式,它可能包含實例變量和實例方法,但為了簡單起見,我沒有使用任何實例方法,只是要注意,如果你使用的實例方法且該方法能改變對象的狀態(tài)的話, 則需要確保該方法的線程安全。默認情況下,創(chuàng)建枚舉實例是線程安全的,但 Enum 上的任何其他方法是否線程安全都是程序員的責任。

/**
* 使用 Java 枚舉的單例模式示例
*/
public enum EasySingleton{
    INSTANCE;
}

你可以通過EasySingleton.INSTANCE來處理它,這比在單例上調(diào)用getInstance()方法容易得多。

具有雙檢查鎖定的單例示例

下面的代碼是單例模式中雙重檢查鎖定的示例,此處的 getInstance() 方法檢查兩次,以查看 INSTANCE 是否為空,這就是為什么它被稱為雙檢查鎖定模式,請記住,雙檢查鎖定是代理之前Java 5,但Java5內(nèi)存模型中易失變量的干擾,它應該工作完美。

/**
* 單例模式示例,雙重鎖定檢查
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;  
     private DoubleCheckedLockingSingleton(){}  
     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

你可以調(diào)用DoubleCheckedLockingSingleton.getInstance() 來獲取此單例類的訪問權限。

現(xiàn)在,只需查看創(chuàng)建延遲加載的線程安全的 Singleton 所需的代碼量。使用枚舉單例模式, 你可以在一行中具有該模式, 因為創(chuàng)建枚舉實例是線程安全的, 并且由 JVM 進行。

人們可能會爭辯說,有更好的方法來編寫 Singleton 而不是雙檢查鎖定方法, 但每種方法都有自己的優(yōu)點和缺點, 就像我最喜歡在類加載時創(chuàng)建的靜態(tài)字段 Singleton, 如下面所示, 但請記住, 這不是一個延遲加載單例:

單例模式用靜態(tài)工廠方法

這是我最喜歡的在 Java 中影響 Singleton 模式的方法之一,因為 Singleton 實例是靜態(tài)的,并且最后一個變量在類首次加載到內(nèi)存時初始化,因此實例的創(chuàng)建本質(zhì)上是線程安全的。

/**
* 單例模式示例與靜態(tài)工廠方法
*/
public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();  
    //to prevent creating another instance of Singleton
    private Singleton(){}
    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

你可以調(diào)用 Singleton.getSingleton() 來獲取此類的訪問權限。

(2)枚舉單例自行處理序列化

傳統(tǒng)單例的另一個問題是,一旦實現(xiàn)可序列化接口,它們就不再是 Singleton, 因為 readObject() 方法總是返回一個新實例, 就像 Java 中的構造函數(shù)一樣。通過使用 readResolve() 方法, 通過在以下示例中替換 Singeton 來避免這種情況:

//readResolve to prevent another instance of Singleton
private Object readResolve(){
    return INSTANCE;
}

如果 Singleton 類保持內(nèi)部狀態(tài), 這將變得更加復雜, 因為你需要標記為 transient(不被序列化),但使用枚舉單例, 序列化由 JVM 進行。

(3)創(chuàng)建枚舉實例是線程安全的

如第 1 點所述,因為 Enum 實例的創(chuàng)建在默認情況下是線程安全的, 你無需擔心是否要做雙重檢查鎖定。

總之, 在保證序列化和線程安全的情況下,使用兩行代碼枚舉單例模式是在 Java 5 以后的世界中創(chuàng)建 Singleton 的最佳方式。

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 中文字幕一区二区视频 | 国产做爰免费视频观看 | 五月天婷婷在线播放 | 99热这里只有精品8 99热这里只有精品88 | 精品国产呦系列在线看 | 成人私人影院在线观看网址 | 天天射夜夜骑 | 久久久视频在线 | 久久久青青久久国产精品 | 日韩在线观看一区二区三区 | 久夜色精品国产一区二区三区 | 婷婷六月色 | 国产成人精品2021欧美日韩 | 欧美日韩国产一区三区 | 精品哟哟国产在线观看 | 国产原创精品 | 殴美一级片 | 99热久久精品最新 | 狠狠久久亚洲欧美专区 | 天然素人在线观看 | 亚洲一区欧洲一区 | 国产一区二区三区成人久久片 | 国产精品亚洲精品 | 免费观看一级欧美大 | 天天操精品 | xxx中国bbbwww| 欧美精品亚洲精品日韩专区va | 国产精品一区三区 | 国产精品精品国产一区二区 | 四虎影视永久地址www成人 | 国产高清一区二区三区四区 | 欧美午夜寂寞影院安卓列表 | 亚洲成人网在线观看 | 欧美另类久久久精品 | 丁香狠狠色婷婷久久综合 | 精品免费国产一区二区女 | 色另类 | 亚洲精品mm1313久久 | 极品精品国产超清自在线观看 | 涩涩视频免费看 | 九九九好热在线 |