成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

真等不及了,沖阿里去了!

開發 前端
聯合索引遵循最左匹配原則,即在查詢時,只有按照索引字段的順序從最左邊開始連續使用索引字段,索引才會被使用。因此,根據最常用作查詢條件的字段放在聯合索引的最左邊,可以提高索引的利用率。

大家好,我是小林。

阿里巴巴上周開啟了 25 屆的實習招聘,阿里是Java 一線大廠,到底面試難度如何呢?同學們準備好了嗎?

今天分享一位校招同學阿里的 Java 后端開發的面經,阿里的風格會比較關注 Java 和后端組件,而網絡和系統考察通常都比較少的,所以準備面阿里的同學 Java 和后端組件這兩方面的要準備好。

這次的 Java 后端開發的面經,考察的知識點, 針對八股文的涉及的內容,我幫大家列了一下:

  • Java 基礎:面向對象、多態、重載
  • Java 集合:HashMap 連環問、紅黑樹
  • Java并發:volatile、Synchronized、ReentrantLock
  • Java 線程池:線程池參數、核心線程數設置
  • MySQL:索引結構、建立索引規則、explain、聯合索引

八股這塊一共問了 30 分鐘,其余時間問了項目方面的內容。

Java基礎

講一下Java面向對象的特點

封裝、繼承、多態是Java面向對象編程的三大特點。

  • 封裝(Encapsulation):封裝是面向對象編程的基本特點之一,它將數據和方法封裝在對象內部,隱藏對象的內部實現細節,只暴露必要的接口供外部訪問。通過封裝,可以實現信息的隱藏和保護,提高代碼的安全性和可靠性。
  • 繼承(Inheritance):繼承是面向對象編程的重要特點,它允許一個類(子類)繼承另一個類(父類)的屬性和方法。子類可以重用父類的代碼,并可以通過擴展和重寫來增加新的功能或修改現有功能。繼承提高了代碼的復用性和可維護性,同時也體現了類與類之間的關系。
  • 多態(Polymorphism):多態是面向對象編程的核心概念之一,它允許不同對象對同一消息作出不同的響應。在Java中,多態性通過方法重載和方法重寫來實現。方法重載是指在同一個類中可以定義多個同名方法,但參數列表不同;方法重寫是指子類可以重寫父類的方法,實現不同的行為。多態性提高了代碼的靈活性和擴展性,使得程序更易于理解和維護。

用過“多態”嗎?舉一個具體例子

一個具體的例子是,假設有一個動物類(Animal)和它的兩個子類:狗類(Dog)和貓類(Cat)。它們都有一個名為“makeSound”的方法,但是每種動物發出的聲音是不同的。

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("Woof");
    }
}

class Cat extends Animal {
    public void makeSound() {
        System.out.println("Meow");
    }
}

public class PolymorphismExample {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();
        
        dog.makeSound(); // 輸出:Woof
        cat.makeSound(); // 輸出:Meow
    }
}

通過多態性,可以創建一個Animal類型的引用指向一個具體的Dog或Cat對象。當調用這個引用的“makeSound”方法時,根據實際指向的對象類型,會執行相應子類的方法,從而實現不同動物發出不同聲音的效果。這樣就體現了多態的特性,同一個方法調用可以產生不同的行為,提高了代碼的靈活性和可擴展性。

多態和重載有什么關系?

重載是一種編譯時多態,而多態是一種運行時多態。兩者都是實現多態性的方式,但發生的時間點和機制不同。

  • 重載是指在同一個類中,方法名相同但參數列表不同的情況,通過參數個數、類型或順序的不同來區分不同的方法。重載是靜態綁定的概念,編譯器在編譯期間根據方法的參數列表來確定調用哪個方法。
  • 多態是指同一個方法名可以在不同的類中有不同的實現,不同的子類可以重寫父類的方法,通過父類引用指向子類對象時,根據實際對象的類型來確定調用哪個方法。多態是動態綁定的概念,運行時根據對象的實際類型來確定調用哪個方法。

Java集合

Java中的HashMap了解嗎?

了解的,HashMap是Java中常用的一種數據結構,它基于哈希表實現,用于存儲鍵值對

聊聊HashMap的底層結構

在 JDK 1.7 版本之前, HashMap 數據結構是數組和鏈表,HashMap通過哈希算法將元素的鍵(Key)映射到數組中的槽位(Bucket)。如果多個鍵映射到同一個槽位,它們會以鏈表的形式存儲在同一個槽位上,因為鏈表的查詢時間是O(n),所以沖突很嚴重,一個索引上的鏈表非常長,效率就很低了。

