Java面試題全集上(2)

Java面試題全集上(2)

接着上次的面試題我們繼續

  Java 中會存在內存泄漏嗎,請簡單描述。

      答:理論上Java因爲有垃圾回收機制(GC)不會存在內存泄露問題(這也是Java被廣泛使用於服務器端編程的一個重要原因);然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被GC回收,因此也會導致內存泄露的發生。例如Hibernate的Session(一級緩存)中的對象屬於持久態,垃圾回收器是不會回收這些對象的,然而這些對象中可能存在無用的垃圾對象,如果不及時關閉(close)或清空(flush)一級緩存就可能導致內存泄露。

抽象的(abstract)方法是否可同時是靜態的(static),是否可同時是本地方法(native),是否可同時被synchronized修飾?

答:都不能。抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。本地方法是由本地代碼(如C代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。synchronized和方法的實現細節有關,抽象方法不涉及實現細節,因此也是相互矛盾的。

闡述靜態變量和實例變量的區別

答:靜態變量是被static修飾符修飾的變量,也稱爲類變量,它屬於類,不屬於類的任何一個對象,一個類不管創建多少個對象,靜態變量在內存中有且僅有一個拷貝;實例變量必須依存於某一實例,需要先創建對象然後通過對象才能訪問到它。靜態變量可以實現讓多個對象共享內存。

補充:在Java開發中,上下文類和工具類中通常會有大量的靜態成員。

是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的調用? 

答:不可以,靜態方法只能訪問靜態成員,因爲非靜態方法的調用要先創建對象,在調用靜態方法時可能對象並沒有被初始化。

如何實現對象克隆?

答:有兩種方式: 
  1). 實現Cloneable接口並重寫Object類中的clone()方法; 
  2). 實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆

注意:基於序列化和反序列化實現的克隆不僅僅是深度克隆,更重要的是通過泛型限定,可以檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出異常,這種是方案明顯優於使用Object類的clone方法克隆對象。讓問題在編譯的時候暴露出來總是好過把問題留到運行時。

GC是什麼?爲什麼要有GC? 

答:GC是垃圾收集的意思,內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會導致程序或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測對象是否超過作用域從而達到自動回收內存的目的,Java語言沒有提供釋放已分配內存的顯示操作方法。Java程序員不用擔心內存管理,因爲垃圾收集器會自動進行管理。要請求垃圾收集,可以調用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉顯示的垃圾回收調用。 
       垃圾回收可以有效的防止內存泄露,有效的使用可以使用的內存。垃圾回收器通常是作爲一個單獨的低優先級的線程運行,不可預知的情況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回收器對某個對象或所有對象進行垃圾回收。在Java誕生初期,垃圾回收是Java最大的亮點之一,因爲服務器端的編程需要有效的防止內存泄露問題,然而時過境遷,如今Java的垃圾回收機制已經成爲被詬病的東西。移動智能終端用戶通常覺得iOS的系統比Android系統有更好的用戶體驗,其中一個深層次的原因就在於Android系統中垃圾回收的不可預知性。

       補充:垃圾回收機制有很多種,包括:分代複製垃圾回收、標記垃圾回收、增量垃圾回收等方式。標準的Java進程既有棧又有堆。棧保存了原始型局部變量,堆保存了要創建的對象。Java平臺對堆內存回收和再利用的基本算法被稱爲標記和清除,但是Java對其進行了改進,採用“分代式垃圾收集”。這種方法會跟Java對象的生命週期將堆內存劃分爲不同的區域,在垃圾收集過程中,可能會將對象移動到不同區域: 
- 伊甸園(Eden):這是對象最初誕生的區域,並且對大多數對象來說,這裏是它們唯一存在過的區域。 
- 倖存者樂園(Survivor):從伊甸園倖存下來的對象會被挪到這裏。 
- 終身頤養園(Tenured):這是足夠老的倖存對象的歸宿。年輕代收集(Minor-GC)過程是不會觸及這個地方的。當年輕代收集不能把對象放進終身頤養園時,就會觸發一次完全收集(Major-GC),這裏可能還會牽扯到壓縮,以便爲大對象騰出足夠的空間。

與垃圾回收相關的JVM參數:

-Xms / -Xmx — 堆的初始大小 / 堆的最大大小

-Xmn — 堆中年輕代的大小

-XX:-DisableExplicitGC — 讓System.gc()不產生任何作用

-XX:+PrintGCDetails — 打印GC的細節

-XX:+PrintGCDateStamps — 打印GC操作的時間戳

-XX:NewSize / XX:MaxNewSize — 設置新生代大小/新生代最大大小

-XX:NewRatio — 可以設置老生代和新生代的比例

