最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java常見面試題以及答案匯總大全(2025最新版)

 更新時間:2026年04月14日 08:40:23   作者:獨角鯨網(wǎng)絡安全實驗室  
這篇文章主要介紹了Java常見面試題及答案匯總的相關資料,適合準備求職、或者有跳槽想法的初/中/高級程序員,也合適想要對自己技術棧查缺補漏、不斷完善成長的人,需要的朋友可以參考下

一、Java基礎語法與核心特性

1. Java的核心特性有哪些?

答案

  • 跨平臺性(Write Once, Run Anywhere):通過JVM(Java虛擬機)實現(xiàn),字節(jié)碼文件可在任意支持JVM的操作系統(tǒng)運行;
  • 面向對象(OOP):封裝、繼承、多態(tài)三大核心特性;
  • 安全性:支持沙箱機制、字節(jié)碼校驗、權限控制(如文件IO權限);
  • 健壯性:自動垃圾回收(GC)避免內存泄漏,強類型檢查、異常處理機制減少運行時錯誤;
  • 分布式:支持RMI(遠程方法調用)、HTTP協(xié)議,便于開發(fā)分布式應用;
  • 多線程:內置多線程API,支持并發(fā)編程。

2. 基本數(shù)據(jù)類型與包裝類的區(qū)別?

答案

維度基本數(shù)據(jù)類型(如int、float)包裝類(如Integer、Float)
本質原始值,無對象屬性引用類型,繼承Object類
默認值有(如int默認0,boolean默認false)無,默認null
適用場景簡單運算、局部變量,效率高集合框架(如List)、泛型、需要null值的場景
緩存機制部分包裝類(Integer[-128~127]、Byte、Short等)有常量池緩存

關鍵考點

  • 自動裝箱/拆箱:Java 5+特性,編譯器自動完成基本類型與包裝類的轉換(如int i = new Integer(10)→拆箱,Integer j = 10→裝箱);
  • 緩存陷阱:Integer a = 127; Integer b = 127;a == b為true(復用緩存);Integer c = 128; Integer d = 128;c == d為false(新建對象),需用equals()比較值。

3. String、StringBuffer、StringBuilder的區(qū)別?

答案
核心差異在于可變性線程安全

  • String:不可變(底層是final修飾的char數(shù)組/JDK9+ byte數(shù)組),每次修改都會創(chuàng)建新對象,效率低;
  • StringBuffer:可變,線程安全(方法加synchronized鎖),適用于多線程環(huán)境的字符串拼接;
  • StringBuilder:可變,線程不安全,效率高于StringBuffer,適用于單線程環(huán)境的字符串拼接。

底層原理
String的不可變性源于private final char value[](JDK8),final修飾數(shù)組引用不可變,且數(shù)組無暴露修改接口;StringBuffer和StringBuilder繼承AbstractStringBuilder,底層是可變char數(shù)組,擴容機制為:默認初始容量16,當長度超過容量時,新容量=原容量×2+2,不足則直接擴容到所需長度。

4. final關鍵字的三種用法?

答案

  • 修飾類:類不可被繼承(如String、Math),子類無法擴展其功能;
  • 修飾方法:方法不可被重寫,可防止子類修改父類核心邏輯;
  • 修飾變量:變量不可被重新賦值(基本類型:值不可變;引用類型:引用地址不可變,但對象內容可修改)。

易錯點
final int[] arr = {1,2,3}; arr[0] = 4; 合法(數(shù)組內容可變);arr = new int[5]; 非法(引用地址不可變)。

5. 接口(Interface)與抽象類(Abstract Class)的區(qū)別?

答案

維度接口(Interface)抽象類(Abstract Class)
繼承方式多實現(xiàn)(一個類可實現(xiàn)多個接口)單繼承(一個類只能繼承一個抽象類)
成員變量只能是public static final常量可包含普通變量、靜態(tài)變量、常量
成員方法JDK8前:只能是抽象方法;JDK8+:支持default/static方法;JDK9+:支持private方法可包含抽象方法、普通方法、靜態(tài)方法
構造方法有(不能實例化,供子類調用)
設計目的定義行為規(guī)范,解耦(如List接口)定義類的模板,復用代碼(如HttpServlet)

應用場景

  • 接口:不同類需統(tǒng)一行為但實現(xiàn)不同(如Runnable接口);
  • 抽象類:同類組件共享核心邏輯(如AbstractList封裝List的公共方法)。

6. Java異常體系的核心結構?

答案

  • 頂層父類:Throwable,包含兩個核心子類:
    1. Error:嚴重錯誤(如OutOfMemoryError、StackOverflowError),程序無法恢復,無需捕獲;
    2. Exception:可處理的異常,分為:
      • 受檢異常(Checked Exception):編譯時必須捕獲(如IOException、SQLException);
      • 非受檢異常(Unchecked Exception):運行時異常(如NullPointerException、ArrayIndexOutOfBoundsException),繼承自RuntimeException,無需強制捕獲。

異常處理關鍵字

  • try:包裹可能拋出異常的代碼;
  • catch:捕獲并處理異常(可多個catch,按異常子類→父類順序);
  • finally:無論是否拋出異常,都會執(zhí)行(常用于關閉資源,如流、數(shù)據(jù)庫連接);
  • throw:手動拋出異常(如throw new IllegalArgumentException("參數(shù)非法"));
  • throws:聲明方法可能拋出的異常,告知調用者。

最佳實踐

  • 避免捕獲Throwable(包含Error,無法處理);
  • 不要忽略異常(空catch塊);
  • 優(yōu)先使用try-with-resources自動關閉資源(JDK7+,支持實現(xiàn)AutoCloseable接口的類)。

二、Java集合框架

1. 集合框架的核心接口與繼承關系?

答案
Java集合框架核心分為兩大體系(均位于java.util包):

  • 單列集合(Collection):存儲單個元素,核心子接口:
    • List:有序、可重復(如ArrayList、LinkedList、Vector);
    • Set:無序、不可重復(如HashSet、TreeSet、LinkedHashSet);
  • 雙列集合(Map):存儲鍵值對(key-value),核心實現(xiàn)類:HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap。

關鍵特性

  • List:支持索引訪問,可通過get(int index)獲取元素;
  • Set:基于equals()hashCode()保證元素唯一性;
  • Map:key不可重復(重復會覆蓋value),value可重復;JDK8+中Map提供forEach()、computeIfAbsent()等便捷方法。

2. ArrayList與LinkedList的區(qū)別?

答案

維度ArrayList(數(shù)組實現(xiàn))LinkedList(雙向鏈表實現(xiàn))
底層結構動態(tài)數(shù)組(Object[])雙向鏈表(每個節(jié)點存儲prev、next、value)
訪問效率隨機訪問快(O(1)),通過索引直接定位隨機訪問慢(O(n)),需遍歷鏈表
增刪效率尾部增刪快(O(1)),中間增刪慢(需移動數(shù)組元素,O(n))中間增刪快(O(1),只需修改節(jié)點指針),尾部增刪需遍歷到末尾(O(n),可通過last指針優(yōu)化)
內存占用連續(xù)內存,占用少(無額外指針開銷)非連續(xù)內存,每個節(jié)點有額外指針開銷
線程安全不安全不安全

應用場景

  • ArrayList:頻繁查詢、少量增刪(如數(shù)據(jù)展示列表);
  • LinkedList:頻繁中間增刪、隊列/棧實現(xiàn)(如消息隊列)。

3. HashMap的底層實現(xiàn)原理(JDK1.7 vs JDK1.8)?

答案
HashMap是基于“哈希表”的Map實現(xiàn),核心是“數(shù)組+鏈表/紅黑樹”的結構,目的是平衡查詢和增刪效率。

JDK1.7實現(xiàn):

  • 底層:數(shù)組(Entry[])+ 單向鏈表;
  • 存儲流程:
    1. 計算key的hashCode() → 經(jīng)過哈希擾動(hashCode() ^ (hashCode() >>> 16))得到哈希值;
    2. 哈希值 & 數(shù)組長度-1 → 定位數(shù)組索引(保證索引在數(shù)組范圍內);
    3. 若索引位置無元素,直接存儲;若有元素(哈希沖突),采用“頭插法”插入鏈表。

JDK1.8優(yōu)化:

  • 底層:數(shù)組(Node[])+ 單向鏈表 + 紅黑樹(鏈表長度≥8且數(shù)組長度≥64時,鏈表轉為紅黑樹;鏈表長度≤6時,紅黑樹轉回鏈表);
  • 存儲流程:哈希擾動邏輯不變,哈希沖突時采用“尾插法”插入鏈表(避免JDK1.7頭插法導致的鏈表循環(huán)問題);
  • 其他優(yōu)化:
    • 擴容機制:默認初始容量16,負載因子0.75,當元素個數(shù)≥容量×負載因子時,觸發(fā)擴容(新容量=原容量×2);
    • 支持null key和null value(null key的hash值為0,存儲在數(shù)組索引0位置)。

哈希沖突解決

  • 哈希擾動:減少哈希值的高位忽略問題,提升哈希分布均勻性;
  • 鏈地址法:沖突元素以鏈表/紅黑樹形式存儲在同一索引位置。

線程安全問題
HashMap線程不安全,多線程環(huán)境下可能出現(xiàn):

  • JDK1.7:擴容時頭插法導致鏈表循環(huán);
  • JDK1.8:put操作可能覆蓋數(shù)據(jù)。
    解決方案:使用ConcurrentHashMapCollections.synchronizedMap(new HashMap<>())

4. ConcurrentHashMap的線程安全實現(xiàn)(JDK1.7 vs JDK1.8)?

答案
ConcurrentHashMap是HashMap的線程安全版本,核心差異在于鎖機制:

JDK1.7實現(xiàn):

  • 底層:Segment數(shù)組 + HashEntry數(shù)組 + 鏈表;
  • 鎖機制:分段鎖(Segment繼承ReentrantLock),每個Segment對應一把鎖,僅鎖定當前Segment,支持多線程并發(fā)訪問不同Segment,提高并發(fā)度(默認Segment數(shù)量16,支持16個線程并發(fā))。

JDK1.8優(yōu)化:

  • 底層:Node數(shù)組 + 鏈表 + 紅黑樹(與HashMap結構一致);
  • 鎖機制:放棄分段鎖,采用“CAS + synchronized”實現(xiàn)線程安全:
    • 無沖突時:CAS操作原子性插入元素;
    • 有沖突時:synchronized鎖定當前鏈表/紅黑樹的頭節(jié)點,僅鎖定沖突節(jié)點所在的鏈表/樹,并發(fā)度更高;
  • 其他優(yōu)化:支持computeIfAbsent()、forEach()等原子操作,性能優(yōu)于JDK1.7。

5. HashSet的實現(xiàn)原理?

答案
HashSet底層依賴HashMap實現(xiàn),核心邏輯:

  • HashSet的構造方法會創(chuàng)建一個HashMap實例;
  • 存儲元素時:add(E e) → 調用HashMap的put(e, PRESENT),其中PRESENT是一個靜態(tài)空Object(僅占位,不存儲實際值);
  • 元素唯一性:依賴HashMap的key不可重復特性(通過equals()hashCode()判斷);
  • 特性:無序、不可重復、線程不安全,查詢/增刪效率O(1)(無哈希沖突時)。

關鍵考點
自定義對象作為HashSet元素時,必須重寫equals()hashCode(),否則無法保證唯一性(默認使用Object類的方法,比較對象地址)。重寫原則:

  • 兩個對象equals()返回true → hashCode()必須相等;
  • 兩個對象hashCode()相等 → equals()不一定返回true(哈希沖突)。

三、Java多線程與并發(fā)

1. Java創(chuàng)建線程的三種方式?

答案

  • 方式1:繼承Thread類,重寫run()方法(線程執(zhí)行邏輯),調用start()方法啟動線程(底層調用start0() native方法創(chuàng)建操作系統(tǒng)線程);
    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Thread running");
        }
    }
    // 啟動
    new MyThread().start();
  • 方式2:實現(xiàn)Runnable接口,重寫run()方法,將實例傳入Thread類啟動;
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("Runnable running");
        }
    }
    // 啟動
    new Thread(new MyRunnable()).start();
  • 方式3:實現(xiàn)Callable接口,重寫call()方法(支持返回值和拋出異常),通過FutureTask包裝后傳入Thread啟動;
    class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Callable result";
        }
    }
    // 啟動
    FutureTask<String> future = new FutureTask<>(new MyCallable());
    new Thread(future).start();
    String result = future.get(); // 獲取返回值(阻塞直到線程完成)

對比

  • 繼承Thread:無法繼承其他類(Java單繼承),代碼簡單;
  • 實現(xiàn)Runnable/Callable:可繼承其他類,支持多線程共享資源,Callable支持返回值和異常處理。

2. 線程的生命周期與狀態(tài)轉換?

答案
Java線程有6種狀態(tài)(定義在Thread.State枚舉中),狀態(tài)轉換如下:

  • NEW:線程創(chuàng)建后未啟動(未調用start());
  • RUNNABLE:線程啟動后,處于可運行狀態(tài)(包含操作系統(tǒng)的“運行中”和“就緒”);
  • BLOCKED:線程等待同步鎖(如synchronized未獲取鎖時);
  • WAITING:線程無限期等待(如調用Object.wait()、Thread.join()、LockSupport.park(),需其他線程喚醒);
  • TIMED_WAITING:線程限時等待(如調用Thread.sleep(ms)、Object.wait(ms)、Thread.join(ms),超時自動喚醒);
  • TERMINATED:線程執(zhí)行完成或異常終止。

核心轉換路徑
NEWRUNNABLE(start()) → TERMINATED(執(zhí)行完成);
RUNNABLEBLOCKED(競爭鎖失?。?→ RUNNABLE(獲取鎖);
RUNNABLEWAITING/TIMED_WAITING(調用等待方法) → RUNNABLE(被喚醒/超時)。

3. synchronized與Lock的區(qū)別?

答案

維度synchronized(內置鎖)Lock(顯式鎖,如ReentrantLock)
鎖實現(xiàn)JVM層面實現(xiàn)(C++代碼)JDK層面實現(xiàn)(Java代碼)
鎖獲取與釋放自動獲?。ㄟM入同步塊)、自動釋放(退出同步塊/異常)手動獲取(lock())、手動釋放(unlock(),需在finally中執(zhí)行)
鎖類型可重入鎖、非公平鎖(默認),JDK6+支持偏向鎖/輕量級鎖/重量級鎖升級可重入鎖,支持公平鎖/非公平鎖(構造函數(shù)指定)
功能擴展無(僅支持基礎同步)支持中斷鎖(lockInterruptibly())、超時鎖(tryLock(ms))、條件變量(Condition)、讀寫鎖(ReentrantReadWriteLock)
性能JDK6+優(yōu)化后,性能接近Lock高并發(fā)場景下性能更優(yōu),靈活度高

應用場景

  • synchronized:簡單同步場景(如單例模式、簡單方法同步),代碼簡潔,無需手動管理鎖;
  • Lock:復雜并發(fā)場景(如超時獲取鎖、中斷鎖、讀寫分離),如緩存系統(tǒng)、分布式鎖實現(xiàn)。

4. volatile關鍵字的作用?

答案
volatile是Java提供的輕量級同步機制,核心作用有兩個:

  1. 保證可見性:一個線程修改volatile變量后,其他線程能立即看到最新值(禁止CPU緩存,變量讀寫直接操作主內存);
  2. 禁止指令重排序:編譯器和CPU會對指令重排序優(yōu)化,volatile通過內存屏障(Memory Barrier)阻止重排序(如DCL單例模式中,volatile修飾實例變量防止指令重排導致的空指針)。

局限性

  • 不保證原子性:如volatile int i = 0; i++; 非原子操作(包含讀取、加1、寫入三步),多線程下可能出現(xiàn)計數(shù)錯誤,需配合synchronized或AtomicInteger使用;
  • 不能替代鎖:僅適用于“單寫多讀”或“狀態(tài)標記”場景(如volatile boolean flag = false; 控制線程啟停)。

經(jīng)典應用:雙重校驗鎖(DCL)單例模式:

public class Singleton {
    // volatile禁止指令重排,防止instance未初始化完成就被其他線程獲取
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) { // 第一次校驗(無鎖,提高效率)
            synchronized (Singleton.class) { // 加鎖
                if (instance == null) { // 第二次校驗(防止多線程并發(fā)創(chuàng)建)
                    instance = new Singleton(); // 禁止重排:分配內存→初始化→賦值
                }
            }
        }
        return instance;
    }
}

5. 線程池的核心參數(shù)與工作原理?

答案
Java線程池核心類是ThreadPoolExecutor,基于“池化思想”減少線程創(chuàng)建/銷毀開銷,提高并發(fā)效率。

核心參數(shù)(構造方法):

public ThreadPoolExecutor(
    int corePoolSize,    // 核心線程數(shù)(常駐線程,即使空閑也不銷毀)
    int maximumPoolSize, // 最大線程數(shù)(核心線程+臨時線程的總上限)
    long keepAliveTime,  // 臨時線程空閑時間(超過則銷毀)
    TimeUnit unit,       // keepAliveTime的時間單位
    BlockingQueue<Runnable> workQueue, // 任務阻塞隊列(核心線程滿時,任務入隊)
    ThreadFactory threadFactory,       // 線程創(chuàng)建工廠(自定義線程名稱、優(yōu)先級等)
    RejectedExecutionHandler handler   // 拒絕策略(隊列和最大線程數(shù)都滿時,處理新任務)
)

工作原理:

  1. 提交任務時,若核心線程數(shù)未滿,創(chuàng)建核心線程執(zhí)行任務;
  2. 核心線程滿時,任務加入阻塞隊列;
  3. 隊列滿時,若未達到最大線程數(shù),創(chuàng)建臨時線程執(zhí)行任務;
  4. 臨時線程空閑時間超過keepAliveTime,銷毀臨時線程;
  5. 隊列和最大線程數(shù)都滿時,執(zhí)行拒絕策略。

常見拒絕策略:

  • AbortPolicy(默認):直接拋出RejectedExecutionException
  • CallerRunsPolicy:由提交任務的線程(調用者)執(zhí)行任務;
  • DiscardPolicy:直接丟棄新任務,無異常;
  • DiscardOldestPolicy:丟棄隊列中最舊的任務,加入新任務。

常見線程池(Executors工具類):

  • Executors.newFixedThreadPool(n):固定核心線程數(shù)和最大線程數(shù)(n),隊列無界(LinkedBlockingQueue);
  • Executors.newCachedThreadPool():核心線程數(shù)0,最大線程數(shù)Integer.MAX_VALUE,臨時線程空閑60秒銷毀,隊列同步移交(SynchronousQueue);
  • Executors.newSingleThreadExecutor():核心線程數(shù)1,最大線程數(shù)1,隊列無界,保證任務串行執(zhí)行;
  • Executors.newScheduledThreadPool(n):核心線程數(shù)n,支持定時/延遲執(zhí)行任務(ScheduledFutureTask)。

注意:阿里巴巴Java開發(fā)手冊禁止使用Executors創(chuàng)建線程池,原因是:

  • newFixedThreadPool/newSingleThreadExecutor:隊列無界,可能導致OOM;
  • newCachedThreadPool:最大線程數(shù)無界,可能創(chuàng)建大量線程導致CPU/內存耗盡。
    推薦直接使用ThreadPoolExecutor構造方法,指定合理參數(shù)(如核心線程數(shù)=CPU核心數(shù)±1,隊列使用有界隊列)。

6. ThreadLocal的原理與內存泄漏問題?

答案
ThreadLocal是線程本地存儲工具,允許每個線程擁有獨立的變量副本,避免多線程共享變量的并發(fā)問題。