所以在 JDK 1.8 版本的時候做了優化,當一個鏈表的長度超過8的時候就轉換數據結構,不再使用鏈表存儲,而是使用紅黑樹,查找時使用紅黑樹,時間復雜度O(log n),可以提高查詢性能,但是在數量較少時,即數量小于6時,會將紅黑樹轉換回鏈表。

圖片圖片

為什么要引入紅黑樹,而不用其他樹?

  • 為什么不使用二叉排序樹?問題主要出現在二叉排序樹在添加元素的時候極端情況下會出現線性結構。比如由于二叉排序樹左子樹所有節點的值均小于根節點的特點,如果我們添加的元素都比根節點小,會導致左子樹線性增長,這樣就失去了用樹型結構替換鏈表的初衷,導致查詢時間增長。所以這是不用二叉查找樹的原因。
  • 為什么不使用平衡二叉樹呢?紅黑樹不追求"完全平衡",而而AVL是嚴格平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多。紅黑樹讀取略遜于AVL,維護強于AVL,空間開銷與AVL類似,內容極多時略優于AVL,維護優于AVL。

基本上主要的幾種平衡樹看來,紅黑樹有著良好的穩定性和完整的功能,性能表現也很不錯,綜合實力強,在諸如STL的場景中需要穩定表現。

HashMap會出現紅黑樹一直增高變成無限高的情況嗎?

不能無限增長。當集合中的節點數超過了閾值,HashMap會進行擴容,這時原始的紅黑樹節點會被打散,可能會退化成鏈表結構。

HashMap讀和寫的時間復雜度是多少?

HashMap的讀取(查找)和寫入(插入、更新、刪除)操作的時間復雜度均為O(1),即常數時間復雜度。

這是因為HashMap內部使用哈希表來存儲鍵值對,通過計算鍵的哈希值可以直接定位到對應的存儲位置,從而實現快速的讀取和寫入操作。

在理想情況下,HashMap可以在常數時間內完成查找和插入操作,具有高效的性能表現。

HashMap是線程安全的嗎?怎么解決?

不是線程安全的。

  • JDK 1.7 HashMap 采用數組 + 鏈表的數據結構,多線程背景下,在數組擴容的時候,存在 Entry 鏈死循環和數據丟失問題。
  • JDK 1.8 HashMap 采用數組 + 鏈表 + 紅黑二叉樹的數據結構,優化了 1.7 中數組擴容的方案,解決了 Entry 鏈死循環和數據丟失問題。但是多線程背景下,put 方法存在數據覆蓋的問題。

解決的方式:

  • 使用ConcurrentHashMap:ConcurrentHashMap是Java提供的線程安全的哈希表實現,它通過分段鎖(Segment)和CAS操作來保證線程安全性,適用于高并發環境。
  • 使用Collections.synchronizedMap:可以通過Collections工具類中的synchronizedMap方法來創建一個線程安全的HashMap,該方法會返回一個同步的Map對象,但性能可能不如ConcurrentHashMap。

解決線程安全問題還有哪些辦法?

  • 使用同步關鍵字synchronized:可以通過在方法或代碼塊上使用synchronized關鍵字來實現線程安全,確保同一時刻只有一個線程可以訪問共享資源。
  • 使用volatile關鍵字:可以使用volatile關鍵字修飾變量,保證變量的可見性,即一個線程修改了變量的值,其他線程能立即看到最新值,從而避免數據不一致的問題。
  • 使用線程安全的工具類:Java中提供了諸如AtomicInteger、AtomicLong、CountDownLatch等線程安全的工具類,可以幫助解決并發場景下的線程安全性問題。
  • 使用并發容器:Java中提供了多種線程安全的并發容器,如ConcurrentLinkedQueue、CopyOnWriteArrayList等,可以替代傳統的非線程安全容器來解決多線程并發訪問問題。

Java并發

volatile關鍵字是如何保證內存可見性的?底層是怎么實現的?

volatile關鍵字通過兩種機制來保證內存可見性:

  • 禁止指令重排序:在程序運行時,為了提高性能,編譯器和處理器可能會對指令進行重排序,這可能會導致變量的更新操作被延遲執行或者亂序執行,從而使得其他線程無法立即看到最新的值。使用volatile關鍵字修飾的變量會禁止指令重排序,保證變量的更新操作按照代碼順序執行。
  • 內存屏障(Memory Barrier):在多核處理器架構下,每個線程都有自己的緩存,volatile關鍵字會在寫操作后插入寫屏障(Write Barrier),在讀操作前插入讀屏障(Read Barrier),確保變量的更新能夠立即被其他線程看到,保證內存可見性。

