Java面試題匯總---基礎版(附答案)


基於我個人對面試的認知和招聘經驗,在此我總結一下Java開發者的基礎知識掌握要求,及應聘者面試的需要准備的內容。

首先,Java基礎是每個面試官都會問到的,可能只是針對工作經驗的多少,對問題追蹤深度有所差異。基本對初中級開發者來說,基礎理論和應用不可缺少。對中高級,面試官會基於基礎理論問一些底層的原理甚至對源碼的理解。

一,JVM及工作原理

JVM --- Java Virtual Machine,即Java虛擬機。大家都知道Java具有可跨平台特性,其主要是指字節碼(.class文件)可以在任何具有Java虛擬機的計算機上運行,Java虛擬機中的Java解釋器負責將字節碼文件解釋成為特定的機器碼進行運行。JVM的體系結構分為三部分,分別是:類加載器(ClassLoader),運行時數據區和執行引擎。

1,類加載器:在JVM啟動時或者在類運行時將需要的,class加載到JVM中。

2,運行時數據區:是在JVM運行的時候操作所分配的內存區,主要划分為5個區域:

  • 方法區(Method Area):用於存儲類結構信息的地方,包括常量池、靜態變量、構造函數等。

  • Java堆(Heap):存儲java實例或者對象的地方,也是GC的主要區域。

  • Java棧(Stack):java棧總是和線程關聯在一起,每當創建一個線程時,JVM就會為這個線程創建一個對應的java棧。

  • 程序計數器(PC Register):用於保存當前線程執行的內存地址。

  • 本地方法棧(Native Method Stack):和java棧的作用差不多,只不過是為JVM使用到的native方法服務的

3,執行引擎:負責執行class文件中包含的字節碼指令。

二,java垃圾回收機制

在java中,程序員是不需要去釋放一個對象的內存的,而是由虛擬機自行執行。在JVM中,有一個垃圾回收線程,它是低優先級的,在正常情況下是不會執行,只有在虛擬機空閑或者當前堆內存不足時,才會觸發執行,掃描那些沒有被任何引用的對象,並將它們添加到要回收的集合中進行回收。

  垃圾回收算法

  • 標記-清除 

  • 標記-復制 

  • 標記-整理 

  • 分代回收 

  那些對象應該被回收:

 這就要從對象存活性判斷,常用的方法有兩種:1.引用計數法;2.對象可達性分析。由於引用計數法存在互相引用導致無法進行GC的問題,所以目前JVM虛擬機多使用對象可達性分析算法。

三,接口和抽象類的區別

  • 默認方法:抽象類可以有默認的方法實現,接口中不存在方法的實現。

  • 實現方式:子類使用extends關鍵字來繼承抽象類,如果子類不是抽象類,子類需要提供抽象類中所聲明方法的實現。而接口的子類使用implements來實現接口,需要提供接口中所有聲明的實現。

  • 構造函數:抽象類中可以有構造函數,接口中不能。

  • 和正常類區別:抽象類不能被實例化,接口則是完全不同的類型。

  • 訪問修飾符:抽象方法可以有public,protected和default等修飾,接口默認是public,不能使用其他修飾符。

  • 多繼承:一個子類只能繼承在一個抽象類,而一個子類可以實現多個接口。

  • 添加新方法:想在抽象類中添加新方法,可以提供默認的實現,因此可以不修改子類現有的代碼。如果往接口中添加新方法,則子類中需要實現該方法。

四,String相關問題

String相關問題很常見,對初級開發者來說,String的基礎使用方法必須掌握。

  1. Strings=new String(“xyz”) 創建了幾個String Object?

    兩個或一個,”xyz”對應一個對象,這個對象放在字符串常量緩沖區,常量”xyz”不管出現多少遍,都是緩沖區中的那一個。New String每寫一遍,就創建一個新的對象,它一句那個常量”xyz”對象的內容來創建出一個新String對象。如果以前就用過’xyz’,這句代表就不會創建”xyz”自己了,直接從緩沖區拿。

  2. String與StringBuffer及StringBuilder的區別

    它們都可以儲存和操作字符串,即包含多個字符的字符數據,主要區別:

    1)  String類表示內容不可改變的字符串,而StringBuffer和StringBuilder類都表示內容可以被修改的字符串。

    2)String實現了equals方法,new String(“abc”).equals(new String(“abc”)的結果為true。而StringBuffer和StringBuilder沒有實現equals方法,所以,new StringBuffer(“abc”).equals(new StringBuffer(“abc”)的結果為false。

  3. StringBuffer與StringBuilder的區別

    StringBuffer和StringBuilder都表示內容可以被修改的字符串,StringBuffer是線程安全的,而StringBuilder是線程不安全的,它相比StringBuffer運行效率高。如果一個字符串變量是在方法里面定義,這種情況只可能有一個線程訪問它,不存在不安全因素了,則用StringBuilder。如果要在類里面定義成員變量,並且這個類的實例對象會在多線程環境下使用,那么最好用StringBuffer。

四,線程、進程相關

  1. 線程和進程的區別

    簡而言之,進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程中擁有獨立的內存單元,而多個線程共享內存資源,減少切換次數,從而效率更高。線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間可以並發執行。

  2. 如何創建線程,他們有什么區別?

    創建線程可以實現java.lang.Runnable或者通過繼承java.lang.Thread類兩種方法。這兩種方法的區別:

    1)Java不支持多繼承。因此擴展Thread類就代表這個子類不能擴展其他類。而實現Runnable接口的類還可能擴展另一個類。

    2)類可能只要求可執行即可,因此繼承整個Thread類的開銷過大。

  3. Thread的start()和run()方法的區別?

    start()方法被用來啟動新創建的線程,而且start()內部調用了run()方法,這和直接調用run()方法的效果不一樣。當你調用run()方法的時候,只會是在原來的線程中調用,沒有新的線程啟動,start()方法才會啟動新線程。

  4. wait()和sleep()方法的區別?

    1)sleep()來自Thread類,和wait()來自Object類。調用sleep()方法的過程中,線程不會釋放對象鎖。而 調用 wait 方法線程會釋放對象鎖;

    2)sleep()睡眠后不出讓系統資源,wait讓其他線程可以占用CPU;

    3)sleep(milliseconds)需要指定一個睡眠時間,時間一到會自動喚醒,而wait()需要配合notify()或者notifyAll()使用;

  5. 什么導致線程阻塞,怎么喚醒一個已經阻塞的線程?

    線程阻塞指的是暫停一個線程的執行以等待某個條件發生。Java 提供了大量方法來支持阻塞,下面讓我們逐一分析。

    1)sleep() 允許指定以毫秒為單位的一段時間作為參數,它使得線程在指定的時間內進入阻塞狀態,指定的時間一過,線程重新進入可執行狀態。

    2)suspend()和resume() 這兩個方法配套使用,suspend()使得線程進入阻塞狀態,並且不會自動恢復,必須其對應的resume() 被調用,才能使得線程重新進入可執行狀態。

    3)yield()  使當前線程放棄當前已經分得的CPU 時間,但不使當前線程阻塞,即線程仍處於可執行狀態,隨時可能再次分得 CPU 時間。

    4)wait()和notify() 這兩個方法配套使用,wait() 使得線程進入阻塞狀態,它有兩種形式,一種允許指定以毫秒為單位的一段時間作為參數,另一種沒有參數,前者當對應的 notify() 被調用或者超出指定時間時線程重新進入可執行狀態,后者則必須對應的 notify() 被調用。

    喚醒阻塞線程:如果線程是因為調用了wait()、sleep()或者join()方法而導致的阻塞,可以中斷線程,並且通過拋出InterruptedException來喚醒它;如果線程遇到了IO阻塞,無能為力,因為IO是操作系統實現的,Java代碼並沒有辦法直接接觸到操作系統。

  6. 產生死鎖的條件

    1)互斥條件:一個資源每次只能被一個進程使用。 
    2)請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。 
    3)不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。 
    4)循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。

  7. 線程鎖(ThreadLocal)的作用

    簡單說ThreadLocal就是一種以空間換時間的做法,在每個Thread里面維護了一個ThreadLocal.ThreadLocalMap把數據進行隔離,數據不共享,自然就沒有線程安全方面的問題了。

  8. 樂觀鎖與悲觀鎖

    樂觀鎖:樂觀鎖認為競爭不總是會發生,因此它不需要持有鎖,將比較、替換這兩個動作作為一個原子操作嘗試去修改內存中的變量,如果失敗則表示發生沖突,那么就應該有相應的重試邏輯。

    悲觀鎖:它競爭總是會發生,因此每次對某資源進行操作時,都會持有一個獨占的鎖,就像synchronized,不管其他,直接上了鎖就操作資源了。