原理:

  • 底層結構:每個Thread對象持有一個ThreadLocalMap(ThreadLocal的內部類),ThreadLocalMap的key是ThreadLocal實例(弱引用),value是線程本地變量副本;
  • 核心方法:
    • set(T value):獲取當前線程的ThreadLocalMap,將(當前ThreadLocal實例,value)存入;
    • get():獲取當前線程的ThreadLocalMap,根據(jù)當前ThreadLocal實例獲取value,無則調用initialValue()初始化;
    • remove():刪除當前線程的ThreadLocalMap中對應的鍵值對。

內存泄漏問題:

  • 原因:ThreadLocalMap的key是弱引用(WeakReference<ThreadLocal<?>>),當ThreadLocal實例被回收(如外部引用置null),key會變成null,而value是強引用,若線程未結束(如線程池核心線程),value無法被GC回收,導致內存泄漏;
  • 解決方案:
    1. 用完ThreadLocal后調用remove()方法刪除value;
    2. 避免使用靜態(tài)ThreadLocal(生命周期長,易導致內存泄漏);
    3. 線程池場景下,確保任務執(zhí)行完成后清理ThreadLocal變量。

應用場景

  • 存儲線程上下文信息(如用戶登錄信息、數(shù)據(jù)庫連接、事務對象);
  • 避免方法參數(shù)傳遞(如Spring的RequestContextHolder底層使用ThreadLocal存儲HttpServletRequest)。

四、JVM核心原理

1. JVM內存模型(運行時數(shù)據(jù)區(qū))?