通過禁止指令重排序和插入內存屏障,volatile關鍵字能夠保證被修飾變量的更新操作對其他線程是可見的,從而有效解決了多線程環境下的內存可見性問題。

為什么需要保證內存可見性?

如果不保證內存可見性,就會出現數據臟讀,一個線程修改了共享變量的值,但其他線程無法立即看到最新值,導致其他線程讀取到了過期數據,從而產生錯誤的結果。

通過保證內存可見性,避免數據不一致性和并發訪問帶來的問題,保證程序的正確性和穩定性。

volatile為什么要禁止指令重排,能舉一個具體的指令重排出現問題的例子嗎

禁止指令重排是為了確保程序的執行順序與代碼編寫順序一致,特別是在多線程環境下,避免出現意外的結果。具體來說,如果不禁止指令重排,可能會導致以下問題:

舉例來說,假設有如下代碼片段:

int a = 0;
boolean flag = false;

// 線程1
a = 1;
flag = true;

// 線程2
if (flag) {
    System.out.println(a);
}

如果發生指令重排,可能會導致線程2在判斷flag時先于a的賦值操作,那么線程2就會打印出0,而不是預期的1。這種情況下,禁止指令重排可以確保線程2在看到flag為true時,也能看到a被正確賦值為1,避免出現問題。

因此,通過禁止指令重排,可以保證程序的執行順序符合代碼邏輯,避免出現意外的行為,特別是在涉及多線程并發的情況下更為重要。

Synchronized的底層原理是什么?鎖升級的過程了解嗎?

  • 底層實現:Synchronized關鍵字底層是使用monitor對象鎖實現的,每一個對象關聯一個monitor對象,而monitor對象可以看成是一個對象鎖,它采用互斥的方式讓同一時刻至多只有一個線程能持有對象鎖,其他線程再想獲取這個對 象鎖時會被阻塞住,這樣就能保證擁有鎖的線程可以安全的執行臨界區的代碼。
  • 鎖升級:是指JVM根據鎖的競爭情況和對象的狀態,將對象的鎖從偏向鎖、輕量級鎖升級為重量級鎖的過程。偏向鎖是指針對無競爭的情況下,鎖會偏向于第一個獲取鎖的線程;輕量級鎖是指針對短時間內只有一個線程競爭鎖的情況下,使用CAS操作來避免阻塞;重量級鎖是指多個線程競爭同一個鎖時,通過操作系統的互斥量來實現線程阻塞和喚醒。鎖升級的過程是為了提高多線程并發訪問的效率和性能。

線程是怎么確定拿到鎖的?

線程確定拿到鎖的過程:是通過檢查鎖的狀態并嘗試獲取鎖來實現的。在JVM中,鎖信息具體是存放在Java對象頭中的。

當一個線程嘗試進入synchronized代碼塊或方法時,JVM會檢查對應對象的鎖狀態。如果對象的鎖未被其他線程持有,即鎖狀態為可獲取,那么該線程將成功獲取鎖并進入臨界區執行代碼。

鎖信息具體放到哪的?

鎖的狀態信息是Java對象頭中的 Mark Word 字段,這個字段對象關于鎖的信息、垃圾回收信息等。

圖片圖片

Java 對象在內存中的表示

JVM通過操作對象的頭部信息來實現鎖的獲取、釋放以及等待隊列的管理。當線程成功獲取鎖后,對象的頭部信息會被更新為當前線程的標識,表示該線程擁有了這個鎖。

其他線程在嘗試獲取同一個鎖時,會檢查對象的頭部信息,如果鎖已經被其他線程持有,它們將會被阻塞直到鎖被釋放。

Synchronized加鎖和ReentrantLock加鎖有什么區別?

synchronized 和 ReentrantLock 都是 Java 中提供的可重入鎖:

  • 用法不同:synchronized 可用來修飾普通方法、靜態方法和代碼塊,而 ReentrantLock 只能用在代碼塊上。
  • 獲取鎖和釋放鎖方式不同:synchronized 會自動加鎖和釋放鎖,當進入 synchronized 修飾的代碼塊之后會自動加鎖,當離開 synchronized 的代碼段之后會自動釋放鎖。而 ReentrantLock 需要手動加鎖和釋放鎖
  • 鎖類型不同:synchronized 屬于非公平鎖,而 ReentrantLock 既可以是公平鎖也可以是非公平鎖。
  • 響應中斷不同:ReentrantLock 可以響應中斷,解決死鎖的問題,而 synchronized 不能響應中斷。
  • 底層實現不同:synchronized 是 JVM 層面通過監視器實現的,而 ReentrantLock 是基于 AQS 實現的。