五,JAVA集合

關於Java中的集合體系是每個人都應該爛熟於心的,尤其是對我們經常使用的List、Set、Map的原理更該如此。

  1. 集合與數組的區別?

    1)數組是固定長度的,集合可變長度的;

    2)數組可以存儲基本數據類型,也可以存儲引用數據類型。集合只能存儲引用數據類型;

    3)數組存儲的元素必須是同一個數據類型,而集合存儲的對象可以是不同數據類型;

 

Java集合的層次關系

如圖所示,圖中實線邊框的是實現類,折線邊框的是抽象類,點線的是接口。

 

Collection是集合類的根接口,Java中沒有提供這個接口的直接的實現類。但是卻讓其被繼承產生了兩個接口List和Set。List是一個有序、可重復的集合,提供了按索引訪問的方式。Set中不能包含重復的元素。

Map是Java.util包中的另一個接口,它和Collection接口沒有關系,是相互獨立的,但是都屬於集合類的一部分。Map包含了key-value對。Map不能包含重復的key,但是可以包含相同的value。

Iterator,所有的集合類都實現了Iterator接口,這是一個用於遍歷集合中元素的接口,主要包含以下三種方法:

    1)hasNext()是否還有下一個元素。

    2)next()返回下一個元素。

    3)remove()刪除當前元素

 

  1. List、Set及Map的區別

    1)List(有序、可重復)

    List里存放的對象是有序的,同時也是可以重復的,List關注的是索引,擁有一系列和索引相關的方法,查詢速度快。因為往list集合里插入或刪除數據時,會伴隨着后面數據的移動,所有插入刪除數據速度慢。

    2)Set(無序、不能重復)

    Set里存放的對象是無序,不能重復的,集合中的對象不按特定的方式排序,只是簡單地把對象加入集合中。

    3)Map(鍵值對、鍵唯一、值不唯一)

    Map集合中存儲的是鍵值對,鍵不能重復,值可以重復。根據鍵得到值,對map集合遍歷時先得到鍵的set集合,對set集合進行遍歷,得到相應的值。

  2. ArrayList和Array有什么區別?

    1)Array可以容納基本類型和對象,而ArrayList只能容納引用類型對象;

    2)Array是指定大小的,而ArrayList大小是固定的;

  3. ArrayList和LinkedList的區別?

    ArrrayList底層的數據結構是數組,支持隨機訪問,而 LinkedList 的底層數據結構是雙向循環鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間復雜度是 O(1),而 LinkedList 是 O(n)。ArrrayList查詢快,增刪改慢,而LinkedList增刪快,卻查詢慢。

  4. ArrayList和Vector的區別?

    ArrayList和Vector都實現了List接口,他們都是有序集合,並且存放的元素是允許重復的。它們的底層都是通過數組來實現的,因此列表這種數據結構檢索數據速度快,但增刪改速度慢。

    ArrayList和Vector的區別:

    1)線程安全。Vector是線程安全的,而ArrayList是線程不安全的。因此在如果集合數據只有單線程訪問,那么使用ArrayList可以提高效率。而如果有多線程訪問你的集合數據,那么就必須要用Vector,因為要保證數據安全。

    2)數據增長。ArrayList和Vector都有一個初始的容量大小,當存儲進它們里面的元素超過了容量時,就需要增加它們的存儲容量。ArrayList每次增長原來的0.5倍,而Vector增長原來的一倍。ArrayList和Vector都可以設置初始空間的大小,Vector還可以設置增長的空間大小,而ArrayList沒有提供設置增長空間的方法。

  5. HashMap和Hashtable的區別

    HashMap和Hashtable都實現了Map接口,並且都是key-value的數據結構。它們的不同點主要在三個方面:

    1)Hashtable是Java1.1的一個類,它基於陳舊的Dictionary類。而HashMap是Java1.2引進的Map接口的一個實現。

    2)Hashtable是線程安全的,也就是說是線程同步的,而HashMap是線程不安全的。也就是說在單線程環境下應該用HashMap,這樣效率更高。

    3)HashMap允許將null值作為key或value,但Hashtable不允許(會拋出NullPointerException)。

  6. LinkedHashMap及TreeMap

    LinkedHashMap保存了記錄的插入順序,在用Iteraor遍歷LinkedHashMap時,先得到的記錄肯定是先插入的,在遍歷的時候會比HashMap慢,有HashMap的全部特性。

    TreeMap實現SortMap接口,能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序(自然順序),也可以指定排序的比較器,當用Iterator遍歷TreeMap時,得到的記錄是排過序的。不允許key值為空,非同步的;