-XX:PrintTenuringDistribution — 設置每次新生代GC後輸出倖存者樂園中對象年齡的分佈

-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:設置老年代閥值的初始值和最大值

-XX:TargetSurvivorRatio:設置倖存區的目標使用率


String s = new String("xyz");創建了幾個字符串對象? 

答:兩個對象,一個是靜態區的"xyz",一個是用new創建在堆上的對象。

接口是否可繼承(extends)接口?抽象類是否可實現(implements)接口?抽象類是否可繼承具體類(concrete class)?

答:接口可以繼承接口,而且支持多重繼承。抽象類可以實現(implements)接口,抽象類可繼承具體類也可以繼承抽象類

一個".java"源文件中是否可以包含多個類(不是內部類)?有什麼限制? 

答:可以,但一個源文件中最多隻能有一個公開類(public class)而且文件名必須和公開類的類名完全保持一致。

Anonymous Inner Class(匿名內部類)是否可以繼承其它類?是否可以實現接口?

答:可以繼承其他類或實現其他接口,在Swing編程和Android開發中常用此方式來實現事件監聽和回調。

內部類可以引用它的包含類(外部類)的成員嗎?有沒有什麼限制? 

答:一個內部類對象可以訪問創建它的外部類對象的成員,包括私有成員。

Java 中的final關鍵字有哪些用法? 

答:(1)修飾類:表示該類不能被繼承;

(2)修飾方法:表示方法不能被重寫;

(3)修飾變量:表示變量只能一次賦值以後值不能被修改(常量)

指出下面程序的運行結果。

class A {   

 static {     

   System.out.print("1");  

  }   

 public A() {    

    System.out.print("2");   

 }}

class B extends A{   

 static {       

 System.out.print("a");  

  }    

public B() {      

  System.out.print("b");   

 }}

public class Hello { 

