51.Anonymous Inner Class(匿名內部類)是否可以繼承其它類?是否可以實現接口?
答:可以繼承其他類或實現其他接口,在Swing編程和Android開發中常用此方式來實現事件監聽和回調。
52.內部類可以引用它的包含類(外部類)的成員嗎?有沒有什么限制?
答:一個內部類對象可以訪問創建它的外部類對象的成員,包括私有成員。
53.Java 中的final關鍵字有哪些用法?
答:(1)修飾類:表示該類不能被繼承;
(2)修飾方法:表示方法不能被重寫;
(3)修飾變量:表示變量只能一次賦值以后值不能被修改(常量)。
54.指出下面程序的運行結果。
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。創建對象時構造器的調用順序是:先初始化靜態成員,然后調用父類構造器,再初始化非靜態成員,最后調用自身構造器。
提示:如果不能給出此題的正確答案,說明之前第21題Java類加載機制還沒有完全理解,趕緊再看看吧。
55.數據類型之間的轉換:
- 如何將字符串轉換為基本數據類型?
- 如何將基本數據類型轉換為字符串?
答:
- 調用基本數據類型對應的包裝類中的方法parseXXX(String)或valueOf(String)即可返回相應基本類型;
- 一種方法是將基本數據類型與空字符串("")連接(+)即可獲得其所對應的字符串;另一種方法是調用String 類中的valueOf()方法返回相應字符串。
56.如何實現字符串的反轉及替換?
答:方法很多,可以自己寫實現也可以使用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); }
57.怎樣將GB2312編碼的字符串轉換為ISO-8859-1編碼的字符串?
答:代碼如下所示:
String s1 = "你好"; String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");
58.日期和時間:
- 如何取得年月日、小時分鍾秒?
- 如何取得從1970年1月1日0時0分0秒到現在的毫秒數?
- 如何取得某月的最后一天?
- 如何格式化日期?
答:
問題1:創建java.util.Calendar 實例,調用其get()方法傳入不同的參數即可獲得參數所對應的值。Java 8中可以使用java.time.LocalDateTimel來獲取,代碼如下所示。
public class DateTimeTest { public static void main(String[] args) { Calendar cal = Calendar.getInstance(); System.out.println(cal.get(Calendar.YEAR)); System.out.println(cal.get(Calendar.MONTH)); // 0 - 11 System.out.println(cal.get(Calendar.DATE)); System.out.println(cal.get(Calendar.HOUR_OF_DAY)); System.out.println(cal.get(Calendar.MINUTE)); System.out.println(cal.get(Calendar.SECOND)); // Java 8 LocalDateTime dt = LocalDateTime.now(); System.out.println(dt.getYear()); System.out.println(dt.getMonthValue()); // 1 - 12 System.out.println(dt.getDayOfMonth()); System.out.println(dt.getHour()); System.out.println(dt.getMinute()); System.out.println(dt.getSecond()); } }
問題2:以下方法均可獲得該毫秒數。
Calendar.getInstance().getTimeInMillis(); System.currentTimeMillis(); Clock.systemDefaultZone().millis(); // Java 8
問題3:代碼如下所示。
Calendar time = Calendar.getInstance();
time.getActualMaximum(Calendar.DAY_OF_MONTH);
問題4:利用java.text.DataFormat 的子類(如SimpleDateFormat類)中的format(Date)方法可將日期格式化。Java 8中可以用java.time.format.DateTimeFormatter來格式化時間日期,代碼如下所示。
import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Date; class DateFormatTest { public static void main(String[] args) { SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd"); Date date1 = new Date(); System.out.println(oldFormatter.format(date1)); // Java 8 DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); LocalDate date2 = LocalDate.now(); System.out.println(date2.format(newFormatter)); } }
補充:Java的時間日期API一直以來都是被詬病的東西,為了解決這一問題,Java 8中引入了新的時間日期API,其中包括LocalDate、LocalTime、LocalDateTime、Clock、Instant等類,這些的類的設計都使用了不變模式,因此是線程安全的設計。如果不理解這些內容,可以參考我的另一篇文章《關於Java並發編程的總結和思考》。
59.打印昨天的當前時刻。
答:
import java.util.Calendar; class YesterdayCurrent { public static void main(String[] args){ Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -1); System.out.println(cal.getTime()); } }
在Java 8中,可以用下面的代碼實現相同的功能。
import java.time.LocalDateTime; class YesterdayCurrent { public static void main(String[] args) { LocalDateTime today = LocalDateTime.now(); LocalDateTime yesterday = today.minusDays(1); System.out.println(yesterday); } }
60.比較一下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表達式以及函數式接口的支持。對於這類問題,在面試的時候最好還是用自己的語言回答會更加靠譜,不要背網上所謂的標准答案。
61.什么時候用斷言(assert)?
答:斷言在軟件開發中是一種常用的調試方式,很多開發語言中都支持這種機制。一般來說,斷言用於保證程序最基本、關鍵的正確性。斷言檢查通常在開發和測試時開啟。為了保證程序的執行效率,在軟件發布后斷言檢查通常是關閉的。斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式為true;如果表達式的值為false,那么系統會報告一個AssertionError。斷言的使用如下面的代碼所示:
assert(a > 0); // throws an AssertionError if a <= 0
斷言可以有兩種形式:
assert Expression1;
assert Expression1 : Expression2 ;
Expression1 應該總是產生一個布爾值。
Expression2 可以是得出一個值的任意表達式;這個值用於生成顯示更多調試信息的字符串消息。
要在運行時啟用斷言,可以在啟動JVM時使用-enableassertions或者-ea標記。要在運行時選擇禁用斷言,可以在啟動JVM時使用-da或者-disableassertions標記。要在系統類中啟用或禁用斷言,可使用-esa或-dsa標記。還可以在包的基礎上啟用或者禁用斷言。
注意:斷言不應該以任何方式改變程序的狀態。簡單的說,如果希望在不滿足某些條件時阻止代碼的執行,就可以考慮用斷言來阻止它。
62.Error和Exception有什么區別?
答:Error表示系統級的錯誤和程序不必處理的異常,是恢復不是不可能但很困難的情況下的一種嚴重問題;比如內存溢出,不可能指望程序能處理這樣的情況;Exception表示需要捕捉或者需要程序進行處理的異常,是一種設計或實現問題;也就是說,它表示如果程序運行正常,從不會發生的情況。
面試題:2005年摩托羅拉的面試中曾經問過這么一個問題“If a process reports a stack overflow run-time error, what’s the most possible cause?”,給了四個選項a. lack of memory; b. write on an invalid memory space; c. recursive function calling; d. array index out of boundary. Java程序在運行時也可能會遭遇StackOverflowError,這是一個無法恢復的錯誤,只能重新修改代碼了,這個面試題的答案是c。如果寫了不能迅速收斂的遞歸,則很有可能引發棧溢出的錯誤,如下所示:
class StackOverflowErrorTest { public static void main(String[] args) { main(null); } }
提示:用遞歸編寫程序時一定要牢記兩點:1. 遞歸公式;2. 收斂條件(什么時候就不再繼續遞歸)。
63.try{}里有一個return語句,那么緊跟在這個try后的finally{}里的代碼會不會被執行,什么時候被執行,在return前還是后?
答:會執行,在方法返回調用者前執行。
注意:在finally中改變返回值的做法是不好的,因為如果存在finally代碼塊,try中的return語句不會立馬返回調用者,而是記錄下返回值待finally代碼塊執行完畢之后再向調用者返回其值,然后如果在finally中修改了返回值,就會返回修改后的值。顯然,在finally中返回或者修改返回值會對程序造成很大的困擾,C#中直接用編譯錯誤的方式來阻止程序員干這種齷齪的事情,Java中也可以通過提升編譯器的語法檢查級別來產生警告或錯誤,Eclipse中可以在如圖所示的地方進行設置,強烈建議將此項設置為編譯錯誤。
64.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。
65.運行時異常與受檢異常有何異同?
答:異常表示程序運行過程中可能出現的非正常狀態,運行時異常表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題通常就不會發生。受檢異常跟程序運行的上下文環境有關,即使程序設計無誤,仍然可能因使用的問題而引發。Java編譯器要求方法必須聲明拋出可能發生的受檢異常,但是並不要求必須聲明拋出未被捕獲的運行時異常。異常和繼承一樣,是面向對象程序設計中經常被濫用的東西,在Effective Java中對異常的使用給出了以下指導原則:
- 不要將異常處理用於正常的控制流(設計良好的API不應該強迫它的調用者為了正常的控制流而使用異常)
- 對可以恢復的情況使用受檢異常,對編程錯誤使用運行時異常
- 避免不必要的使用受檢異常(可以通過一些狀態檢測手段來避免異常的發生)
- 優先使用標准的異常
- 每個方法拋出的異常都要有文檔
- 保持異常的原子性
- 不要在catch中忽略掉捕獲到的異常