「JAVA秒會技術之秒殺面試官」JavaSE常見面試題(二)

21.在Java中,如何跳出當前的多重嵌套循環?

答:在最外層循環前加一個標記如A,然後用break A;可以跳出多重循環。(Java中支持帶標籤的break和continue語句,作用有點類似於C和C++中的goto語句,但是就像要避免使用goto一樣,應該避免使用帶標籤的break和continue,因為它不會讓你的程序變得更優雅,很多時候甚至有相反的作用,所以這種語法其實不知道更好)

22.構造器(constructor)是否可被重寫(override)?

答:構造器不能被繼承,因此不能被重寫,但可以被重載。

23.兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?

答:不對,如果兩個對象x和y滿足x.equals(y) == true,它們的哈希碼(hash code)應當相同。Java對於eqauls方法和hashCode方法是這樣規定的:

(1)如果兩個對象相同(equals方法返回true),那麼它們的hashCode值一定要相同;

(2)如果兩個對象的hashCode相同,它們並不一定相同。

當然,你未必要按照要求去做,但是如果你違背了上述原則就會發現在使用容器時,相同的對象可以出現在Set集合中,同時增加新元素的效率會大大下降(對於使用哈希存儲的系統,如果哈希碼頻繁的衝突將會造成存取性能急劇下降)。

補充:關於equals和hashCode方法,很多Java程序都知道,但很多人也就是僅僅知道而已,在Joshua Bloch的大作《Effective Java》(很多軟件公司,《Effective Java》、《Java編程思想》以及《重構:改善既有代碼質量》是Java程序員必看書籍,如果你還沒看過,那就趕緊去亞馬遜買一本吧)中是這樣介紹equals方法的:首先equals方法必須滿足自反性(x.equals(x)必須返回true)、對稱性(x.equals(y)返回true時,y.equals(x)也必須返回true)、傳遞性(x.equals(y)和y.equals(z)都返回true時,x.equals(z)也必須返回true)和一致性(當x和y引用的對象信息沒有被修改時,多次調用x.equals(y)應該得到同樣的返回值),而且對於任何非null值的引用x,x.equals(null)必須返回false。實現高質量的equals方法的訣竅包括:1.使用==操作符檢查"參數是否為這個對象的引用";2.使用instanceof操作符檢查"參數是否為正確的類型";3.對於類中的關鍵屬性,檢查參數傳入對象的屬性是否與之相匹配;4.編寫完equals方法後,問自己它是否滿足對稱性、傳遞性、一致性;5.重寫equals時總是要重寫hashCode;6.不要將equals方法參數中的Object對象替換為其他的類型,在重寫時不要忘掉@Override註解。

24.是否可以繼承String類?

答:String 類是final類,不可以被繼承。

補充:繼承String本身就是一個錯誤的行為,對String類型最好的重用方式是關聯關係(Has-A)和依賴關係(Use-A)而不是繼承關係(Is-A)。

25.當一個對象被當作參數傳遞到一個方法後,此方法可改變這個對象的屬性,並可返回變化後的結果,那麼這裡到底是值傳遞還是引用傳遞?

答:是值傳遞。Java語言的方法調用只支持參數的值傳遞。當一個對象實例作為一個參數被傳遞到方法中時,參數的值就是對該對象的引用。對象的屬性可以在被調用過程中被改變,但對對象引用的改變是不會影響到調用者的。C++和C#中可以通過傳引用或傳輸出參數來改變傳入的參數的值。在C#中可以編寫如下所示的代碼,但是在Java中卻做不到。

namespace CS01 {

class Program {

public static void swap(ref int x, ref int y) {

int temp = x;

x = y;

y = temp;

}

public static void Main (string[] args) {

int a = 5, b = 10;

swap (ref a, ref b);

// a = 10, b = 5;

Console.WriteLine ("a = {0}, b = {1}", a, b);

}

}

}

說明:Java中沒有傳引用實在是非常的不方便,這一點在Java 8中仍然沒有得到改進,正是如此在Java編寫的代碼中才會出現大量的Wrapper類(將需要通過方法調用修改的引用置於一個Wrapper類中,再將Wrapper對象傳入方法),這樣的做法只會讓代碼變得臃腫,尤其是讓從C和C++轉型為Java程序員的開發者無法容忍。

26.String和StringBuilder、StringBuffer的區別?

答:(1)String和StringBuffer、StringBuider:

①String:是不可變字符序列;

②StringBuffer、StringBuider:是可變字符序列;

③String覆蓋了equals方法和hashCode方法,而StringBuffer沒有覆蓋equals方法和hashCode方法,所以,將StringBuffer對象存儲進Java集合類中時會出現問題。