六,異常及異常處理相關

異常是發生在程序執行過程中阻礙程序正常執行的錯誤事件。比如:用戶輸入錯誤數據、硬件故障、網絡阻塞等都會導致出現異常。

  1. error和exception有什么區別

    error表示系統級錯誤,是java運行環境內部錯誤或者硬件問題,不能指望程序來處理這樣的問題,除退出運行外別無選擇,它是Java虛擬機拋出的。

    exception 表示程序需要捕捉、需要處理的異常,是由與程序設計的不完善而出現的問題,程序必須處理的問題。

     

  2. 運行時異常和一般異常有何不同

    Java提供了兩類主要的異常:runtimeException和checkedException

    一般異常(checkedException)主要是指IO異常、SQL異常等。對於這種異常,JVM要求我們必須對其進行處理。

    運行時異常(runtimeException)一般不處理,當出現這類異常時程序會由虛擬機接管。比如NullPointerException,而且這個異常也是最常見的異常之一。

    出現運行時異常的時候,程序會將異常一直向上拋,一直拋到遇到處理代碼,如果沒有catch塊進行處理,到了最上層,如果是多線程就有Thread.run()拋出,如果不是多線程那么就由main.run()拋出。拋出之后,如果是線程,那么該線程也就終止了,如果是主程序,那么該程序也就終止了。

  3. throw和throws的區別

    throw用於主動拋出java.lang.Throwable 類的一個實例化對象,意思是說你可以通過關鍵字 throw 拋出一個 Error 或者 一個Exception,如:throw new IllegalArgumentException(“size must be multiple of 2″)。

    而throws 的作用是作為方法聲明和簽名的一部分,方法被拋出相應的異常以便調用者能處理。在Java 中,任何未處理的受檢查異常強制在 throws 子句中聲明。

  4. 你平時在項目中是怎樣對異常進行處理的。

    1)盡量避免出現runtimeException 。例如對於可能出現空指針的代碼,帶使用對象之前一定要判斷一下該對象是否為空,必要的時候對runtimeException也進行try catch處理。

    2)進行try catch處理的時候要在catch代碼塊中對異常信息進行記錄,通過調用異常類的相關方法獲取到異常的相關信息,返回到web端,不僅要給用戶良好的用戶體驗,也要能幫助程序員良好的定位異常出現的位置及原因。

    3)使用自定義異常,在業務代碼中將可預測的異常使用自定義異常拋出,最后通過AOP在最外層統一捕獲,並根據異常類型封裝返回。

