整整7天,梳理 Java開(kāi)發(fā)2022年(圖文+代碼)面試題及答案
應(yīng)各位小伙伴要求,今天抽空來(lái)整理整理java開(kāi)發(fā)面試中的那些事情,幫助那些正在找工作或想跳槽找工作的伙伴們!分享目前Java常見(jiàn)的面試問(wèn)題以及答案。
本文所有答案相對(duì)權(quán)威,有不對(duì)之處還請(qǐng)不吝賜教。篇幅較長(zhǎng),后續(xù)還會(huì)繼續(xù)更新~
需要這份文檔的小伙伴直接私信【37】就可以無(wú)償免費(fèi)領(lǐng)取,絕不需要關(guān)注什么公眾號(hào)!
Spring由哪些模塊組成?
Spring 總共大約有 20 個(gè)模塊, 由 1300 多個(gè)不同的文件構(gòu)成。 而這些組件被分別整合在一起
核心容器(Core Container) 、 AOP(Aspect Oriented Programming)和設(shè)備支持
(Instrmentation)
數(shù)據(jù)訪問(wèn)與集成(Data Access/Integeration)
Web
消息(Messaging)
Test
等 6 個(gè)模塊中。 以下是 spring 5 的模塊結(jié)構(gòu)圖:
- spring core:提供了框架的基本組成部分,包括控制反轉(zhuǎn)(Inversion of Control,IOC)和依賴
注入(Dependency Injection,DI)功能。 - spring beans:提供了beanFactory,是工廠模式的一個(gè)經(jīng)典實(shí)現(xiàn),Spring將管理對(duì)象稱為
Bean。 - spring context:構(gòu)建于 core 封裝包基礎(chǔ)上的 context 封裝包,提供了一種框架式的對(duì)象訪問(wèn)方式
法。 - spring jdbc:提供了一個(gè)JDBC的抽象層,消除了煩瑣的JDBC編碼和數(shù)據(jù)庫(kù)廠商特有的錯(cuò)誤代碼解
析, 用于簡(jiǎn)化JDBC。 - spring aop:提供了面向切面的編程實(shí)現(xiàn),讓你可以自定義攔截器、切點(diǎn)等。
- spring Web:提供了針對(duì) Web 開(kāi)發(fā)的集成特性,例如文件上傳,利用 servlet listeners 進(jìn)行 ioc容器初始化和針對(duì) Web 的 ApplicationContext。
- spring test:主要為測(cè)試提供支持的,支持使用JUnit或TestNG對(duì)Spring組件進(jìn)行單元測(cè)試和集成測(cè)試
Spring的優(yōu)缺點(diǎn)是什么?
優(yōu)點(diǎn)
- 方便解耦,簡(jiǎn)化開(kāi)發(fā)
Spring就是一個(gè)大工廠,可以將所有對(duì)象的創(chuàng)建和依賴關(guān)系的維護(hù),交給Spring管理。 - AOP編程的支持
Spring提供面向切面編程,可以方便的實(shí)現(xiàn)對(duì)程序進(jìn)行權(quán)限攔截、運(yùn)行監(jiān)控等功能。 - 聲明式事務(wù)的支持
只需要通過(guò)配置就可以完成對(duì)事務(wù)的管理,而無(wú)需手動(dòng)編程。 - 方便程序的測(cè)試
Spring對(duì)Junit4支持,可以通過(guò)注解方便的測(cè)試Spring程序。 - 方便集成各種優(yōu)秀框架
Spring不排斥各種優(yōu)秀的開(kāi)源框架,其內(nèi)部提供了對(duì)各種優(yōu)秀框架的直接支持(如:Struts、Hibernate、MyBatis等)。 - 降低JavaEE API的使用難度
Spring對(duì)JavaEE開(kāi)發(fā)中非常難用的一些API(JDBC、JavaMail、遠(yuǎn)程調(diào)用等),都提供了封裝,使這些API應(yīng)用難度大大降低。
缺點(diǎn)
- Spring明明一個(gè)很輕量級(jí)的框架,卻給人感覺(jué)大而全
- Spring依賴反射,反射影響性能
- 使用門檻升高,入門Spring需要較長(zhǎng)時(shí)間
Spring 應(yīng)用程序有哪些不同組件?
Spring 應(yīng)用一般有以下組件:
- 作為一個(gè)成熟的 Spring Web 應(yīng)用程序。
- 作為第三方 Web 框架,使用 Spring Frameworks 中間層。
- 作為企業(yè)級(jí) Java Bean,它可以包裝現(xiàn)有的 POJO(Plain Old Java Objects)。
用于遠(yuǎn)程使用。
IOC的優(yōu)點(diǎn)是什么?
- IOC 或 依賴注入把應(yīng)用的代碼量降到最低。
- 它使應(yīng)用容易測(cè)試,單元測(cè)試不再需要單例和JNDI查找機(jī)制。
- 最小的代價(jià)和最小的侵入性使松散耦合得以實(shí)現(xiàn)。
- IOC容器支持加載服務(wù)時(shí)的餓漢式初始化和懶加載。
Spring IOC 的實(shí)現(xiàn)機(jī)制
Spring 中的 IOC 的實(shí)現(xiàn)原理就是工廠模式加反射機(jī)制。
示例:
interface Fruit {public abstract void eat();}class Apple implements Fruit {public void eat(){System.out.println("Apple");}}class Orange implements Fruit {public void eat(){System.out.println("Orange");}}class Factory {public static Fruit getInstance(String ClassName) {Fruit f=null;try {f=(Fruit)Class.forName(ClassName).newInstance();} catch (Exception e) {e.printStackTrace();}return f;}}class Client {public static void main(String[] a) {Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple");if(f!=null){f.eat();}}}
Spring如何處理線程并發(fā)問(wèn)題?
- 在一般情況下,只有無(wú)狀態(tài)的Bean才可以在多線程環(huán)境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用域,因?yàn)镾pring對(duì)一些Bean中非線程安全狀態(tài)采用ThreadLocal進(jìn)行處理,解決線程安全問(wèn)題。
- ThreadLocal和線程同步機(jī)制都是為了解決多線程中相同變量的訪問(wèn)沖突問(wèn)題。同步機(jī)制采用了“時(shí)間換空間”的方式,僅提供一份變量,不同的線程在訪問(wèn)前需要獲取鎖,沒(méi)獲得鎖的線程則需要排隊(duì)。而ThreadLocal采用了“空間換時(shí)間”的方式。
- ThreadLocal會(huì)為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線程對(duì)數(shù)據(jù)的訪問(wèn)沖突。因?yàn)槊恳粋€(gè)線程都擁有自己的變量副本,從而也就沒(méi)有必要對(duì)該變量進(jìn)行同步了。ThreadLocal提供了線程安全的共享對(duì)象,在編寫多線程代碼時(shí),可以把不安全的變量封裝進(jìn)ThreadLocal。
解釋Spring框架中bean的生命周期
- 在傳統(tǒng)的Java應(yīng)用中,bean的生命周期很簡(jiǎn)單。使用Java關(guān)鍵字new進(jìn)行bean實(shí)例化,然后該bean就可以使用了。一旦該bean不再被使用,則由Java自動(dòng)進(jìn)行垃圾回收。相比之下,Spring容器中的bean的生命周期就顯得相對(duì)復(fù)雜多了。正確理解Spring bean的生命周期非常重要,因?yàn)槟慊蛟S要利用Spring提供的擴(kuò)展點(diǎn)來(lái)自定義bean的創(chuàng)建過(guò)程。下圖展示了bean裝載到Spring應(yīng)用上
下文中的一個(gè)典型的生命周期過(guò)程。
- bean在Spring容器中從創(chuàng)建到銷毀經(jīng)歷了若干階段,每一階段都可以針對(duì)Spring如何管理bean進(jìn)行個(gè)性化定制。
- 正如你所見(jiàn),在bean準(zhǔn)備就緒之前,bean工廠執(zhí)行了若干啟動(dòng)步驟。
我們對(duì)上圖進(jìn)行詳細(xì)描述:
- Spring對(duì)bean進(jìn)行實(shí)例化;
- Spring將值和bean的引用注入到bean對(duì)應(yīng)的屬性中;
- 如果bean實(shí)現(xiàn)了BeanNameAware接口,Spring將bean的ID傳遞給setBean-Name()方法;
- 如果bean實(shí)現(xiàn)了BeanFactoryAware接口,Spring將調(diào)用setBeanFactory()方法,將BeanFactory容器實(shí)例傳入;
- 如果bean實(shí)現(xiàn)了ApplicationContextAware接口,Spring將調(diào)用setApplicationContext()方法,將bean所在的應(yīng)用上下文的引用傳入進(jìn)來(lái);
- 如果bean實(shí)現(xiàn)了BeanPostProcessor接口,Spring將調(diào)用它們的postProcessBeforeInitialization()方法;
- 如果bean實(shí)現(xiàn)了InitializingBean接口,Spring將調(diào)用它們的after-PropertiesSet()方法。類似地,如果bean使用initmethod聲明了初始化方法,該方法也會(huì)被調(diào)用;
- 如果bean實(shí)現(xiàn)了BeanPostProcessor接口,Spring將調(diào)用它們的post-ProcessAfterInitialization()方法;
- 此時(shí),bean已經(jīng)準(zhǔn)備就緒,可以被應(yīng)用程序使用了,它們將一直駐留在應(yīng)用上下文中,直到該應(yīng)用上下文被銷毀;
- 如果bean實(shí)現(xiàn)了DisposableBean接口,Spring將調(diào)用它的destroy()接口方法。同樣,如果bean使用destroy-method聲明了銷毀方法,該方法也會(huì)被調(diào)用。
現(xiàn)在你已經(jīng)了解了如何創(chuàng)建和加載一個(gè)Spring容器。但是一個(gè)空的容器并沒(méi)有太大的價(jià)值,在你把東西放進(jìn)去之前,它里面什么都沒(méi)有。為了從Spring的DI(依賴注入)中受益,我們必須將應(yīng)用對(duì)象裝配進(jìn)Spring容器中。
什么是基于Java的Spring注解配置? 給一些注解的例子
- 基于Java的配置,允許你在少量的Java注解的幫助下,進(jìn)行你的大部分Spring配置而非通過(guò)XML文件。
- 以@Configuration 注解為例,它用來(lái)標(biāo)記類可以當(dāng)做一個(gè)bean的定義,被Spring IOC容器使用。
- 另一個(gè)例子是@Bean注解,它表示此方法將要返回一個(gè)對(duì)象,作為一個(gè)bean注冊(cè)進(jìn)Spring應(yīng)用上下文。
@Configurationpublic class StudentConfig {@Beanpublic StudentBean myStudent() {return new StudentBean();}}
MySQL由哪些部分組成, 分別用來(lái)做什么
1. Server
- 連接器: 管理連接, 權(quán)限驗(yàn)證.
- 分析器: 詞法分析, 語(yǔ)法分析.
- 優(yōu)化器: 執(zhí)行計(jì)劃生成, 索引的選擇.
- 執(zhí)行器: 操作存儲(chǔ)引擎, 返回執(zhí)行結(jié)果.
2. 存儲(chǔ)引擎: 存儲(chǔ)數(shù)據(jù), 提供讀寫接口.
MySQL怎么恢復(fù)半個(gè)月前的數(shù)據(jù)
通過(guò)整庫(kù)備份 binlog進(jìn)行恢復(fù). 前提是要有定期整庫(kù)備份且保存了binlog日志.
一千萬(wàn)條數(shù)據(jù)的表, 如何分頁(yè)查詢
數(shù)據(jù)量過(guò)大的情況下, limit offset 分頁(yè)會(huì)由于掃描數(shù)據(jù)太多而越往后查詢?cè)铰? 可以配合當(dāng)前頁(yè)最后一條ID進(jìn)行查詢, SELECT * FROM T WHERE id > #{ID} LIMIT #{LIMIT} . 當(dāng)然, 這種情況下ID必須是有序的, 這也是有序ID的好處之一
SQL 語(yǔ)言包括哪幾部分?每部分都有哪些操作關(guān)鍵字?
SQL 語(yǔ)言包括數(shù)據(jù)定義(DDL)、數(shù)據(jù)操縱(DML),數(shù)據(jù)控制(DCL)和數(shù)據(jù)查詢( DQL) 四個(gè)部分。
數(shù)據(jù)定義: Create Table,Alter Table,Drop Table, Craete/Drop Index 等數(shù)據(jù)操縱: Select
,insert,update,delete,數(shù)據(jù)控制: grant,revoke 數(shù)據(jù)查詢: select
簡(jiǎn)單 Linux 文件系統(tǒng)?
在 Linux 操作系統(tǒng)中,所有被操作系統(tǒng)管理的資源,例如網(wǎng)絡(luò)接口卡、磁盤驅(qū)動(dòng)器、打印機(jī)、輸入輸出設(shè)備、普通文件或是目錄都被看作是一個(gè)文件。
- 也就是說(shuō)在 Linux 系統(tǒng)中有一個(gè)重要的概念:一切都是文件。其實(shí)這是 Unix 哲學(xué)的一個(gè)體現(xiàn),而Linux 是重寫 Unix 而來(lái),所以這個(gè)概念也就傳承了下來(lái)。在 Unix 系統(tǒng)中,把一切資源都看作是文件,包括硬件設(shè)備。UNIX系統(tǒng)把每個(gè)硬件都看成是一個(gè)文件,通常稱為設(shè)備文件,這樣用戶就可以用讀寫文件的方式實(shí)現(xiàn)對(duì)硬件的訪問(wèn)。
- Linux 支持 5 種文件類型,如下圖所示:
Linux 的目錄結(jié)構(gòu)是怎樣的?
這個(gè)問(wèn)題,一般不會(huì)問(wèn)。更多是實(shí)際使用時(shí),需要知道。
- Linux 文件系統(tǒng)的結(jié)構(gòu)層次鮮明,就像一棵倒立的樹(shù),最頂層是其根目錄:
Unix和Linux有什么區(qū)別?
Linux和Unix都是功能強(qiáng)大的操作系統(tǒng),都是應(yīng)用廣泛的服務(wù)器操作系統(tǒng),有很多相似之處,甚至有一部分人錯(cuò)誤地認(rèn)為Unix和Linux操作系統(tǒng)是一樣的,然而,事實(shí)并非如此,以下是兩者的區(qū)別。
- 開(kāi)源性
Linux是一款開(kāi)源操作系統(tǒng),不需要付費(fèi),即可使用;Unix是一款對(duì)源碼實(shí)行知識(shí)產(chǎn)權(quán)保護(hù)的傳統(tǒng)商業(yè)軟件,使用需要付費(fèi)授權(quán)使用。 - 跨平臺(tái)性
Linux操作系統(tǒng)具有良好的跨平臺(tái)性能,可運(yùn)行在多種硬件平臺(tái)上;Unix操作系統(tǒng)跨平臺(tái)性能較弱,大多需與硬件配套使用。 - 可視化界面
Linux除了進(jìn)行命令行操作,還有窗體管理系統(tǒng);Unix只是命令行下的系統(tǒng)。 - 硬件環(huán)境
Linux操作系統(tǒng)對(duì)硬件的要求較低,安裝方法更易掌握;Unix對(duì)硬件要求比較苛刻,按照難度較大。 - 用戶群體
Linux的用戶群體很廣泛,個(gè)人和企業(yè)均可使用;Unix的用戶群體比較窄,多是安全性要求高
的大型企業(yè)使用,如銀行、電信部門等,或者Unix硬件廠商使用,如Sun等。
相比于Unix操作系統(tǒng),Linux操作系統(tǒng)更受廣大計(jì)算機(jī)愛(ài)好者的喜愛(ài),主要原因是Linux操作系統(tǒng)具有Unix操作系統(tǒng)的全部功能,并且能夠在普通PC計(jì)算機(jī)上實(shí)現(xiàn)全部的Unix特性,開(kāi)源免費(fèi)的特性,更容易普及使用!
什么是 Linux 內(nèi)核?
- Linux 系統(tǒng)的核心是內(nèi)核。內(nèi)核控制著計(jì)算機(jī)系統(tǒng)上的所有硬件和軟件,在必要時(shí)分配硬件,并根據(jù)需要執(zhí)行軟件。
- 系統(tǒng)內(nèi)存管理
- 應(yīng)用程序管理
- 硬件設(shè)備管理
- 文件系統(tǒng)管理
Linux 的體系結(jié)構(gòu)
- 從大的方面講,Linux 體系結(jié)構(gòu)可以分為兩塊:
- 用戶空間(User Space) :用戶空間又包括用戶的應(yīng)用程序(User Applications)、C 庫(kù)(C Library) 。
- 內(nèi)核空間(Kernel Space) :內(nèi)核空間又包括系統(tǒng)調(diào)用接口(System Call Interface)、內(nèi)核(Kernel)、平臺(tái)架構(gòu)相關(guān)的代碼(Architecture-Dependent Kernel Code) 。
為什么 Linux 體系結(jié)構(gòu)要分為用戶空間和內(nèi)核空間的原因?
1、現(xiàn)代 CPU 實(shí)現(xiàn)了不同的工作模式,不同模式下 CPU 可以執(zhí)行的指令和訪問(wèn)的寄存器不同。
2、Linux 從 CPU 的角度出發(fā),為了保護(hù)內(nèi)核的安全,把系統(tǒng)分成了兩部分。
用戶空間和內(nèi)核空間是程序執(zhí)行的兩種不同的狀態(tài),我們可以通過(guò)兩種方式完成用戶空間到內(nèi)核空
間的轉(zhuǎn)移:
- 系統(tǒng)調(diào)用;
- 硬件中斷。
JVM內(nèi)存模型的相關(guān)知識(shí)了解多少,比如重排序,內(nèi)存屏障,happen-before,主內(nèi)存,工作內(nèi)存。
思路: 先畫出Java內(nèi)存模型圖,結(jié)合例子volatile ,說(shuō)明什么是重排序,內(nèi)存屏障,最好能給面試官寫以下demo說(shuō)明。
1)Java內(nèi)存模型圖:
Java內(nèi)存模型規(guī)定了所有的變量都存儲(chǔ)在主內(nèi)存中,每條線程還有自己的工作內(nèi)存,線程的工作內(nèi)存中保存了該線程中是用到的變量的主內(nèi)存副本拷貝,線程對(duì)變量的所有操作都必須在工作內(nèi)存中進(jìn)行,而不能直接讀寫主內(nèi)存。不同的線程之間也無(wú)法直接訪問(wèn)對(duì)方工作內(nèi)存中的變量,線程間變量的傳遞均需要自己的工作內(nèi)存和主存之間進(jìn)行數(shù)據(jù)同步進(jìn)行。
2)指令重排序。
在這里,先看一段代碼
public class PossibleReordering {static int x = 0, y = 0;static int a = 0, b = 0;public static void main(String[] args) throws InterruptedException {Thread one = new Thread(new Runnable() { public void run() { a = 1; x = b; }});Thread other = new Thread(new Runnable() { public void run() { b = 1; y = a; }}); one.start();other.start(); one.join();other.join(); System.out.println(“(” x “,” y “)”);}
運(yùn)行結(jié)果可能為(1,0)、(0,1)或(1,1),也可能是(0,0)。因?yàn)?,在?shí)際運(yùn)行時(shí),代碼指令可能并不是嚴(yán)格按照代碼語(yǔ)句順序執(zhí)行的。大多數(shù)現(xiàn)代微處理器都會(huì)采用將指令亂序執(zhí)行(out-of-order execution,簡(jiǎn)稱OoOE或OOE)的方法,在條件允許的情況下,直接運(yùn)行當(dāng)前有能力立即執(zhí)行的后續(xù)指令,避開(kāi)獲取下一條指令所需數(shù)據(jù)時(shí)造成的等待3。通過(guò)亂序執(zhí)行的技術(shù),處理器可以大大提高執(zhí)行效率。而這就是指令重排。
3)內(nèi)存屏障
內(nèi)存屏障,也叫內(nèi)存柵欄,是一種CPU指令,用于控制特定條件下的重排序和內(nèi)存可見(jiàn)性問(wèn)題。
- LoadLoad屏障:對(duì)于這樣的語(yǔ)句Load1; LoadLoad; Load2,在Load2及后續(xù)讀取操作要讀取的數(shù)據(jù)被訪問(wèn)前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
- StoreStore屏障:對(duì)于這樣的語(yǔ)句Store1; StoreStore; Store2,在Store2及后續(xù)寫入操作執(zhí)行前,保證Store1的寫入操作對(duì)其它處理器可見(jiàn)。
- LoadStore屏障:對(duì)于這樣的語(yǔ)句Load1; LoadStore; Store2,在Store2及后續(xù)寫入操作被刷出前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
- StoreLoad屏障:對(duì)于這樣的語(yǔ)句Store1; StoreLoad; Load2,在Load2及后續(xù)所有讀取操作執(zhí)行
前,保證Store1的寫入對(duì)所有處理器可見(jiàn)。它的開(kāi)銷是四種屏障中最大的。 在大多數(shù)處理器的實(shí)現(xiàn)中,這個(gè)屏障是個(gè)萬(wàn)能屏障,兼具其它三種內(nèi)存屏障的功能。
4)happen-before原則
- 單線程happen-before原則:在同一個(gè)線程中,書寫在前面的操作happen-before后面的操作。鎖的happen-before原則:同一個(gè)鎖的unlock操作happen-before此鎖的lock操作。
- volatile的happen-before原則:對(duì)一個(gè)volatile變量的寫操作happen-before對(duì)此變量的任意操作(當(dāng)然也包括寫操作了)。
- happen-before的傳遞性原則:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
- 線程啟動(dòng)的happen-before原則:同一個(gè)線程的start方法happen-before此線程的其它方法。
- 線程中斷的happen-before原則 :對(duì)線程interrupt方法的調(diào)用happen-before被中斷線程的檢測(cè)到中斷發(fā)送的代碼。
- 線程終結(jié)的happen-before原則: 線程中的所有操作都happen-before線程的終止檢測(cè)。
- 對(duì)象創(chuàng)建的happen-before原則: 一個(gè)對(duì)象的初始化完成先于他的finalize方法調(diào)用。
怎么打出線程棧信息
思路: 可以說(shuō)一下jps,top ,jstack這幾個(gè)命令,再配合一次排查線上問(wèn)題進(jìn)行解答。
- 輸入jps,獲得進(jìn)程號(hào)。
- top -Hp pid 獲取本進(jìn)程中所有線程的CPU耗時(shí)性能
- jstack pid命令查看當(dāng)前java進(jìn)程的堆棧狀態(tài)
- 或者 jstack -l > /tmp/output.txt 把堆棧信息打到一個(gè)txt文件。
- 可以使用fastthread 堆棧定位,fastthread.io/