(2)StringBuffer和StringBuider:

①StringBuffer:是JDK1.0版本的,線程安全,效率低;

②StringBuilder:是JDK1.5版本的,線程不安全,效率高;

③如果一個字符串變量是在方法裡面定義,這種情況只可能有一個線程訪問它,不存在不安全的因素了,則用StringBuilder。如果要在類裡面定義成員變量,並且這個類的實例對象會在多線程環境下使用,那麼最好用StringBuffer。

27.描述一下JVM加載class文件的原理機制?

答:JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。

由於Java的跨平臺性,經過編譯的Java源程序並不是一個可執行程序,而是一個或多個類文件。當Java程序需要使用某個類時,JVM會確保這個類已經被加載、連接(驗證、準備和解析)和初始化。類的加載是指把類的.class文件中的數據讀入到內存中,通常是創建一個字節數組讀入.class文件,然後產生與所加載類對應的Class對象。加載完成後,Class對象還不完整,所以此時的類還不可用。當類被加載後就進入連接階段,這一階段包括驗證、準備(為靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換為直接引用)三個步驟。最後JVM對類進行初始化,包括:1)如果類存在直接的父類並且這個類還沒有被初始化,那麼就先初始化父類;2)如果類中存在初始化語句,就依次執行這些初始化語句。

類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。從Java 2(JDK 1.2)開始,類加載過程採取了父親委託機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載。JVM不會向Java程序提供對Bootstrap的引用。下面是關於幾個類加載器的說明:

Bootstrap:一般用本地代碼實現,負責加載JVM基礎核心類庫(rt.jar);

Extension:從java.ext.dirs系統屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap;

System:又叫應用類加載器,其父類是Extension。它是應用最廣泛的類加載器。它從環境變量classpath或者系統屬性java.class.path所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。

28.抽象類(abstract class)和接口(interface)有什麼異同?

答:(1)成員特點:

①構造方法:接口沒有;抽象類有;

②成員變量:接口中只有常量;抽象類中常量、變量都可;

③成員方法:接口只有抽象方法;抽象類中抽象方法、非抽象方法都可;

(2)關係特點:

①類與類:只有單繼承,但可以多層繼承;

②類與接口:實現關係,可以單實現,也可以多實現;

③接口與接口:繼承關係,可以單繼承,也可以多繼承;

(3)設計理念:

①接口是簡單工廠設計模式,like a的關係 ,接口中定義的是該繼承體系的擴展功能;

③抽象類是模板設計模式,is a的關係,抽象類中定義的是繼承體系的共性功能;

29.Java中會存在內存洩漏嗎,請簡單描述?

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

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

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

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

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

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

答:GC是垃圾收集的意思,Java提供的GC功能可以自動監測對象是否超過作用域從而達到自動回收內存的目的,從而有效的防止內存洩露。要請求垃圾收集,可以調用下面的方法之一:System.gc()或Runtime.getRuntime().gc(),但JVM可以屏蔽掉顯示的垃圾回收調用。

33.String st = null和 String st =“ ”的區別?String s = new String("xyz")創建了幾個對象?

答:(1)String st = null表示聲明瞭一個String對象的引用str,但是沒有為其分配存內存空間。

(2)String st =“ ”表示創建了一個長度等於0的空字符串,並在內存中分配了內存空間。

(3)String st = new String(“xyz”);表示創建了兩個對象:

①堆內存中一個new String;

②常量池中一個字符串。

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

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

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

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

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

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

37.final、finally和finalized的區別?

答:(1)final:被final修飾的類,不被能繼承;被final修飾的方法,不能被重寫;被fianl修飾的量,為常量,只能被賦值一次;

(2)finally:異常處理,和try、catch結合使用,可加可不加,用於執行一些必須執行的代碼,如釋放資源等;

(3)finalized:Object類中的方法,其中定義了對象要被垃圾回收器回收之前,要做的一些清理工作。

38.數據類型之間的轉換?

答:(1)如何將字符串轉換為基本數據類型?

調用基本數據類型對應的包裝類中的方法parseXXX(String)或valueOf(String)即可返回相應基本類型;

(2)如何將基本數據類型轉換為字符串?

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

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

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

public static String reverse(String originStr) {

if(originStr == null || originStr.length() <= 1)

return originStr;

return reverse(originStr.substring(1)) + originStr.charAt(0);

}

40.怎樣將GB2312編碼的字符串轉換為ISO-8859-1編碼的字符串?

答:代碼如下所示:

String s1 = "你好";

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


分享到:


相關文章: