Java面試題-基礎知識

基礎題目

1. Java線程的狀態

Java線程在某個時刻只能處於以下六個狀態中的一個。 – New(新創建),一個線程剛剛被創建出來,還沒有開始運行的狀態,更通俗點說:還沒有調用start方法; – Runnable(可運行),可以在Java虛擬機中運行的狀態;一個可運行的線程可能正在運行自己的代碼也可能沒有,這取決於操作系統提供的時間片; – Blocked(被阻塞),當一個線程試圖獲取一個內部的對象鎖(不是java.util.concurrent庫中的鎖),而該鎖此時正被其他線程持有,則該線程進入阻塞狀態; – Waiting(等待),當線程等待另一個線程通知調度器一個條件時,它自己進入等待狀態。在調用Object.wait方法或Thread.join方法,或者是等待java.util.concurrent庫中的Lock或Condition時,就會出現這種情況; – Timed waiting(計時等待),Object.wait、Thread.join、Lock.tryLock和Condition.await等方法有超時參數,還有Thread.sleep方法、LockSupport.parkNanos方法和LockSupport.parkUntil方法,這些方法會導致線程進入計時等待狀態,如果超時或者出現通知,都會切換會可運行狀態; – Terminated(被終止),因爲run方法正常退出而死亡,或者因爲沒有捕獲的異常終止了run方法而死亡。

Java線程狀態.png

參考資料: – Java Platform SE 8文檔 – Java核心技術 卷I—P634

2. 進程與線程的區別,進程間如何通訊,線程間如何通訊?

在併發編程領域,有進程和線程兩個概念,在Java語言中說起併發編程,常常是指多線程,但是瞭解進程的概念也非常重要: – 進程是操作系統的資源調度實體,有自己的內存地址空間和運行環境; – 線程一般被稱爲輕量級的進程,線程和進程一樣,也有自己的運行環境,但是創建一個線程要需要的資源比創建一個進程要少。線程存在於進程之中——每個進程至少有一個線程。一個進程下的多個線程之間可以共享進程的資源,包括內存空間和打開的文件。 – 進程跟程序(programs)、應用(applications)具備相同的含義,進程間通訊依靠IPC資源,例如管道(pipes)、套接字(sockets)等; – 線程間通訊依靠JVM提供的API,例如wait方法、notify方法和notifyAll方法,線程間還可以通過共享的主內存來進行值的傳遞;

參考資料: – Oracle Java Doc——進程和線程

3. HashMap的數據結構是什麼?如何實現的?和HashTable、ConcurrentHashMap的區別?

  • 在Java 8中,HashMap的數據結構是由Node<k,v>作爲元素組成的數組:(1)如果有多個值hash到同一個桶中,則組織成一個鏈表,而且,當這個鏈表的節點個數超過某個值(TREEIFY_THRESHOLD參數指定)時,則將這個鏈表重構爲一個二叉樹;(2)如果發現map中的元素個數超過了threshold,則進行空間擴容——二倍空間。</k,v>
  • HashMap和HashTable的數據結構和操作基本相同,區別是前者是非線程安全,並且HashMap接受value爲null。
  • ConcurrentHashMap和HashTable一樣,都是線程安全的,但是區別是:HashTable每次操作都會鎖住整個表結構——導致一次只能有一個線程訪問HashTable對象,而ConcurrentHashMap不會,只會鎖住某個節點,只有在涉及到size的操作時纔會鎖整個表結構。
  • 參考資料:《Java併發編程實戰》 ### 4. Cookie和Session的區別 HTTP是無狀態協議,但是在實際應用中有跟蹤客戶端狀態的需求,Cookie和Session是兩種不同的實現方案。
  • Cookie保存在客戶端,Session保存在服務端
  • Cookie沒有Session安全,侵入者可以通過分析客戶端的cookie信息侵入網站;
  • 使用Session存儲重要信息,使用Cookie存儲不那麼重要的信息;
  • 使用Session方案時,常常需要依賴Cookie傳遞SID的值,如果客戶端禁用了Cookie,則轉而採取URL重寫技術(但是這種技術有安全風險);
  • 參考資料:What is the difference between Sessions and Cookies in PHP?

5. 索引有什麼用?如何建索引?

  • 索引的作用:索引是一種數據結構,用於加快mysql獲取數據的速度;
  • 如何建索引?在使用InnoDB引擎的前提下討論:(1)最左前綴原理:分析業務中的查詢條件,區分度高的字段放在前面,儘量減少一條SQL的影響行數;(2)A+B可以代替A,A+B+C可以代替A+B,如果查詢是A+C則只能使用到A列索引;
    • 關於InnoDB的認識:InnoDB使用B+Tree作爲存儲數據結構,屬於聚簇索引,每個輔助索引最後都會指向主鍵的值,每次查詢兩次;(4)由於聚簇索引的特性,建議在使用InnoDB引擎的時候,使用自增ID作爲主鍵,不要使用隨機的業務列作爲主鍵。
  • 參考資料

6. ArrayList是如何實現的,ArrayList和LinkedList的區別?ArrayList如何實現擴容?

  • 可變數組實現了List接口的所有操作,功能上跟Vector相同,區別是Vector是線程安全的;
  • 區別:LinkedList實現了List和Deque接口,一般稱爲雙向鏈表;LinkedList在插入和刪除數據時效率更高,ArrayList在查找某個index的數據時效率更高;LinkedList比ArrayList需要更多的內存;
  • 關於可變數組的擴容策略,可以查看源碼,不同的JDK實現不太一樣,我這裏使用JDK 8:首先嚐試擴容爲原來大小的二倍,如果newCapacity還不夠大,則再擴大爲minCapacity值;如果newCapacity比數組的規定最大容量還大,則根據minCapacity的值進行定奪,參見hugeCapacity方法。 “` private static final int MAXARRAYSIZE = Integer.MAXVALUE – 8 /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity – minCapacity < 0) newCapacity = minCapacity; if (newCapacity – MAXARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAXARRAYSIZE) ? Integer.MAXVALUE : MAXARRAY_SIZE; } “`
  • 在代碼中,如果預先知道需要增加大量元素,則可以提前對當前的可變數組調用ensureCapacity方法,可以避免多次遞增的內存重新分配;
  • 參考資料:

