Java面試題—基礎(二)

  1. 日期和時間

    • 如何取得年、月、日、時、分、秒、毫秒?
    • 如何取得從1970年1月1日0時0分0秒到現在的毫秒數?
    • 如何取得某月的後一天?
    • 如何獲取昨天的當前時刻?
    • 如何格式化日期?

    • 取得年、月、日、時、分、秒、毫秒
       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());   
      
    • 如何取得從1970年1月1日0時0分0秒到現在的毫秒數?
      Calendar.getInstance().getTimeInMillis();
      System.currentTimeMillis();
      Clock.systemDefaultZone().millis(); // Java 8 
      
    • 如何取得某月的後一天?
      Calendar time = Calendar.getInstance(); 
      time.getActualMaximum(Calendar.DAY_OF_MONTH); 
      //Java 8
      LocalDate date = LocalDate.now();
      date.with(TemporalAdjusters.lastDayOfMonth());
      
    • 如何獲取昨天的當前時刻?
      Calendar cal = Calendar.getInstance();
      cal.add(Calendar.DATE, -1); 
      //Java 8
      LocalDateTime today = LocalDateTime.now(); 
      LocalDateTime yesterday = today.minusDays(1); 		
      
    • 如何格式化日期?
      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)); 
      
  2. 比較一下Java和JavaScript
    Java與JavaScript是兩個公司開發的不同的兩個產品。Java 是原Sun Micro Systems公司推出的面向對象的程序設計語言,特別適合於互聯網應用程序開發;而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表達式以及函數式接口的支持。

  3. Error和Exception有什麼區別?
    Error表示系統級的錯誤和程序不必處理的異常,是能恢復但很困難的情況下的一種嚴重問題;比如內存溢出,不可能指望程序能處理這樣的情況;Exception表示需要捕捉或者需要程序進行處理的異常,是一種設計或實現問題,也就是說,它表示如果程序運行正常,從不會發生的情況。
    常見的Error:

    • OutOfMemoryError:內存溢出,內存不夠用
    • StackOverFlowError:棧溢出,棧空間不夠用或者方法調用深度超過最大值(遞歸不合理造成)
  4. try裏有一個return語句,對應結構finally裏的代碼會不會被執行,什麼時候被執行,在return前還是後?
    會執行。

    finally中的語句會在try和catch裏控制流傳語句前執行和語句結束後執行,控制流傳語句分四種情況:break、continue、返回return和拋出異常throw,語句結束就是除了控制流轉語句之外後面沒有語句了(除了System.exit(n))。
    try裏的語句對返回值的變量的引用的修改不會影響返回的引用,但是對返回值的變量屬性的修改會影響返回值的屬性,因爲操作的同一對象;finally裏的語句同樣在catch裏的return或者throw語句結束之前執行,finally裏的return和throw會覆蓋返回值和拋出異常,一般不建議這麼做,這違背了try(-catch)-finally的設計初衷,這裏的catch是可選的,沒有catch編譯自動添加catch並重拋異常。
    try(-catch)-finally的實現方式:(《深入理解Java虛擬機》)

    在JDK1.4.2之前的Javac編譯器採用了jsr和ret指令實現finally語句,但1.4.2之後已經改爲編譯器自動在每段可能的分支路徑之後都將finally語句塊的內容冗餘生成一遍來實現finally語義。在JDK 1.7中,已經完全禁止Class文件中出現jsr和ret指令,如果遇到這兩條指令,虛擬機會在類加載的字節碼校驗階段拋出異常。

  5. 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。

  6. 運行時異常與受檢異常有何異同?
    異常表示程序運行過程中可能出現的非正常狀態。
    運行時異常(RuntimeException)表示虛擬機的通常操作中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題通常就不會發生。
    受檢異常(Exception)跟程序運行的上下文環境有關,即使程序設計無誤,仍然可能因使用的問題而引發。Java編譯器要求方法必須聲明拋出可能發生的受檢異常,但是並不要求必須聲明拋出未被捕獲的運行 時異常。
    異常和繼承一樣,是面向對象程序設計中經常被濫用的東西,在Effective Java中對異常的使用給出了以下指導原則:

    • 不要將異常處理用於正常的控制流(設計良好的API不應該強迫它的調用者爲了正常的控制流而使用異常)
    • 對可以恢復的情況使用受檢異常,對編程錯誤使用運行時異常
    • 避免不必要的使用受檢異常(可以通過一些狀態檢測手段來避免異常的發生)
    • 優先使用標準的異常 - 每個方法拋出的異常都要有文檔 - 保持異常的原子性 - 不要在catch中忽略掉捕獲到的異常。

    基本異常繼承關係。(圖片來源:https://blog.csdn.net/a15089415104/article/details/83350277)
    異常基繼承關係
    凡是繼承RuntimeException的類都是運行時異常,其他只要不是Error都是受檢異常。
    運行時異常和Error不要去try檢查,其他異常都需要try檢查。
    try中的語句所調用的方法如果未能拋出某個Exception子類匹配的受檢異常,那麼catch中不能捕捉此類型的異常,其他均不受限制。

    常見的運行時異常:ArithmeticException(算術異常) 、ClassCastException (類轉換異常) 、IllegalArgumentException (非法參數異常)、 IndexOutOfBoundsException (下標越界異常) 、NullPointerException (空指針異常) 、SecurityException (安全異常)

    常見的受檢異常:IOException(IO異常) 、FileNotFoundException (文件未找到異常) 、CloneNotSupportedException(不支持clone異常)、ReflectiveOperationException(反射操作異常)、ClassNotFoundException(類未找到異常)、SQLException(SQL異常)

  7. 闡述final、finally、finalize的區別。

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

    try {
           throw new ExampleB("b")
       } catch(ExampleA e){
           System.out.println("ExampleA");
       } catch(Exception e){
           System.out.println("Exception");
    }
    

    請問執行此段代碼的輸出是什麼?
    ExampleA。(根據里氏代換原則[能使用父類型的地方一定能使用子類型],抓取ExampleA類型異常的catch塊能夠抓住try塊中拋出的ExampleB類型的異常) 。

    class Human {
         public static void main(String[] args) throws Exception {
             try {
                 try {
                     throw new Sneeze();
                 } catch (Annoyance a) {
                     System.out.println("Caught Annoyance");
                     throw a;
                 }
             } catch (Sneeze s) {
                 System.out.println("Caught Sneeze");
             } finally {
                 System.out.println("Hello World!");
             }
         }
    }
    

    執行結果

     Caught Annoyance
     Caught Sneeze
     Hello World!
    
  9. List、Set、Map是否繼承自Collection接口?
    List、Set 是,Map 不是。Map是鍵值對映射容器,與List和Set有明顯的區別,而Set存儲的零散的元素且不允許有重複元素(數學中的集合也是如此),List是線性結構的容器,適 用於按數值索引訪問元素的情形。

  10. 闡述ArrayList、Vector、LinkedList的存儲性能和特性
    ArrayList 和Vector都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及數組元素移動和數組擴容等內存操作,所以索引數據快而插入數據慢,Vector中的方法由於添加了synchronized修飾,因此Vector是線程安全的容器,但性能上較ArrayList差,因此已經是Java中的遺留容器。 LinkedList使用雙向鏈表實現存儲(將內存中零散的內存單元通過附加的引用關聯起來,形成一個可以按序號索引的線性結構,這種鏈式存儲方式與數組的連續存儲方式相比,內存的利 用率更高),按序號索引數據需要進行前向或後向遍歷,但是插入數據時只需要記錄本項的前後項即可,所以插入速度較快。Vector屬於遺留容器(Java早期的版本中提供的容器,除此 之外,Hashtable、Dictionary、Stack、Properties都是遺留容器)已經不推薦使用。

    由於ArrayList和LinkedList都是非線程安全的,如果遇到多個線程操作同一個容器的場景,則可以通過工具類Collections中的synchronizedList方法將其轉換成線程安全的容器後再使用(這是對裝飾模式的應用,將已有對象傳入另一個類的構造器中創建新的對象來 增強實現)。

  11. Collection和Collections的區別?
    Collection是一個接口,它是Set、List等容器的父接口;Collections是個一個工具類,提供了一系列的靜態方法來輔助容器操作,這些方法包括對容器的搜索、排序、線程安全化等等。

  12. List、Map、Set三個接口存取元素時,各有什麼特點?
    List以特定索引來存取元素,可以有重複元素。
    Set不能存放重複元素(用對象的equals()方法來區分元素是否重複),不能安裝索引訪問。
    Map保存鍵值對(key-value pair)映射,映射關係可以是一 對一或多對一。
    Set和Map容器都有基於哈希存儲和排序樹的兩種實現版本,基於哈希存儲的版本理論存取時間複雜度爲O(1),而基於排序樹版本的實現在插入或刪除元素時會按照元素或元素的鍵(key)構成排序樹從而達到排序和去重的效果。

  13. TreeMap和TreeSet在排序時如何比較元素?Collections工具類中的sort()方法如何比較元素?
    TreeMap和TreeSet通過在構造時指定一個排序器或要求存放的對象所屬的類必須實現Comparable接口(該接口提供了比較元素的compareTo()方法)當插入元素時會回調該方法比較元素的大小;TreeSet內部持有一個TreeMap的,忽略了Value。

    Collections工具類的sort方法有兩種重載的形式,第一種要求傳入的待排序容器中存放的對象比較實現Comparable 接口以實現元素的比較;第二種不強制性的要求容器中的元素必須可比較,但是要求傳入第二個參數,參數是Comparator接口的子類型(需要重寫compare方法實現元素的比較),相當於一個臨時定義的排序規則,其實就是通過接口注入比較元素大小的算法,也是對回調模式的應用(Java8中對函數式編程的支持)。

  14. 集合與數組區別和特性
    數組(可以存儲基本數據類型)是用來存現對象的一種容器,但是數組的長度固定,不適合在對象數量未知的情況下使用。
    集合(只能存儲對象,對象類型可以不一樣)的長度可變,可在多數情況下使用。
    集合框架層級圖,如圖所示:圖中,實線邊框的是實現類,折線邊框的是抽象類,而點線邊框的是接口
    集合框架層級圖
    Collection接口是集合類的根接口,Java中沒有提供這個接口的直接的實現類。但是卻讓其被繼承產生了兩個接口,就是Set和List。Set中不能包含重複的元素。List是一個有序的集合,可以包含重複的元素,提供了按索引訪問的方式。
    Map是Java.util包中的另一個接口,它和Collection接口沒有關係,是相互獨立的,但是都屬於集合類的一部分。Map包含了key-value對。Map不能包含重複的key,但是可以包含相同的value。
    Iterator,所有的集合類,都實現了Iterator接口,這是一個用於遍歷集合中元素的接口,主要包含以下三種方法:

    1. hasNext()是否還有下一個元素。
    2. next()返回下一個元素。
    3. remove()刪除當前元素
  15. Enumeration和Iterator接口的區別?
    Enumeration的速度是Iterator的兩倍,也使用更少的內存。Enumeration是非常基礎的,也滿足了基礎的需要。但是,與Enumeration相比,Iterator更加安全,因爲當一個集合正在被遍歷的時候,它會阻止其它線程去修改集合。

  16. Java集合框架常見的Map有哪些實現類,他們的區別是什麼
    HashMap、Hashtable、ConcurrentHashMap、LinkedHashMap和TreeMap。

    • HashMap:它根據鍵的HashCode值決定鍵值對存儲位置的,根據鍵可以直接獲取它的值,具有很快的訪問速度,遍歷時,取得數據的順序是完全隨機的。因爲鍵對象不可以重複,所以HashMap最多隻允許一條記錄的鍵爲Null,允許多條記錄的值爲Null,是非同步的。HashMap解決衝突的方式是“拉鍊法”,java8中當拉鍊上的元素個數超過8(含)個時採用紅黑樹,以便快速訪問。思考與HashSet的關係。
    • Hashtable:Hashtable與HashMap類似,是HashMap的線程安全版,它支持線程的同步,即任一時刻只有一個線程能寫Hashtable,因此也導致了Hashtale在寫入時會比較慢,它繼承自Dictionary類,不同的是它不允許記錄的鍵或者值爲null,同時效率較低。
    • ConcurrentHashMap:線程安全,並且鎖分離。Java7之前,ConcurrentHashMap內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的hash table,它們有自己的鎖,只要多個修改操作發生在不同的段上,它們就可以併發進行。Java8之後放棄使用分段鎖機制,採用CAS(樂觀鎖)方式保證線程安全。
    • LinkedHashMap:LinkedHashMap維護了迭代順序,既可以是插入順序,也可以時訪問順序,在構造時可以傳入這個標誌,有HashMap的全部特性。其實現方式繼承HashMap,然後其Entry繼承了HashMap.Entry,添加了before和after屬性,維護了Entry節點的先後關係,使節點之間形成了了一個雙向鏈表,在插入或者訪問時把對應節點調整到鏈表的最後,但是Hash結構性依然保留。思考與LinkedHashSet的關係。
    • TreeMap:TreeMap實現SortMap接口,能夠把它保存的記錄根據鍵排序,默認是按鍵值(此種情況Key需要實現Comparable接口,否則會異常)的升序排序,也可以指定排序的比較器。當用Iterator遍歷TreeMap時,得到的記錄是排過序的。不允許key值爲空,非同步的;思考與TreeSet的關係。

    思考:Set與Map有個共性:不允許元素/key重複,那麼Set的實現可以藉助Map,用Key來做元素,忽略value,只關注Key就可以,Java的set實現基本上是這麼做的。

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