Java 線程池

線程池了解過嗎?有哪些核心參數?

了解過的,線程池是為了減少頻繁的創建線程和銷毀線程帶來的性能損耗。

線程池分為核心線程池,線程池的最大容量,還有等待任務的隊列,提交一個任務,如果核心線程沒有滿,就創建一個線程,如果滿了,就是會加入等待隊列,如果等待隊列滿了,就會增加線程,如果達到最大線程數量,如果都達到最大線程數量,就會按照一些丟棄的策略進行處理。

圖片圖片

線程池的構造函數有7個參數:

圖片圖片

  • corePoolSize:線程池核心線程數量。默認情況下,線程池中線程的數量如果 <= corePoolSize,那么即使這些線程處于空閑狀態,那也不會被銷毀。
  • maximumPoolSize:線程池中最多可容納的線程數量。當一個新任務交給線程池,如果此時線程池中有空閑的線程,就會直接執行,如果沒有空閑的線程且當前線程池的線程數量小于corePoolSize,就會創建新的線程來執行任務,否則就會將該任務加入到阻塞隊列中,如果阻塞隊列滿了,就會創建一個新線程,從阻塞隊列頭部取出一個任務來執行,并將新任務加入到阻塞隊列末尾。如果當前線程池中線程的數量等于maximumPoolSize,就不會創建新線程,就會去執行拒絕策略。
  • keepAliveTime:當線程池中線程的數量大于corePoolSize,并且某個線程的空閑時間超過了keepAliveTime,那么這個線程就會被銷毀。
  • unit:就是keepAliveTime時間的單位。
  • workQueue:工作隊列。當沒有空閑的線程執行新任務時,該任務就會被放入工作隊列中,等待執行。
  • threadFactory:線程工廠。可以用來給線程取名字等等
  • handler:拒絕策略。當一個新任務交給線程池,如果此時線程池中有空閑的線程,就會直接執行,如果沒有空閑的線程,就會將該任務加入到阻塞隊列中,如果阻塞隊列滿了,就會創建一個新線程,從阻塞隊列頭部取出一個任務來執行,并將新任務加入到阻塞隊列末尾。如果當前線程池中線程的數量等于maximumPoolSize,就不會創建新線程,就會去執行拒絕策略。

為什么核心線程滿了之后是先加入阻塞隊列而不是直接加到總線程?

  • 線程池創建線程需要獲取mainlock這個全局鎖,會影響并發效率,所以使用阻塞隊列把第一步創建核心線程與第三步創建最大線程隔離開來,起一個緩沖的作用。
  • 引入阻塞隊列,是為了在執行execute()方法時,盡可能的避免獲取全局鎖。

核心線程數一般設置為多少?

假設機器有N個CPU:

  • 如果是CPU密集型應用,則線程池大小設置為N+1,線程的應用場景:主要是復雜算法
  • 如果是IO密集型應用,則線程池大小設置為2N+1,線程的應用場景:主要是:數據庫數據的交互,文件上傳下載,網絡數據傳輸等等

對于同時有計算工作和IO工作的任務,應該考慮使用兩個線程池,一個處理計算任務,一個處理IO任務,分別對兩個線程池按照計算密集型和IO密集型來設置線程數。

IO密集型的線程數為什么一般設置為2N+1?

在IO密集型任務中,線程通常會因為IO操作而阻塞,此時可以讓其他線程繼續執行,充分利用CPU資源。設置為2N+1可以保證在有多個線程阻塞時,仍有足夠的線程可以繼續執行。

MySQL

聊聊MySQL的索引結構,為什么使用B+樹而不用B樹?

MySQL 默認的存儲引擎 InnoDB 采用的是 B+ 作為索引的數據結構。

圖片圖片