7. equals、hashcode等Object類中一些方法的討論?

8. 面向對象

  • 三大特性
    • 封裝
    • 繼承
    • 多態

9. JVM如何加載字節碼文件?

虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。

Java語言中類的加載、連接和初始化過程都是在程序運行期間完成的,領Java具備高度的靈活性。

類加載的過程:加載、連接(驗證、準備、解析)、初始化。 – 加載:通過一個類的名字獲取此類的二進制字節流(PS:不限於從文件中讀取);將這個字節流代表的靜態存儲結構轉換爲方法區的運行時結構(由具體的虛擬機自己定義);在內存中生成一個java.lang.Class對象,作爲方法區這個類的各種數據結構的訪問入口。 – 驗證:文件格式驗證、元數據驗證(語義分析,類與類的繼承關係等)、字節碼驗證(數據流和控制流分析)、符號引用驗證(對類自身以外的信息進行匹配校驗) – 準備:正式爲類變量分配內存並設置初始值,這裏類變量指的是被static修飾的變量。例外:如果類字段是常量,則在這裏會被初始化爲表達式指定的值。 – 解析:將常量池內的符號引用替換爲直接引用。符號引用:類似於OS中的邏輯地址;直接引用:類似於OS中的物理地址,直接指向目標的指針、相對偏移量或一個能間接定位到目標的句柄。 – 初始化:真正開始執行類中定義的Java程序代碼;初始化用於執行Java類的構造方法。類初始化的過程是不可逆的,如果中間一步出錯,則無法執行下一步,參見不可逆的類初始化過程

10. GC算法

  • 垃圾回收解決三個問題:哪些內存需要回收?什麼時候回收?如何回收?
  • 垃圾回收關注的是堆內存(heap);
  • 常見的垃圾收集算法
    • 標記-清除算法
    • 複製算法
    • 標記-整理算法
    • 分代收集算法

11. 什麼情況下回出現Full GC,什麼情況下會出現Young GC

  • 對象優先在新生代Eden區中分配,如果Eden區沒有足夠的空間時,就會觸發一次young gc
  • Full gc的觸發條件有多個,FULL GC的時候會STOP THE WORD。
    • 在執行Young gc之前,JVM會進行空間分配擔保——如果老年代的連續空間小於新生代對象的總大小(或歷次晉升的平均大小),則觸發一次full gc。
    • 顯式調用System.gc()方法時
    • 大對象直接進入老年代,從年輕代晉升上來的老對象,嘗試在老年代分配內存時,但是老年代內存空間不夠;

12. JVM內存模型

JVM內存模型– Java虛擬機規範定義Java內存模型,嘗試屏蔽掉各種硬件和操作系統的訪問差異; – JVM內存模型的目標:定義程序中各個變量的訪問規則,即在虛擬機中將變量存儲到內存和從內存取出來這樣的細節; – volatile關鍵字:當一個變量用volatile關鍵字限定後,會有兩個語義:(1)當這個變量的值被修改後,會立即刷新到主內存中,對其他線程可見;當某個線程讀取這個變量的時候,也會重新將主內存中的數據刷一份到工作內存中來。但是,如果多線程操作這個變量的計算中,後一個值依賴前一個值,就還是會有併發問題,說明volatile不具備原子性;(2)禁止指令重排優化,觀察voatile變量對應的字節碼文件,會發現變量的操作指令後面加了一句lock addl $0x0,(%esp)的操作,這個操作相當於一個內存屏障。 – synchronized關鍵字:當一個線程對一個變量加鎖的時候,就會清空這個變量在當前工作內存中的值,因此該關鍵字同時滿足了可見性和原子性。 – 參考資料 – 程曉明:深入理解JVM內存模型1 – 《深入理解JVM虛擬機》 – 《Java併發編程實戰》

13. Java運行時數據區

Java虛擬機運行時數據區 – 程序計數器(PC):Java線程私有,類似於操作系統裏的PC計數器,用於指定下一條需要執行的字節碼的地址; – Java虛擬機棧:Java線程私有,虛擬機展描述的是Java方法執行的內存模型:每個方法在執行的時候,都會創建一個棧幀用於存儲局部變量、操作數、動態鏈接、方法出口等信息;每個方法調用都意味着一個棧幀在虛擬機棧中入棧到出棧的過程; – 本地方法棧:和Java虛擬機棧的作用類似,區別是該該區域爲JVM調用到的本地方法服務; – 堆(Heap):所有線程共享的一塊區域,垃圾收集器管理的主要區域。目前主要的垃圾回收算法都是分代收集,因此該區域還可以細分爲如下區域: – 年輕代 – Eden空間 – From Survivor空間1,From Survivor空間2,用於存儲在Young gc過程中倖存的對象; – 老年代 – 方法區:各個線程共享的一個區域,用於存儲虛擬機加載的類信息、常量、靜態變量等信息; – 運行時常量池:方法區的一部分,用於存放編譯器生成的各種字面量和符號引用;

14. 事務的實現原理

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