    public static void main(String[] args) {        A ab = new B();     

   ab = new B();

    }


答:執行結果:1a2b2b。創建對象時構造器的調用順序是:先初始化靜態成員,然後調用父類構造器,再初始化非靜態成員,最後調用自身構造器。

數據類型之間的轉換: 
- 如何將字符串轉換爲基本數據類型? 
- 如何將基本數據類型轉換爲字符串? 

答: 
- 調用基本數據類型對應的包裝類中的方法parseXXX(String)或valueOf(String)即可返回相應基本類型; 
- 一種方法是將基本數據類型與空字符串("")連接(+)即可獲得其所對應的字符串;另一種方法是調用String 類中的valueOf()方法返回相應字符串

如何實現字符串的反轉及替換? 

答:方法很多,可以自己寫實現也可以使用String或StringBuffer/StringBuilder中的方法。有一道很常見的面試題是用遞歸實現字符串反轉

怎樣將GB2312編碼的字符串轉換爲ISO-8859-1編碼的字符串

答:代碼如下所示:

String s1 = "你好";String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");

比較一下Java和JavaSciprt。 

答:JavaScript 與Java是兩個公司開發的不同的兩個產品。Java 是原Sun Microsystems公司推出的面向對象的程序設計語言,特別適合於互聯網應用程序開發;而JavaScript是Netscape公司的產品,爲了擴展Netscape瀏覽器的功能而開發的一種可以嵌入Web頁面中運行的基於對象和事件驅動的解釋性語言。JavaScript的前身是LiveScript;而Java的前身是Oak語言。 
下面對兩種語言間的異同作如下比較: 
- 基於對象和麪向對象:Java是一種真正的面向對象的語言,即使是開發簡單的程序,必須設計對象;JavaScript是種腳本語言,它可以用來製作與網絡無關的,與用戶交互作用的複雜軟件。它是一種基於對象(Object-Based)和事件驅動(Event-Driven)的編程語言,因而它本身提供了非常豐富的內部對象供設計人員使用。 
- 解釋和編譯:Java的源代碼在執行之前,必須經過編譯。JavaScript是一種解釋性編程語言,其源代碼不需經過編譯,由瀏覽器解釋執行。(目前的瀏覽器幾乎都使用了JIT(即時編譯)技術來提升JavaScript的運行效率) 
- 強類型變量和類型弱變量:Java採用強類型變量檢查,即所有變量在編譯之前必須作聲明;JavaScript中變量是弱類型的,甚至在使用變量前可以不作聲明,JavaScript的解釋器在運行時檢查推斷其數據類型。 
- 代碼格式不一樣。

補充:上面列出的四點是網上流傳的所謂的標準答案。其實Java和JavaScript最重要的區別是一個是靜態語言,一個是動態語言。目前的編程語言的發展趨勢是函數式語言和動態語言。在Java中類(class)是一等公民,而JavaScript中函數(function)是一等公民,因此JavaScript支持函數式編程,可以使用Lambda函數和閉包(closure),當然Java 8也開始支持函數式編程,提供了對Lambda表達式以及函數式接口的支持。對於這類問題,在面試的時候最好還是用自己的語言回答會更加靠譜,不要背網上所謂的標準答案。

try{}裏有一個return語句,那麼緊跟在這個try後的finally{}裏的代碼會不會被執行,什麼時候被執行,在return前還是後? 

答:會執行,在方法返回調用者前執行。

注意:在finally中改變返回值的做法是不好的,因爲如果存在finally代碼塊,try中的return語句不會立馬返回調用者,而是記錄下返回值待finally代碼塊執行完畢之後再向調用者返回其值,然後如果在finally中修改了返回值,就會返回修改後的值。顯然,在finally中返回或者修改返回值會對程序造成很大的困擾,C#中直接用編譯錯誤的方式來阻止程序員幹這種齷齪的事情,Java中也可以通過提升編譯器的語法檢查級別來產生警告或錯誤,Eclipse中可以在如圖所示的地方進行設置,強烈建議將此項設置爲編譯錯誤。

Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally分別如何使用?

答:Java通過面向對象的方法進行異常處理,把各種不同的異常進行分類,並提供了良好的接口。在Java中,每個異常都是一個對象,它是Throwable類或其子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法可以捕獲到這個異常並可以對其進行處理。Java的異常處理是通過5個關鍵詞來實現的:try、catch、throw、throws和finally。一般情況下是用try來執行一段程序,如果系統會拋出(throw)一個異常對象,可以通過它的類型來捕獲(catch)它,或通過總是執行代碼塊(finally)來處理;try用來指定一塊預防所有異常的程序;catch子句緊跟在try塊後面,用來指定你想要捕獲的異常的類型;throw語句用來明確地拋出一個異常;throws用來聲明一個方法可能拋出的各種異常(當然聲明異常時允許無病呻吟);finally爲確保一段代碼不管發生什麼異常狀況都要被執行;try語句可以嵌套,每當遇到一個try語句,異常的結構就會被放入異常棧中,直到所有的try語句都完成。如果下一級的try語句沒有對某種異常進行處理,異常棧就會執行出棧操作,直到遇到有處理這種異常的try語句或者最終將異常拋給JVM。

運行時異常與受檢異常有何異同? 

答:異常表示程序運行過程中可能出現的非正常狀態,運行時異常表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題通常就不會發生。受檢異常跟程序運行的上下文環境有關,即使程序設計無誤,仍然可能因使用的問題而引發。Java編譯器要求方法必須聲明拋出可能發生的受檢異常,但是並不要求必須聲明拋出未被捕獲的運行時異常。異常和繼承一樣,是面向對象程序設計中經常被濫用的東西,在Effective Java中對異常的使用給出了以下指導原則: 
- 不要將異常處理用於正常的控制流(設計良好的API不應該強迫它的調用者爲了正常的控制流而使用異常) 
- 對可以恢復的情況使用受檢異常,對編程錯誤使用運行時異常 
- 避免不必要的使用受檢異常(可以通過一些狀態檢測手段來避免異常的發生) 
- 優先使用標準的異常 
- 每個方法拋出的異常都要有文檔 
- 保持異常的原子性 
- 不要在catch中忽略掉捕獲到的異常

列出一些你常見的運行時異常?

- ArithmeticException(算術異常) 
- ClassCastException (類轉換異常) 
- IllegalArgumentException (非法參數異常) 
- IndexOutOfBoundsException (下標越界異常) 
- NullPointerException (空指針異常) 
- SecurityException (安全異常)

闡述final、finally、finalize的區別。 

答: 
- final:修飾符(關鍵字)有三種用法:如果一個類被聲明爲final,意味着它不能再派生出新的子類,即不能被繼承,因此它和abstract是反義詞。將變量聲明爲final,可以保證它們在使用中不被改變,被聲明爲final的變量必須在聲明時給定初值,而在以後的引用中只能讀取不可修改。被聲明爲final的方法也同樣只能使用,不能在子類中被重寫。 
- finally:通常放在try…catch…的後面構造總是執行代碼塊,這就意味着程序無論正常執行還是發生異常,這裏的代碼只要JVM不關閉都能執行,可以將釋放外部資源的代碼寫在finally塊中。 
- finalize:Object類中定義的方法,Java中允許使用finalize()方法在垃圾收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在銷燬對象時調用的,通過重寫finalize()方法可以整理系統資源或者執行其他清理工作。

想要了解更多😊

那就關注我們的訂閱號吧!!!

  

本文分享自微信公衆號 - 小白技術社(xbjss123)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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