歡迎光臨
每天分享高質量文章

Java面試題-基礎知識

本文來源於清英的一篇文章:你應該知道的JAVA面試題,最近自己也在面試一些候選人,發現這篇文章中的有些點我也拿不准,因此按照自己的理解整理一份參考回答(這篇是基礎知識部分,共14道題目)。

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方法而死亡。

 

2
行程與執行緒的區別,行程間如何通訊,執行緒間如何通訊?

在併發編程領域,有行程和執行緒兩個概念,在Java語言中說起併發編程,常常是指多執行緒,但是瞭解行程的概念也非常重要:

  • 行程是操作系統的資源調度物體,有自己的記憶體地址空間和運行環境;

  • 執行緒一般被稱為輕量級的行程,執行緒和行程一樣,也有自己的運行環境,但是創建一個執行緒要需要的資源比創建一個行程要少。執行緒存在於行程之中——每個行程至少有一個執行緒。一個行程下的多個執行緒之間可以共享行程的資源,包括記憶體空間和打開的檔案。

  • 行程跟程式(programs)、應用(applications)具備相同的含義,行程間通訊依靠IPC資源,例如管道(pipes)、套接字(sockets)等;

  • 執行緒間通訊依靠JVM提供的API,例如wait方法、notify方法和notifyAll方法,執行緒間還可以通過共享的主記憶體來進行值的傳遞;

3
HashMap的資料結構是什麼?如何實現的?和HashTable、ConcurrentHashMap的區別?
  • 在Java 8中,HashMap的資料結構是由Node作為元素組成的陣列:(1)如果有多個值hash到同一個桶中,則組織成一個鏈表,而且,當這個鏈表的節點個數超過某個值(TREEIFY_THRESHOLD引數指定)時,則將這個鏈表重構為一個二叉樹;(2)如果發現map中的元素個數超過了threshold,則進行空間擴容——二倍空間。

  • HashMap和HashTable的資料結構和操作基本相同,區別是前者是非執行緒安全,並且HashMap接受value為null。

  • ConcurrentHashMap和HashTable一樣,都是執行緒安全的,但是區別是:HashTable每次操作都會鎖住整個表結構——導致一次只能有一個執行緒訪問HashTable物件,而ConcurrentHashMap不會,只會鎖住某個節點,只有在涉及到size的操作時才會鎖整個表結構。

 

 

4
Cookie和Session的區別

 

  • HTTP是無狀態協議,但是在實際應用中有跟蹤客戶端狀態的需求,Cookie和Session是兩種不同的實現方案。

  • Cookie儲存在客戶端,Session儲存在服務端

  • Cookie沒有Session安全,侵入者可以通過分析客戶端的cookie信息侵入網站;

  • 使用Session儲存重要信息,使用Cookie儲存不那麼重要的信息;

  • 使用Session方案時,常常需要依賴Cookie傳遞SID的值,如果客戶端禁用了Cookie,則轉而採取URL重寫技術(但是這種技術有安全風險);

 

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 MAX_ARRAY_SIZE = Integer.MAX_VALUE - 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 - MAX_ARRAY_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 > MAX_ARRAY_SIZE) ?
          Integer.MAX_VALUE :
          MAX_ARRAY_SIZE;
  }

在代碼中,如果預先知道需要增加大量元素,則可以提前對當前的可變陣列呼叫ensureCapacity方法,可以避免多次遞增的記憶體重新分配;

 

7
equals、hashcode等Object類中一些方法的討論?
  • 覆寫equals方法的時候,也必須覆寫hashcode方法;

  • 編寫equals方法後,檢查是否符合:對稱性、傳遞性、一致性、自反性和非空性

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記憶體模型

  • Java虛擬機規範定義Java記憶體模型,嘗試屏蔽掉各種硬體和操作系統的訪問差異;

  • JVM記憶體模型的標的:定義程式中各個變數的訪問規則,即在虛擬機中將變數儲存到記憶體和從記憶體取出來這樣的細節;

  • volatile關鍵字:當一個變數用volatile關鍵字限定後,會有兩個語意:(1)當這個變數的值被修改後,會立即掃清到主記憶體中,對其他執行緒可見;當某個執行緒讀取這個變數的時候,也會重新將主記憶體中的資料刷一份到工作記憶體中來。但是,如果多執行緒操作這個變數的計算中,後一個值依賴前一個值,就還是會有併發問題,說明volatile不具備原子性;(2)禁止指令重排優化,觀察voatile變數對應的位元組碼檔案,會發現變數的操作指令後面加了一句lock addl $0x0,(%esp)的操作,這個操作相當於一個記憶體屏障。

  • synchronized關鍵字:當一個執行緒對一個變數加鎖的時候,就會清空這個變數在當前工作記憶體中的值,因此該關鍵字同時滿足了可見性和原子性。

13
Java運行時資料區

  • 程式計數器(PC):Java執行緒私有,類似於操作系統里的PC計數器,用於指定下一條需要執行的位元組碼的地址;

  • Java虛擬機棧:Java執行緒私有,虛擬機展描述的是Java方法執行的記憶體模型:每個方法在執行的時候,都會創建一個棧幀用於儲存區域性變數、運算元、動態鏈接、方法出口等信息;每個方法呼叫都意味著一個棧幀在虛擬機棧中入棧到出棧的過程;

  • 本地方法棧:和Java虛擬機棧的作用類似,區別是該該區域為JVM呼叫到的本地方法服務;

  • 堆(Heap):所有執行緒共享的一塊區域,垃圾收集器管理的主要區域。目前主要的垃圾回收演算法都是分代收集,因此該區域還可以細分為如下區域:

    • 年輕代:(1)Eden空間;(2)From Survivor空間1,From Survivor空間2,用於儲存在Young gc過程中幸存的物件;

    • 老年代

  • 方法區:各個執行緒共享的一個區域,用於儲存虛擬機加載的類信息、常量、靜態變數等信息;

    • 運行時常量池:方法區的一部分,用於存放編譯器生成的各種字面量和符號取用;

14
事務的實現原理
  • 事務的特性:ACID——原子性、一致性、隔離性和持久性

  • Spring中的事務管理?Spring事務管理那些事

  • MySQL中的事務?事務的隔離級別和鎖,參考何登成的MySQL的加鎖處理分析

    赞(0)

    分享創造快樂