答案
JVM運行時數(shù)據(jù)區(qū)分為5個部分(基于JDK8):

    1. 程序計數(shù)器(Program Counter Register):
    • 作用:存儲當前線程執(zhí)行的字節(jié)碼指令地址(行號),線程切換時恢復執(zhí)行位置;
    • 特點:線程私有(每個線程一個),無OOM風險(唯一不會拋出OutOfMemoryError的區(qū)域)。
    1. 虛擬機棧(VM Stack):
    • 作用:存儲線程執(zhí)行方法時的棧幀(包含局部變量表、操作數(shù)棧、方法出口等);
    • 特點:線程私有,棧幀入棧(方法調用)和出棧(方法返回)對應方法執(zhí)行生命周期;
    • 異常:棧深度超過JVM限制→StackOverflowError(如遞歸調用無終止);棧擴展時內存不足→OutOfMemoryError。
    1. 本地方法棧(Native Method Stack):
    • 作用:與虛擬機棧類似,僅支持Native方法(如Thread.start0())的執(zhí)行;
    • 特點:線程私有,可能拋出StackOverflowErrorOutOfMemoryError。
    1. 堆(Heap):
    • 作用:存儲對象實例和數(shù)組,是JVM內存最大的區(qū)域,也是GC的主要區(qū)域;
    • 特點:線程共享,可通過-Xms(初始堆大?。?、-Xmx(最大堆大?。┡渲?;
    • 分區(qū)(邏輯劃分):
      • 年輕代(Young Generation):分為Eden區(qū)、Survivor0(S0)區(qū)、Survivor1(S1)區(qū),比例默認8:1:1;
      • 老年代(Old Generation):存儲存活時間長的對象(年輕代對象多次GC后存活則進入老年代);
      • 元空間(Metaspace,JDK8+):替代永久代,存儲類元信息(類名、方法信息、字段信息),使用本地內存,默認無大小限制(可通過-XX:MetaspaceSize、-XX:MaxMetaspaceSize配置)。
    • 異常:堆內存不足→OutOfMemoryError: Java heap space;元空間不足→OutOfMemoryError: Metaspace。
    1. 方法區(qū)(Method Area):
    • 作用:存儲類元信息、常量池(String常量池JDK7+移至堆)、靜態(tài)變量、即時編譯后的代碼;
    • 特點:線程共享,JDK8前為永久代(PermGen),JDK8后被元空間替代。

2. 垃圾回收(GC)的核心原理?

答案
GC是JVM自動回收堆中無用對象(無引用的對象)的過程,核心目標是釋放內存,避免內存泄漏。

1. 垃圾判定算法:

  • 引用計數(shù)法:給對象添加引用計數(shù)器,引用+1,引用失效-1,計數(shù)器為0則判定為垃圾;缺點:無法解決循環(huán)引用(如A引用B,B引用A,計數(shù)器均為1,無法回收);
  • 可達性分析算法(JVM采用):以“GC Roots”為起點,遍歷對象引用鏈,不可達的對象判定為垃圾;
    • GC Roots包括:虛擬機棧局部變量表中的引用、本地方法棧中的引用、方法區(qū)靜態(tài)變量引用、常量池引用、活躍線程的引用。

2. 常見GC算法:

  • 標記-清除算法(Mark-Sweep):

    • 步驟:標記垃圾對象→清除垃圾對象;
    • 優(yōu)點:簡單高效;
    • 缺點:產(chǎn)生內存碎片,后續(xù)大對象分配可能失敗。
  • 復制算法(Copying):

    • 步驟:將內存分為兩塊(如Eden和S0/S1),標記存活對象→復制到另一塊內存,清除原內存;
    • 優(yōu)點:無內存碎片,分配效率高;
    • 缺點:內存利用率低(僅50%),適合年輕代(存活對象少)。
  • 標記-整理算法(Mark-Compact):

    • 步驟:標記存活對象→將存活對象向內存一端移動→清除另一端垃圾;
    • 優(yōu)點:無內存碎片,內存利用率高;
    • 缺點:移動對象成本高,適合老年代(存活對象多)。
  • 分代收集算法(JVM采用):

    • 原理:根據(jù)對象存活時間劃分代(年輕代、老年代),不同代采用不同GC算法;
    • 年輕代:存活對象少,采用復制算法;
    • 老年代:存活對象多,采用標記-清除或標記-整理算法。

3. 常見GC收集器:

  • Serial收集器:單線程GC,年輕代采用復制算法,老年代采用標記-整理算法,適合單CPU環(huán)境(如客戶端應用);
  • Parallel Scavenge收集器:多線程GC,年輕代復制算法,追求高吞吐量(吞吐量=運行用戶代碼時間/(運行用戶代碼時間+GC時間)),適合服務器應用;
  • ParNew收集器:Parallel Scavenge的多線程版本,支持與CMS收集器配合;
  • CMS收集器(Concurrent Mark Sweep):老年代GC,基于標記-清除算法,并發(fā)收集(與用戶線程同時執(zhí)行),追求低延遲,適合響應時間敏感的應用(如Web應用);缺點:產(chǎn)生內存碎片、并發(fā)開銷大;
  • G1收集器(Garbage-First):JDK9+默認GC,基于標記-整理算法,將堆劃分為多個大小相等的Region,優(yōu)先回收垃圾多的Region,兼顧吞吐量和低延遲,支持大堆內存(如數(shù)十GB);
  • ZGC/Shenandoah收集器:新一代低延遲GC,暫停時間控制在毫秒級以下,支持TB級堆內存。

3. 類加載機制與雙親委派模型?

答案
類加載是將.class字節(jié)碼文件加載到JVM內存,生成Class對象的過程,核心分為5個階段:

1. 類加載流程:

  • 加載(Loading):通過類加載器讀取.class文件,生成二進制字節(jié)流,在堆中創(chuàng)建Class對象;
  • 驗證(Verification):校驗字節(jié)碼合法性(如文件格式、語法、語義、符號引用驗證),防止惡意字節(jié)碼;
  • 準備(Preparation):為類靜態(tài)變量分配內存并設置默認值(如int默認0,boolean默認false),不包含實例變量;
  • 解析(Resolution):將符號引用(如類名、方法名)轉換為直接引用(內存地址);
  • 初始化(Initialization):執(zhí)行類構造器<clinit>()方法(靜態(tài)變量賦值+靜態(tài)代碼塊執(zhí)行),初始化順序:父類→子類,靜態(tài)變量→靜態(tài)代碼塊。

2. 類加載器分類:

  • 啟動類加載器(Bootstrap ClassLoader):C++實現(xiàn),加載JDK核心類庫(如rt.jar),無父加載器;
  • 擴展類加載器(Extension ClassLoader):Java實現(xiàn),加載jre/lib/ext目錄下的類庫;
  • 應用程序類加載器(Application ClassLoader):Java實現(xiàn),加載應用classpath下的類庫(默認類加載器);
  • 自定義類加載器(Custom ClassLoader):繼承ClassLoader類,重寫findClass()方法,用于加載自定義路徑的類(如熱部署、加密類)。

3. 雙親委派模型:

  • 核心規(guī)則:類加載器加載類時,先委托父加載器加載,父加載器無法加載(找不到類)時,才由自身加載;

  • 流程:應用程序類加載器→擴展類加載器→啟動類加載器(頂層),若啟動類加載器無法加載,反向逐級嘗試加載;

  • 作用:

    1. 避免類重復加載(如java.lang.String僅由啟動類加載器加載一次);
    2. 保護核心類庫(防止自定義java.lang.String類替換核心類)。
  • 破壞雙親委派模型的場景:

    • 熱部署(如OSGi框架):需要不同模塊加載同一類的不同版本;
    • JNDI、SPI機制(如JDBC驅動加載):核心類由啟動類加載器加載,需加載應用classpath下的驅動類,通過線程上下文類加載器(Thread Context ClassLoader)實現(xiàn)。

五、Spring核心框架

1. Spring IoC的原理與實現(xiàn)?

答案
IoC(Inversion of Control,控制反轉)是Spring的核心思想,指將對象的創(chuàng)建、依賴注入(DI)的控制權從應用程序轉移到Spring容器,實現(xiàn)解耦。

核心概念:

  • IoC容器:Spring的核心組件(如ApplicationContext、BeanFactory),負責管理Bean的生命周期(創(chuàng)建、初始化、銷毀)和依賴關系;
  • Bean:IoC容器管理的對象(如Service、Dao層對象);
  • 依賴注入(DI):IoC的具體實現(xiàn),容器在創(chuàng)建Bean時自動注入其依賴的其他Bean(無需手動new對象)。

依賴注入的三種方式:

  • 構造方法注入:通過Bean的構造方法傳入依賴,推薦使用(強制依賴,避免空指針);
    @Service
    public class UserService {
        private final UserDao userDao;
        // 構造方法注入(@Autowired可省略,Spring 4.3+支持)
        public UserService(UserDao userDao) {
            this.userDao = userDao;
        }
    }
  • Setter方法注入:通過Setter方法注入依賴,適用于可選依賴;
    @Service
    public class UserService {
        private UserDao userDao;
        @Autowired
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    }
  • 字段注入:直接在字段上添加@Autowired注解,代碼簡潔,但不推薦(無法通過構造方法校驗依賴,不利于單元測試);
    @Service
    public class UserService {
        @Autowired
        private UserDao userDao;
    }

IoC容器初始化流程:

  1. 加載配置文件(如XML、注解@Configuration);
  2. 解析配置,掃描Bean定義(如@Component、@Service@Repository),注冊到BeanDefinitionRegistry;
  3. 實例化Bean(默認單例,懶加載除外);
  4. 依賴注入(DI):通過BeanPostProcessor(后置處理器)自動注入依賴;
  5. 初始化Bean:執(zhí)行@PostConstruct注解方法、InitializingBean接口的afterPropertiesSet()方法、自定義init-method;
  6. Bean就緒,供應用程序調用;
  7. 容器關閉時,銷毀Bean:執(zhí)行@PreDestroy注解方法、DisposableBean接口的destroy()方法、自定義destroy-method。

2. Spring AOP的原理與應用?

答案
AOP(Aspect-Oriented Programming,面向切面編程)是Spring的核心特性,通過“橫切”機制,將日志、事務、權限等通用功能(切面)與業(yè)務邏輯解耦,實現(xiàn)代碼復用。

核心概念:

  • 切面(Aspect):封裝通用功能的類(如日志切面、事務切面),包含通知和切入點;
  • 通知(Advice):切面的具體邏輯(如日志打印、事務提交),分為5種類型:
    • @Before:目標方法執(zhí)行前執(zhí)行;
    • @After:目標方法執(zhí)行后執(zhí)行(無論是否異常);
    • @AfterReturning:目標方法正常返回后執(zhí)行;
    • @AfterThrowing:目標方法拋出異常后執(zhí)行;
    • @Around:環(huán)繞目標方法執(zhí)行(可控制目標方法的執(zhí)行與否,如事務的開始和提交);
  • 切入點(Pointcut):定義切面作用的目標方法(如“所有Service層的方法”),通過表達式(如execution表達式)指定;
  • 連接點(JoinPoint):目標方法的執(zhí)行點(如方法調用、異常拋出),是切入點的具體實例;
  • 織入(Weaving):將切面邏輯融入目標方法的過程,Spring AOP默認采用動態(tài)代理織入。

實現(xiàn)原理:

Spring AOP基于動態(tài)代理實現(xiàn),分為兩種代理方式:

  1. JDK動態(tài)代理:
  • 適用場景:目標類實現(xiàn)接口;
  • 原理:通過java.lang.reflect.Proxy類動態(tài)生成代理類,代理類實現(xiàn)目標接口,并重寫目標方法,在方法中織入切面邏輯;
  • 缺點:僅支持接口代理,無法代理無接口的類。
  1. CGLIB動態(tài)代理:
  • 適用場景:目標類未實現(xiàn)接口;
  • 原理:通過CGLIB(Code Generation Library)字節(jié)碼生成框架,動態(tài)生成目標類的子類,重寫目標方法,織入切面邏輯;
  • 優(yōu)點:支持任意類代理(無需接口),性能優(yōu)于JDK動態(tài)代理(創(chuàng)建代理類開銷大,但執(zhí)行效率高)。

應用場景:

  • 日志記錄:記錄方法調用參數(shù)、返回值、執(zhí)行時間;
  • 事務管理:控制事務的開始、提交、回滾(Spring聲明式事務基于AOP實現(xiàn));
  • 權限校驗:方法執(zhí)行前校驗用戶權限;
  • 異常處理:統(tǒng)一捕獲目標方法的異常并處理。

示例:日志切面

// 切面類
@Aspect
@Component
public class LogAspect {
    // 切入點:所有com.example.service包下的public方法
    @Pointcut("execution(public * com.example.service..*(..))")
    public void servicePointcut() {}
    // 環(huán)繞通知
    @Around("servicePointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 目標方法執(zhí)行前:打印請求參數(shù)
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("方法" + methodName + "調用,參數(shù):" + Arrays.toString(args));
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed(); // 執(zhí)行目標方法
        // 目標方法執(zhí)行后:打印返回值和執(zhí)行時間
        long cost = System.currentTimeMillis() - start;
        System.out.println("方法" + methodName + "返回值:" + result + ",執(zhí)行時間:" + cost + "ms");
        return result;
    }
}

3. Spring事務管理的原理?

答案
Spring事務管理核心是“聲明式事務”(基于AOP)和“編程式事務”(手動編碼),其中聲明式事務是主流用法。

1. 事務的ACID特性:

  • 原子性(Atomicity):事務是不可分割的最小單位,要么全部執(zhí)行,要么全部回滾;
  • 一致性(Consistency):事務執(zhí)行前后,數(shù)據(jù)完整性保持一致(如轉賬前后總金額不變);
  • 隔離性(Isolation):多個事務并發(fā)執(zhí)行時,事務之間相互隔離,互不影響;
  • 持久性(Durability):事務提交后,數(shù)據(jù)修改永久生效(寫入磁盤)。

2. 事務隔離級別(Spring支持):

  • DEFAULT:默認隔離級別(依賴數(shù)據(jù)庫,如MySQL默認REPEATABLE READ);
  • READ_UNCOMMITTED:讀未提交,最低隔離級別,可能出現(xiàn)臟讀、不可重復讀、幻讀;
  • READ_COMMITTED:讀已提交,避免臟讀,可能出現(xiàn)不可重復讀、幻讀(如Oracle默認);
  • REPEATABLE_READ:可重復讀,避免臟讀、不可重復讀,可能出現(xiàn)幻讀(如MySQL默認);
  • SERIALIZABLE:串行化,最高隔離級別,避免所有并發(fā)問題,性能最低。

3. 事務傳播行為(核心,解決事務嵌套問題):

Spring定義了7種傳播行為,常用的有:

  • REQUIRED(默認):如果當前存在事務,加入事務;如果沒有事務,創(chuàng)建新事務;
  • REQUIRES_NEW:無論當前是否存在事務,都創(chuàng)建新事務(新事務與原事務相互獨立,原事務暫停);
  • SUPPORTS:如果當前存在事務,加入事務;如果沒有事務,以非事務方式執(zhí)行;
  • NOT_SUPPORTED:以非事務方式執(zhí)行,如果當前存在事務,暫停原事務;
  • NEVER:以非事務方式執(zhí)行,如果當前存在事務,拋出異常。

4. 聲明式事務實現(xiàn)(基于AOP):

  • 配置方式:通過@EnableTransactionManagement注解啟用事務管理(Spring Boot自動啟用);
  • 核心注解:@Transactional(標注在類或方法上,類級別的注解對所有方法生效);
  • 原理:
    1. @Transactional注解被解析為切面,切入點是標注該注解的方法;
    2. 通知邏輯:通過AOP動態(tài)代理,在目標方法執(zhí)行前開啟事務,執(zhí)行后提交事務,異常時回滾事務;
    3. 事務管理器:Spring通過PlatformTransactionManager接口適配不同數(shù)據(jù)庫(如DataSourceTransactionManager適配JDBC,HibernateTransactionManager適配Hibernate)。

5. 事務失效的常見場景:

  • 方法非public修飾(@Transactional僅對public方法生效);
  • 事務方法內部調用(如A方法調用本類的B方法,B方法的@Transactional失效,因為未經(jīng)過代理類);
  • 異常類型不匹配(默認僅捕獲RuntimeExceptionError, checked異常需通過rollbackFor指定);
  • 手動捕獲異常未拋出(如try-catch異常但未throw,事務無法感知異常,不會回滾);
  • 傳播行為配置錯誤(如NOT_SUPPORTED、NEVER);
  • 數(shù)據(jù)源未配置事務管理器(PlatformTransactionManager未被Spring管理)。

六、數(shù)據(jù)庫與MyBatis

1. JDBC的核心操作步驟?

答案
JDBC(Java Database Connectivity)是Java訪問數(shù)據(jù)庫的標準API,核心步驟如下:

  1. 加載數(shù)據(jù)庫驅動(JDK8+無需手動加載,DriverManager自動掃描);
  2. 建立數(shù)據(jù)庫連接(通過DriverManager.getConnection(url, username, password));
  3. 創(chuàng)建Statement/PreparedStatement對象(執(zhí)行SQL語句);
  4. 執(zhí)行SQL(executeQuery()查詢,executeUpdate()增刪改);
  5. 處理結果集(查詢時通過ResultSet遍歷結果);
  6. 關閉資源(ResultSet、Statement、Connection,需在finally中關閉,避免資源泄漏)。

示例代碼

public void queryUser() {
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
        // 1. 加載驅動(MySQL 8.0+驅動類:com.mysql.cj.jdbc.Driver)
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 2. 建立連接
        String url = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC";
        conn = DriverManager.getConnection(url, "root", "123456");
        // 3. 創(chuàng)建PreparedStatement(預編譯SQL,防止SQL注入)
        String sql = "SELECT id, name FROM user WHERE id = ?";
        pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, 1); // 設置參數(shù)
        // 4. 執(zhí)行查詢
        rs = pstmt.executeQuery();
        // 5. 處理結果集
        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            System.out.println("id: " + id + ", name: " + name);
        }
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        // 6. 關閉資源(逆序關閉)
        try {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
            if (conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

關鍵考點

  • PreparedStatement vs Statement:PreparedStatement支持預編譯SQL、參數(shù)化查詢,防止SQL注入,性能更優(yōu);Statement不支持參數(shù)化,存在SQL注入風險;
  • SQL注入:如SELECT * FROM user WHERE name = '" + name + "'",若name傳入' OR '1'='1,則SQL變?yōu)?code>SELECT * FROM user WHERE name = '' OR '1'='1',查詢所有用戶;PreparedStatement通過參數(shù)綁定避免該問題。

2. MyBatis的核心組件與工作原理?

答案
MyBatis是持久層框架,簡化JDBC操作,通過XML或注解配置SQL語句,無需手動編寫JDBC代碼。

核心組件:

  • SqlSessionFactory:MyBatis核心工廠類,通過SqlSessionFactoryBuilder讀取配置文件(mybatis-config.xml)創(chuàng)建,線程安全;
  • SqlSession:會話對象,封裝數(shù)據(jù)庫連接和事務管理,線程不安全(每次請求創(chuàng)建新實例);
  • Mapper接口:自定義DAO接口,MyBatis通過動態(tài)代理生成實現(xiàn)類,關聯(lián)XML/注解中的SQL;
  • Mapper.xml:配置SQL語句、參數(shù)映射、結果集映射;
  • Configuration:MyBatis全局配置對象,存儲核心配置(如數(shù)據(jù)源、事務管理器、Mapper注冊)。

工作原理:

  1. 加載配置文件:SqlSessionFactoryBuilder讀取mybatis-config.xml和Mapper.xml,解析配置信息(數(shù)據(jù)源、SQL語句、映射規(guī)則);
  2. 創(chuàng)建SqlSessionFactory:通過解析后的配置信息創(chuàng)建SqlSessionFactory(單例模式);
  3. 創(chuàng)建SqlSession:SqlSessionFactory調用openSession()創(chuàng)建SqlSession,默認不自動提交事務;
  4. 獲取Mapper代理對象:SqlSession調用getMapper(Mapper接口.class),通過動態(tài)代理生成Mapper接口的實現(xiàn)類;
  5. 執(zhí)行SQL:調用Mapper接口方法,MyBatis根據(jù)方法名匹配Mapper.xml中的SQL語句,執(zhí)行JDBC操作;
  6. 處理結果集:MyBatis自動將ResultSet映射為Java對象(根據(jù)resultType/resultMap配置);
  7. 提交/回滾事務:執(zhí)行完成后,SqlSession調用commit()提交事務或rollback()回滾事務;
  8. 關閉SqlSession:釋放資源。

核心配置(mybatis-config.xml):

<configuration>
    <!-- 環(huán)境配置(數(shù)據(jù)源、事務管理器) -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/> <!-- 事務管理類型:JDBC/MANAGED -->
            <dataSource type="POOLED"> <!-- 數(shù)據(jù)源類型:POOLED(連接池)/UNPOOLED/JNDI -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 注冊Mapper.xml -->
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

Mapper.xml示例:

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 結果集映射(數(shù)據(jù)庫字段→Java對象屬性) -->
    <resultMap id="UserResultMap" type="com.example.entity.User">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
    </resultMap>
    <!-- 查詢用戶 -->
    <select id="selectUserById" parameterType="int" resultMap="UserResultMap">
        SELECT id, name, age FROM user WHERE id = #{id}
    </select>
    <!-- 新增用戶 -->
    <insert id="insertUser" parameterType="com.example.entity.User">
        INSERT INTO user (name, age) VALUES (#{name}, #{age})
    </insert>
</mapper>

3. MyBatis的一級緩存與二級緩存?

答案

MyBatis提供緩存機制,減少數(shù)據(jù)庫查詢次數(shù),提升性能,分為一級緩存和二級緩存。

一級緩存(SqlSession級別,默認開啟):

  • 作用范圍:同一個SqlSession內,多次執(zhí)行相同的SQL查詢(參數(shù)相同),僅第一次查詢數(shù)據(jù)庫,后續(xù)從緩存獲取結果;
  • 實現(xiàn)原理:SqlSession內部維護一個HashMap,key為緩存鍵(由SQL語句、參數(shù)、RowBounds、環(huán)境等組成),value為查詢結果;
  • 失效場景:
    • 執(zhí)行insert/update/delete操作(會清空一級緩存);
    • 調用SqlSession.clearCache()手動清空;
    • SqlSession關閉或提交事務。

二級緩存(Mapper級別,默認關閉):

  • 作用范圍:同一個Mapper接口(命名空間)下,多個SqlSession共享緩存;
  • 啟用方式:
    1. 在mybatis-config.xml中開啟全局緩存(默認開啟,可省略):<setting name="cacheEnabled" value="true"/>
    2. 在Mapper.xml中添加<cache/>標簽(啟用當前Mapper的二級緩存);
  • 實現(xiàn)原理:每個Mapper接口對應一個Cache對象,SqlSession查詢后將結果存入二級緩存,其他SqlSession查詢相同SQL時,先從二級緩存獲??;
  • 注意事項:
    • 緩存的對象必須實現(xiàn)Serializable接口(二級緩存可能序列化存儲);
    • 執(zhí)行insert/update/delete操作會清空當前Mapper的二級緩存;
    • 可通過useCache="false"(查詢語句)或flushCache="true"(增刪改語句)控制緩存行為。

緩存查詢順序:二級緩存 → 一級緩存 → 數(shù)據(jù)庫。

七、設計模式與性能優(yōu)化

1. 單例模式的幾種實現(xiàn)方式與線程安全?

答案
單例模式確保一個類僅有一個實例,并提供全局訪問點,常用實現(xiàn)方式如下:

1. 餓漢式(線程安全,非懶加載):

public class Singleton {
    // 類加載時初始化實例(餓漢式)
    private static final Singleton instance = new Singleton();
    // 私有構造方法,禁止外部實例化
    private Singleton() {}
    // 提供全局訪問方法
    public static Singleton getInstance() {
        return instance;
    }
}
  • 優(yōu)點:簡單高效,類加載時初始化,天然線程安全;
  • 缺點:非懶加載,類加載時即創(chuàng)建實例,若實例未被使用,浪費內存。

2. 懶漢式(線程不安全→線程安全優(yōu)化):

  • 基礎版(線程不安全,多線程下可能創(chuàng)建多個實例):
    public class Singleton {
        private static Singleton instance;
        private Singleton() {}
        // 線程不安全:多線程同時進入if條件,創(chuàng)建多個實例
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
  • 優(yōu)化版(雙重校驗鎖DCL,線程安全,懶加載):
    public class Singleton {
        // volatile禁止指令重排
        private static volatile Singleton instance;
        private Singleton() {}
        public static Singleton getInstance() {
            if (instance == null) { // 第一次校驗(無鎖,提高效率)
                synchronized (Singleton.class) { // 加鎖
                    if (instance == null) { // 第二次校驗(防止多線程并發(fā)創(chuàng)建)
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
  • 優(yōu)點:懶加載(使用時才創(chuàng)建實例),線程安全,性能優(yōu);
  • 關鍵:volatile關鍵字必須加,防止instance = new Singleton()指令重排(分配內存→初始化→賦值),導致其他線程獲取到未初始化的實例。

3. 靜態(tài)內部類式(線程安全,懶加載,推薦):

public class Singleton {
    private Singleton() {}
    // 靜態(tài)內部類,類加載時不初始化
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    // 調用getInstance()時,才加載SingletonHolder,初始化實例
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
  • 優(yōu)點:懶加載(靜態(tài)內部類按需加載),線程安全(類加載機制保證),代碼簡潔,無鎖開銷;
  • 原理:靜態(tài)內部類的加載時機是在第一次被引用時,類加載過程是線程安全的,確保實例僅創(chuàng)建一次。

4. 枚舉式(線程安全,防反射/序列化,最佳實踐):

public enum Singleton {
    INSTANCE;
    // 枚舉類的方法
    public void doSomething() {
        System.out.println("Singleton enum");
    }
}
  • 優(yōu)點:
    1. 線程安全:枚舉類的實例在類加載時創(chuàng)建,天然線程安全;
    2. 防反射:枚舉類的構造方法被編譯器私有化,無法通過反射創(chuàng)建實例;
    3. 防序列化:枚舉類默認實現(xiàn)Serializable,序列化時不會創(chuàng)建新實例;
  • 缺點:非懶加載,類加載時即創(chuàng)建實例。

2. Java性能優(yōu)化的常見手段?

答案
Java性能優(yōu)化需從“代碼層面、JVM層面、數(shù)據(jù)庫層面、架構層面”多維度入手,核心目標是提升響應速度、降低資源消耗。

1. 代碼層面優(yōu)化:

  • 集合使用優(yōu)化:
    • ArrayList初始化時指定初始容量(避免頻繁擴容);
    • 頻繁增刪用LinkedList,頻繁查詢用ArrayList;
    • 避免在循環(huán)中使用ArrayList.add(index, element)(O(n)復雜度);
  • 字符串優(yōu)化:
    • 頻繁拼接用StringBuilder(單線程)/StringBuffer(多線程),避免String拼接(創(chuàng)建大量臨時對象);
    • 常量字符串用String.intern()復用常量池對象;
  • 循環(huán)優(yōu)化:
    • 減少循環(huán)內的對象創(chuàng)建(如for (int i = 0; i < list.size(); i++) → 先獲取size:int size = list.size(); for (int i = 0; i < size; i++));
    • 避免循環(huán)內的復雜計算(如方法調用、表達式計算);
  • 避免空指針:使用Objects.requireNonNull()、Optional類,減少null判斷;
  • 資源管理:使用try-with-resources自動關閉流、數(shù)據(jù)庫連接,避免資源泄漏。

2. JVM層面優(yōu)化:

  • 堆內存配置:合理設置-Xms(初始堆)和-Xmx(最大堆),建議兩者相等(避免頻繁擴容),堆大小一般為物理內存的1/2~1/3;
  • 年輕代優(yōu)化:調整-XX:NewRatio(年輕代與老年代比例,默認2:1)、-XX:SurvivorRatio(Eden與S0/S1比例,默認8:1),根據(jù)應用對象存活時間調整;
  • GC收集器選擇:
    • 高吞吐量場景:使用Parallel Scavenge收集器;
    • 低延遲場景:使用G1/ZGC收集器;
  • 逃逸分析:啟用-XX:+DoEscapeAnalysis(默認啟用),JVM自動分析對象是否逃逸,未逃逸的對象可分配在棧上(減少GC壓力)。

3. 數(shù)據(jù)庫層面優(yōu)化:

  • 索引優(yōu)化:給查詢頻繁的字段建立索引(如WHERE、JOIN、ORDER BY字段),避免過度索引(影響增刪效率);
  • SQL優(yōu)化:
    • 避免SELECT *,只查詢需要的字段;
    • 避免WHERE子句中使用函數(shù)(如WHERE DATE(create_time) = '2025-01-01',導致索引失效);
    • 避免JOIN過多表(建議不超過3張表),大表JOIN用分頁;
    • 批量操作替代循環(huán)單條操作(如MyBatis的batchInsert);
  • 連接池優(yōu)化:合理設置數(shù)據(jù)庫連接池大?。ㄈ鏗ikariCP的maximum-pool-size,建議為CPU核心數(shù)×2+1),避免連接泄漏。

4. 架構層面優(yōu)化:

  • 緩存引入:使用Redis、Ehcache等緩存熱點數(shù)據(jù)(如用戶信息、配置數(shù)據(jù)),減少數(shù)據(jù)庫查詢;
  • 異步處理:將耗時操作(如日志打印、郵件發(fā)送)異步化(使用線程池、消息隊列),提升響應速度;
  • 負載均衡:通過Nginx、LVS等實現(xiàn)多服務器負載均衡,分散請求壓力;
  • 分布式部署:將應用拆分為微服務,按業(yè)務模塊部署,提高并發(fā)處理能力。

八、總結

本文覆蓋Java面試核心考點,從基礎語法、集合框架、多線程、JVM、Spring、數(shù)據(jù)庫到設計模式與性能優(yōu)化,每個模塊均包含高頻面試題、詳細答案及核心考點解析,兼顧理論深度與實戰(zhàn)應用。

面試時,除了記憶答案,更要理解底層原理(如HashMap的哈希沖突解決、Spring AOP的動態(tài)代理、JVM的GC機制),并結合項目經(jīng)驗說明實際應用場景(如線程池在項目中的配置、事務失效的排查過程)。建議重點掌握多線程并發(fā)、JVM、Spring核心原理等高級考點,這些是區(qū)分初級與中高級開發(fā)者的關鍵。

到此這篇關于Java常見面試題以及答案匯總大全的文章就介紹到這了,更多相關Java常見面試題及答案內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

到此這篇關于Java常見面試題以及答案匯總大全的文章就介紹到這了,更多相關Java常見面試題及答案內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論

宣汉县| 德江县| 河东区| 始兴县| 涪陵区| 凤城市| 囊谦县| 南川市| 杭锦后旗| 丰县| 邢台市| 保靖县| 浮山县| 遵义县| 天祝| 舞钢市| 甘肃省| 隆回县| 瑞金市| 山阳县| 嵊州市| 祥云县| 海伦市| 康保县| 定陶县| 武功县| 崇礼县| 浦东新区| 韶山市| 邵东县| 都江堰市| 西城区| 长春市| 禹城市| 扶沟县| 寿光市| 永善县| 凯里市| 梓潼县| 远安县| 灌阳县|