原因有:

  • B+ 樹的非葉子節點不存放實際的記錄數據,僅存放索引,因此數據量相同的情況下,相比存儲即存索引又存記錄的 B 樹,B+樹的非葉子節點可以存放更多的索引,因此 B+ 樹可以比 B 樹更「矮胖」,查詢底層節點的磁盤 I/O次數會更少。
  • B+ 樹有大量的冗余節點(所有非葉子節點都是冗余索引),這些冗余索引讓 B+ 樹在插入、刪除的效率都更高,比如刪除根節點的時候,不會像 B 樹那樣會發生復雜的樹的變化;
  • B+ 樹葉子節點之間用鏈表連接了起來,有利于范圍查詢,而 B 樹要實現范圍查詢,因此只能通過樹的遍歷來完成范圍查詢,這會涉及多個節點的磁盤 I/O 操作,范圍查詢效率不如 B+ 樹。

你是怎么建立索引的?一般是建立哪些字段的索引呢?

索引最大的好處是提高查詢速度,我經常針對下面場景來建立索引:

  • 字段有唯一性限制的,比如商品編碼;
  • 經常用于 WHERE 查詢條件的字段,這樣能夠提高整個表的查詢速度,如果查詢條件不是一個字段,可以建立聯合索引。
  • 經常用于 GROUP BY 和 ORDER BY 的字段,這樣在查詢的時候就不需要再去做一次排序了,因為我們都已經知道了建立索引之后在 B+Tree 中的記錄都是排序好的。

怎么確定語句是否走了索引?

可以通過 explian查看執行計劃來確認。

圖片圖片

img

對于執行計劃,參數有:

  • possible_keys 字段表示可能用到的索引;
  • key 字段表示實際用的索引,如果這一項為 NULL,說明沒有使用索引;
  • key_len 表示索引的長度;
  • rows 表示掃描的數據行數。
  • type 表示數據掃描類型,我們需要重點看這個。

如果 typy=all,代表沒有走索引,進行了全表掃描。如果 key 不為 null,說明用到了索引。

如果要建立聯合索引,字段的順序有什么需要注意嗎?

  • 最左匹配原則:聯合索引遵循最左匹配原則,即在查詢時,只有按照索引字段的順序從最左邊開始連續使用索引字段,索引才會被使用。因此,根據最常用作查詢條件的字段放在聯合索引的最左邊,可以提高索引的利用率。
  • 區分度高的字段放在前面:將區分度高的字段放在聯合索引的前面,可以減少索引的掃描范圍,提高查詢效率。

責任編輯:武曉燕 來源: 小林coding
相關推薦

2015-01-06 11:08:47

CES2015物聯網蘋果配件

2013-12-17 10:12:40

微軟CEO

2024-03-08 17:37:47

2023-11-10 17:04:33

2020-09-07 11:36:25

TikTok

2025-04-29 09:03:00

2021-12-06 09:35:38

英偉達人工智能軟件

2017-08-16 09:55:36

2012-05-18 14:41:04

傲游歌會

2013-10-21 09:34:26

4GTD-LTE中移動

2021-06-30 06:49:49

Windows 11操作系統微軟

2013-12-04 09:31:35

小米路由器小米移動電源雷軍

2021-01-19 05:27:44

HTTPSECDHE算法

2023-11-14 07:40:36

阿里云服務中斷事件

2013-12-15 11:03:59

Windows 9概念圖

2018-01-18 10:36:05

微信

2023-04-28 17:24:02

2011-05-19 13:09:50

Fedora 15

2011-09-07 15:57:18

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: av影音资源 | 国产一区二区三区视频 | 亚洲视频在线观看 | 亚洲狠狠丁香婷婷综合久久久 | 亚洲激情综合网 | 久久精品av麻豆的观看方式 | 综合久久综合久久 | 91精品中文字幕一区二区三区 | 日韩中字幕 | 亚洲在线一区二区 | 91精品国产综合久久久久久漫画 | 亚洲综合无码一区二区 | 成人av影院 | 欧美日韩中文在线 | 亚洲国产欧美91 | 亚洲成人一区二区 | 96久久久久久 | 91精品在线播放 | 国产精品1区 | 久久精品亚洲精品国产欧美 | 成人在线免费电影 | 国产精品久久久久久久久久久久 | 久久久在线视频 | 午夜影院在线免费观看视频 | 国产精品美女久久久久久不卡 | 欧美久久一级特黄毛片 | 日日干天天操 | 午夜精品三区 | 91网站在线播放 | 亚洲精品久久久久久国产精华液 | av网站观看 | 成人做爰www免费看视频网站 | 91精品国产91久久久久久吃药 | 欧美日韩精品一区二区 | 天天操天天干天天爽 | 国产精品视频网 | 一区二区三区国产 | 欧美一区2区三区4区公司 | 精品少妇一区二区三区日产乱码 | 天天综合永久 | 日韩一级免费大片 |