七,Spring框架

Spring 是個java企業級應用的開源輕量級開發框架。Spring主要用來開發Java應用,但是有些擴展是針對構建J2EE平台的web應用。Spring 框架目標是簡化Java企業級應用開發,並通過POJO為基礎的編程模型促進良好的編程習慣。

  1. Spring框架的主要功能及好處?

    控制反轉:Spring通過控制反轉實現了松散耦合,對象們給出它們的依賴,而不是創建或查找依賴的對象們。

    面向切面的編程(AOP):Spring支持面向切面的編程,並且把應用業務邏輯和系統服務分開。

    容器:Spring 包含並管理應用中對象的生命周期和配置。

    MVC框架:Spring的WEB框架是個精心設計的框架,是Web框架的一個很好的替代品。

    事務管理:Spring 提供一個持續的事務管理接口,可以擴展到上至本地事務下至全局事務(JTA)。

    異常處理:Spring 提供方便的API把具體技術相關的異常(比如由JDBC,Hibernate or JDO拋出的)轉化為一致的unchecked 異常。

  2. 談談你對Spring IOC和AOP的理解

    IOC即控制反轉。就是對象的創建權反轉交給Spring,由容器控制程序之間的依賴關系,作用是實現了程序的解耦合,而非傳統實現中,由程序代碼直接操控。最直觀的表達就是,IOC讓對象的創建不用去new了,可以由spring自動生產,這里用的就是java的反射機制,通過反射在運行時動態的去創建、調用對象。spring就是根據配置文件在運行時動態的去創建對象,並調用對象的方法的。

    Spring的IOC有三種注入方式 : 

           1)根據屬性注入,也叫set方法注入; 

           2)根據構造方法進行注入; 

           3)根據注解進行注入(Autowired和Resource)。

    AOP即面向切面編程,作為面向對象的一種補充,用於解剖封裝好的對象內部,找出其中對多個對象產生影響的公共行為,並將其封裝為一個可重用的模塊,這個模塊被命名為“切面”(Aspect),切面將那些與業務無關,卻被業務模塊共同調用的邏輯提取並封裝起來,減少了系統中的重復代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。可用於權限認證、日志、事務處理。

    AOP實現的關鍵在於AOP框架自動創建的AOP代理,AOP代理主要分為靜態代理和動態代理。靜態代理的代表為AspectJ;動態代理則以Spring AOP為代表。

    1)AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP代理類,因此也稱為編譯時增強,他會在編譯階段將AspectJ織入到Java字節碼中,運行的時候就是增強之后的AOP對象。

    2)Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改字節碼,而是每次運行時在內存中臨時為方法生成一個AOP對象,這個AOP對象包含了目標對象的全部方法,並且在特定的切點做了增強處理,並回調原對象的方法。

    Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理:

        ①JDK動態代理通過反射來接收被代理的類,並且要求被代理的類必須實現一個接口。JDK動態代理的核心是InvocationHandler接口和Proxy類。生成的代理對象的方法調用都會委托到InvocationHandler.invoke()方法,當我們調用代理類對象的方法時,這個“調用”會轉送到invoke方法中,代理類對象作為proxy參數傳入,參數method標識了我們具體調用的是代理類的哪個方法,args為這個方法的參數。

         ②如果目標類沒有實現接口,那么Spring AOP會選擇使用CGLIB來動態代理目標類。CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動態的生成指定類的一個子類對象,並覆蓋其中特定方法,覆蓋方法時可以添加增強代碼,從而實現AOP。CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態代理的。

    3)靜態代理與動態代理區別在於生成AOP代理對象的時機不同,相對來說AspectJ的靜態代理方式具有更好的性能,但是AspectJ需要特定的編譯器進行處理,而Spring AOP則無需特定的編譯器處理。

    IOC讓相互協作的組件保持松散的耦合,而AOP編程允許你把遍布於應用各層的功能分離出來形成可重用的功能組件。

  3. SpringMVC的原理及執行流程?

     

    SpringMVC核心是前端控制器DispatcherServlet,一個請求的執行流程:

    1)用戶發送請求到前端控制器DispatcherServlet;

    2)DispatcherServlet收到請求后,調用HandlerMapping處理器映射器,請求獲取Handle處理器;

    3)處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一並返回給DispatcherServlet;

    4)DispatcherServlet通過HandlerAdapter處理器適配器調用處理器;

    5)處理器處理業務,完成后返回ModelAndView;

    6) HandlerAdapter將Handler執行結果ModelAndView返回給DispatcherServlet;

    7)DispatcherServlet再將ModelAndView傳給ViewResolver視圖解析器進行解析;

    8)ViewResolver解析后返回具體View;

    9)DispatcherServlet對View進行渲染視圖(即將模型數據填充至視圖中),並響應給用戶。

八,數據庫

數據庫也是Java開發者的面試中的重點,這里只說說常見的數據庫面試題。

  1. 數據庫三范式?

    第一范式(1NF):數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構成,包括整型、實數、字符型、邏輯型、日期型等。 

    第二范式(2NF):數據庫表中不存在非關鍵字段對任一候選關鍵字段的部分函數依賴(部分函數依賴指的是存在組合關鍵字中的某些字段決定非關鍵字段的情況),也即所有非關鍵字段都完全依賴於任意一組候選關鍵字。 

    第三范式(3NF):在第二范式的基礎上,數據表中如果不存在非關鍵字段對任一候選關鍵字段的傳遞函數依賴則符合第三范式。所謂傳遞函數依賴,指的是如果存在”A → B → C”的決定關系,則C傳遞函數依賴於A。

  2. 數據庫事務的四大特性?

    1) 原子性(Atomicity)

    事務被視為不可分割的最小單元,事務的所有操作要么全部提交成功,要么全部失敗回滾。回滾可以用日志來實現,日志記錄着事務所執行的修改操作,在回滾時反向執行這些修改操作即可。

    2)一致性(Consistency)

    數據庫在事務執行前后都保持一致性狀態。在一致性狀態下,所有事務對一個數據的讀取結果都是相同的。

    3)隔離性(Isolation)

    一個事務所做的修改在最終提交以前,對其它事務是不可見的。

    4)持久性(Durability)

    一旦事務提交,則其所做的修改將會永遠保存到數據庫中。即使系統發生崩潰,事務執行的結果也不能丟失。可以通過數據庫備份和恢復來實現,在系統發生崩潰時,使用備份的數據庫進行數據恢復。

  3. 索引是什么?有什么作用以及優缺點?

    1)什么是索引【Index】

    是一種快速查詢表中內容的機制,類似於新華字典的目錄;運用在表中某個些字段上,但存儲時,獨立於表之外;

    2)什么時候【要】創建索引

    (1)表經常進行 SELECT 操作

    (2)表很大(記錄超多),記錄內容分布范圍很廣

    (3)列名經常在 WHERE 子句或連接條件中出現

    3什么時候【不要】創建索引

    (1)表經常進行 INSERT/UPDATE/DELETE 操作

    (2)表很小(記錄超少)

    (3)列名不經常作為連接條件或出現在 WHERE 子句中

    4)索引優缺點:

    (1)索引加快數據庫的檢索速度

    (2)索引降低了插入、刪除、修改等維護任務的速度(雖然索引可以提高查詢速度,但是它們也會導致數據庫系統更新數據的性能下降,因為大部分數據更新需要同時更新索引)

    (3)唯一索引可以確保每一行數據的唯一性,通過使用索引,可以在查詢的過程中使用優化隱藏器,提高系統的性能

    (4)索引需要占物理和數據空間

關於數據庫表設計等,今天就不多說了,畢竟這需要一定的積累,不會一時不會就能說完的,如果你想了解和學習,可以關注此公眾號,添加微信私聊。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM