常見面試題

springmv執(zhí)行流程

1、DispatcherServlet表示前置控制器,是整個SpringMVC的控制中心。用戶發(fā)出請求,DispatcherServlet接收請求并攔截請求。

2、HandlerMapping為處理器映射。DispatcherServlet調用HandlerMapping,HandlerMapping根據(jù)請求url查找Handler。

3、返回處理器執(zhí)行鏈,根據(jù)url查找控制器,并且將解析后的信息傳遞給DispatcherServlet

4、HandlerAdapter表示處理器適配器,其按照特定的規(guī)則去執(zhí)行Handler。

5、執(zhí)行handler找到具體的處理器

6、Controller將具體的執(zhí)行信息返回給HandlerAdapter,如ModelAndView。

7、HandlerAdapter將視圖邏輯名或模型傳遞給DispatcherServlet。

8、DispatcherServlet調用視圖解析器(ViewResolver)來解析HandlerAdapter傳遞的邏輯視圖名。

9、視圖解析器將解析的邏輯視圖名傳給DispatcherServlet。

10、DispatcherServlet根據(jù)視圖解析器解析的視圖結果,調用具體的視圖,進行試圖渲染

11、將響應數(shù)據(jù)返回給客戶端

bean生命周期

1.實例化,反射機制推斷構造函數(shù)進行實例化

2.屬性賦值,DI的體現(xiàn),@autowired,循環(huán)依賴

3.初始化,回調beanfactoryaware,實現(xiàn)了aop,回調動態(tài)代理

4銷毀,spring容器關閉時進行銷毀

springboot啟動順序

一、加載啟動類Spring Boot:應用程序的啟動類必須使用

@SpringBootApplication注解標記,該注解包含了

@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan三個注解的功能。Spring Boot通過掃描啟動類所在的包和子包,自動裝配配置類和Bean定義。

二、加載配置文件:Spring Boot應用程序默認從applicationproperties或application.vml文件中加載配置屬性,也可以通過在啟動類上使用@PropertySource注解指定其他的屬性文件。

三、創(chuàng)建Spring容器Spring Boot使用SpringApplication類創(chuàng)建Spring容器,SpringApplication是Spring Boot的核心類,它提供了各種配置和管理Spring應用程序的方法。

SpringApplication會創(chuàng)建一個嵌入式的Tomcat或Jetty服務器,或者使用外部Tomcat或Jetty服務器。

四、加載自動配置:Spring Boot通過@EnableAutoConfiguration注解和spring-boot-autoconfigure項目提供了大量的自動配置,根據(jù)classpath中的jar包和Bean的裝配情況,自動裝配相應的Bean。

五、運行Spring Boot應用程序:當Spring容器準備就緒后,Spring Boot就會啟動嵌入式的Web服務器并運行應用程序。如果使用的是外部Web服務器,Spring Boot就會將應用程序打包成一個可執(zhí)行的jar文件,并啟動外部Web服務器。

正確的答案是:#{}是預編譯處理,${}是字符串替換。

(1)mybatis在處理#{}時,會將sql中的#{}替換為?號,調用PreparedStatement的set方法來賦值。

(2)mybatis在處理${}時,就是把${}替換成變量的值。

(3)使用#{}可以有效的防止SQL注入,提高系統(tǒng)安全性。原因在于:預編譯機制。預編譯完成之后,SQL的結構已經(jīng)固定,即便用戶輸入非法參數(shù),也不會對SQL的結構產(chǎn)生影響,從而避免了潛在的安全風險。

(4)預編譯是提前對SQL語句進行預編譯,而其后注入的參數(shù)將不會再進行SQL編譯。我們知道,SQL注入是發(fā)生在編譯的過程中,因為惡意注入了某些特殊字符,最后被編譯成了惡意的執(zhí)行操作。而預編譯機制則可以很好的防止SQL注入。

1. 程序本質:代碼是如何被執(zhí)行的?CPU、操作系統(tǒng)、虛擬機各司何職?

先把Java代碼編譯成字節(jié)碼也就是把.java類型的文件字節(jié)碼生成器編譯成.class類型的文件(語法,語義,抽象語法樹)

我們執(zhí)行的時候,會把class文件放置到Java虛擬機,

Java虛擬機使用類加載器,裝載class文件

類加載完成后,JVM解釋器會把字節(jié)碼翻譯成與機器碼交給操作系統(tǒng)執(zhí)行。當然了虛擬機本身也支持JIT動態(tài)編譯器。

2. 基礎語法:從CPU角度看變量、數(shù)組、類型、運算、跳轉、函數(shù)等語法

大小,精度,優(yōu)先級,方法

3. 引用類型:同樣都是存儲地址,為何Java引用比C/C++指針更安全?

java引用有類型檢查(可以強轉)

4. 基本類型:既然Java一切皆對象,那又為何要保留int等基本類型?

因為八種基本類型都有對應的包裝類(自動拆箱和自動裝箱),再有就是基本類型定義的變量創(chuàng)建和銷毀很快,而類定義的變量還需要JVM去銷毀。

6. 浮點數(shù):計算機如何用二進制表示浮點數(shù)?為何0.1+0.1不等于0.2?

十進制轉換成二進制(計算機只認識二進制)的時候,需要近似相等,所以加完了以后就不準確了

https://baijiahao.baidu.com/s?id=1680396399018883986&wfr=spider&for=pc

7. 字符:為何C/C++中char占1個字節(jié),而Java中char占2個字節(jié)?

????C/C++用?ASCII 編碼標準中,每個字符只占用一個字節(jié),Java 采用了 16 位編碼的Unicode 編碼標準

8. 字符串:請解釋String類用到的三大技術:壓縮、常量池、不可變\

String類又稱作不可變Unicode字符序列。

final修飾(工藝品)

修改只是拷貝個新的

String s = "aaaaa"會進入到常量池,new String(...)在堆上創(chuàng)建對象,不會入池,屬于優(yōu)化部分

10. 關鍵字:靜態(tài)內部類實現(xiàn)的單例如何做到線程安全且可延遲加載?

單例模式:懶漢,餓漢,枚舉,靜態(tài)內部類,延遲加載,減少內存開銷,訪問成本低且線程安全

第一次new的時候會被加載,節(jié)約性能,建造者模式

11. 容器:為什么不推薦在項目中使用Vector、Stack、HashTable?

vector是線程安全的

Vector的空間擴容是一倍,內存不可復用,而ArrayList是一半 (C++ Made Easier: How Vectors Grow)

Vector分配內存是連續(xù)的存儲空間

然后是stack:Stack 是 JDK 1.0 的產(chǎn)物。它繼承自 Vector。

當初 JDK1.0 在開發(fā)時,可能為了快速的推出一些基本的數(shù)據(jù)結構操作,所以推出了一些比較粗糙的類。比如,Vector、Stack、Hashtable等。這些類中的一些方法加上了 synchronized 關鍵字,容易誤導。

13. HashMap(上):為何HashMap中數(shù)組的大小必須是2的冪次方?

首先,HashMap的初始化的數(shù)組長度一定是2的n次的,每次擴容仍是原來的2倍的話,就不會破壞這個規(guī)律,每次擴容后,原數(shù)據(jù)都會進行數(shù)據(jù)遷移,根據(jù)二進制的計算,擴容后數(shù)據(jù)要么在原來位置,要么在【原來位置+擴容長度】,這樣就不需要重新hash,效率上更高。擴容后

load factory=0.75的真正原因,在java7、8等中均有注釋,負載因子太小了浪費空間并且會發(fā)生更多次數(shù)的resize,太大了哈希沖突增加會導致性能不好,所以0.75只是一個折中的選擇,和泊松分布沒有什么關系。原來位置還是在原腳標位+擴容長度的位置

1:當添加某個元素后,數(shù)組的總的添加元素數(shù)大于了 數(shù)組長度 * 0.75(默認,也可自己設定),數(shù)組長度擴容為兩倍。(如開始創(chuàng)建HashMap集合后,數(shù)組長度為16,臨界值為16 * 0.75 = 12,當加入元素后元素個數(shù)超過12,數(shù)組長度擴容為32,臨界值變?yōu)?4)

16. 迭代器:為什么使用迭代器遍歷容器的同時修改容器會出錯?

在用迭代器遍歷容器的時候,試圖去修改容器中的元素,可能會引起并發(fā)修改異常(遍歷的時候,相當于一個指針從0索引開始移動,當試圖修改某個數(shù)據(jù)時,系統(tǒng)無法去操作這個指針)

迭代器是依賴于集合而存在的,在判斷成功之后,集合當中增加了新的元素,而迭代器卻不知道,所以就報錯了,這個就叫并發(fā)修改異常。

17. 異常(上):在項目開發(fā)中如何正確的定義、處理、打印異常?

枚舉,定義異常類,

18. 異常(下):高并發(fā)下異常太多導致程序變慢的核心原因是什么?

CPU、內存、IO 設備的讀寫速度差異巨大,表現(xiàn)為 CPU 的速度 >?內存的速度 > IO 設備的速度。

20.nio類庫:BIO、NIO、AIO三種Java I/O模型的實現(xiàn)原理和區(qū)別

BIO

Java BIO即Block I/O , 同步并阻塞的IO。

BIO就是傳統(tǒng)的java.io包下面的代碼實現(xiàn)。

NIO

什么是NIO? NIO 與原來的 I/O 有同樣的作用和目的, 他們之間最重要的區(qū)別是數(shù)據(jù)打包和傳輸?shù)姆绞?。原來?I/O 以流的方式處理數(shù)據(jù),而 NIO 以塊的方式處理數(shù)據(jù)。

面向流 的 I/O 系統(tǒng)一次一個字節(jié)地處理數(shù)據(jù)。一個輸入流產(chǎn)生一個字節(jié)的數(shù)據(jù),一個輸出流消費一個字節(jié)的數(shù)據(jù)。為流式數(shù)據(jù)創(chuàng)建過濾器非常容易。鏈接幾個過濾器,以便每個過濾器只負責單個復雜處理機制的一部分,這樣也是相對簡單的。不利的一面是,面向流的 I/O 通常相當慢。

一個 面向塊 的 I/O 系統(tǒng)以塊的形式處理數(shù)據(jù)。每一個操作都在一步中產(chǎn)生或者消費一個數(shù)據(jù)塊。按塊處理數(shù)據(jù)比按(流式的)字節(jié)處理數(shù)據(jù)要快得多。但是面向塊的 I/O 缺少一些面向流的 I/O 所具有的優(yōu)雅性和簡單性。

AIO

Java AIO即Async非阻塞,是異步非阻塞的IO。

21. 高速I/O(上):普通的I/O讀寫流程都存在哪些性能問題?

多次內存復制? ?阻塞

Java NIO 提供了與標準 IO 不同的 IO 工作方式:

Channels and Buffers(通道和緩沖區(qū)):標準的 IO 基于字節(jié)流和字符流進行操作,而 NIO 是基于通道(Channel)和緩沖區(qū)(Buffer)進行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)中,或者從緩沖區(qū)寫入到通道中。

非阻塞模式:Java NIO 是非阻塞的,每一次數(shù)據(jù)讀寫調用都會立即返回,并將目前可讀(或可寫)的內容寫入緩沖區(qū)或者從緩沖區(qū)中輸出,即使當前沒有可用數(shù)據(jù),調用仍然會立即返回并且不對緩沖區(qū)做任何操作。

Selectors(選擇器):Java NIO 引入了選擇器的概念,選擇器用于監(jiān)聽多個通道的事件(比如:連接打開,數(shù)據(jù)到達)。因此,單個的線程可以監(jiān)聽多個數(shù)據(jù)通道。

23. 泛型:為什么C++泛型支持int等基本類型而Java泛型不支持呢?

基本數(shù)據(jù)類型不是對象

24. 反射:為什么通過反射創(chuàng)建對象要比使用new創(chuàng)建對象慢?

首先new 這是一個指令 系統(tǒng)接到這人命令,立馬就是申請內存空間(堆空間,接下來就是調用無參構造,來初始化等一系列操作,因為你new明確的指定了是哪個類需要創(chuàng)建,而反射不同,他的先去你在某個包下類,找到了之后還要做解析,看看類里有什么東西,再去找構造方法,找到了還去執(zhí)行這個方法,底層應該是通過一個角invoke的方法,接下來就是開辟內存空間,加載,初始化等,這能不慢嘛.

25. 注解:注解的配置方式相對于XML配置文件有什么優(yōu)缺點?

Spring注解配置優(yōu)點

1、使用注解配置可以減少XML配置文件,使得項目更加簡潔,可讀性更高,當項目變得越來越復雜時,可以更加簡潔的管理項目;

2、注解配置可以減少大量XML配置文件中的重復代碼,可以提高項目的可維護性,也可以提高開發(fā)效率;

3、注解配置可以更加直觀的表達程序的功能,可以更加清晰的表達程序的本質;

4、注解配置可以更加方便的管理Spring容器中的Bean,可以更加靈活的進行Bean的定義;

Spring XML配置優(yōu)點

1、XML配置使得項目更加簡單,可讀性更高,當項目變得越來越復雜時,可以更加簡潔的管理項目;

2、XML配置可以減少大量注解配置中的重復代碼,可以提高項目的可維護性,也可以提高開發(fā)效率;

3、XML配置可以更加直觀的表達程序的功能,可以更加清晰的表達程序的本質;

4、XML配置可以更加方便的管理Spring容器中的Bean,可以更加靈活的進行Bean的定義;

26.動態(tài)代理:為什么基于JDK實現(xiàn)的動態(tài)代理要求原始類有接口?

因為 Java 是單繼承的,因此在代理類 $Proxy0 繼承了 Proxy 后,其只能通過實現(xiàn)目標接口的方式來實現(xiàn)方法的擴展,達到我們增強目標方法邏輯的目的

28. 線程概述:有了進程為什么還要有線程?線程越多執(zhí)行就越快嗎?

從資源上來講:線程是一種非常"節(jié)儉"的多任務操作方式。而進程的創(chuàng)建

需要更多的資源。

從切換效率上來講:運行于一個進程中的多個線程,它們之間使用相同的

地址空間,而且線程間彼此切換所需時間也遠遠小于進程間切換所需要的時間。

據(jù)統(tǒng)計,一個進程的開銷大約是一個線程開銷的 30 倍左右。

從通信機制上來講:對不同進程來說,它們具有獨立的數(shù)據(jù)空間,要進行

數(shù)據(jù)的傳遞只能通過進程間通信的方式進行,這種方式不僅費時,而且很不方便。

線程則不然,由于同一進程下的線程之間共享數(shù)據(jù)空間,所以一個線程的數(shù)據(jù)可

以直接為其他線程所用,這不僅快捷,而且方便。

50. 分段加鎖:HashMap線程不安全原因及ConcurrentHashMap實現(xiàn)原理

1.7之前是頭插法,多線程導致.next找不到了,1.8采用尾插法,多線程導致尾部丟失覆蓋,實現(xiàn)原理是多片分區(qū)

51. 線程狀態(tài):為何synchronized和Lock這兩種鎖對應的線程狀態(tài)不同?

52. 線程中斷:如何安全地提前終止正在執(zhí)行業(yè)務邏輯的線程?

volatile 關鍵字

使用 interrupt() + isInterrupted()來中斷線程

使用 interrupt() + InterruptedException來中斷線程

53. 線程池:線程池開多大最合適?為什么Redis單線程執(zhí)行命令?

官方解釋如下:因為Redis是基于內存的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器內存的大小或者網(wǎng)絡帶寬。既然單線程容易實現(xiàn),而且CPU不會成為瓶頸,那就順理成章地采用單線程的方案了。

上面的解釋不是很好理解,我就簡單說一說我自己的理解吧。我們知道,Redis將數(shù)據(jù)存放在內存當中,這也就意味著,Redis在操作數(shù)據(jù)時,不需要進行磁盤I/O。磁盤I/O是一個比較耗時的操作,所以對于需要進行磁盤I/O的程序,我們可以使用多線程,在某個線程進行I/O時,CPU切換到當前程序的其他線程執(zhí)行,以此減少CPU的等待時間。而Redis直接操作內存中的數(shù)據(jù),所以使用多線程并不能有效提升效率,相反,使用多線程反倒會因為需要進行線程的切換而降低效率。

除此之外,使用多線程的話,多個線程間進行同步,保證線程的安全,也是需要開銷的。尤其是Redis的數(shù)據(jù)結構都是一些實現(xiàn)較為簡單的集合結構,若使用多線程,將會頻繁地發(fā)生線程沖突,線程的競爭頻率較高,反倒會拖慢Redis的響應速度。

54.線程執(zhí)行框架:如何獲取一個線程所執(zhí)行的代碼的運行結果?

????Thread的join方法實現(xiàn)

????CountDownLatch

????ExecutorService.submit

????FutureTask????

????CompletableFuture

57. 類加載:雙親委派加載機制存在的意義是什么?

通過委派的方式,可以避免類的重復加載,當父加載器已經(jīng)加載過某一個類時,子加載器就不會再重新加載這個類。

通過雙親委派的方式,還保證了安全性

被不同的類加載器加載的類也不一樣

59. 可達性分析:虛擬機是如何判斷一個對象是否可以被回收的?

引用計數(shù)法?可達性分析算法

60.spring事務的傳播機制


bean創(chuàng)建流程

第一步:要進行bean創(chuàng)建,首先要有定義bean的配置,可以是注解、xml、json等配置方式

第二步:有了配置,要去讀取配置(讀取過程稍后細講),并且將配置bean信息存儲起來,spring使用beanDefinition進行存儲

第三步:spring提供了BeanFactoryPostProcessor對beanDefinition信息做增強

第四步:當bean信息BeanDefinition處理操作完成,開始根據(jù)beanDefinition進行bean的實例化,這個過程創(chuàng)建了空的bean對象,只是分配了內存空間、創(chuàng)建了實例對象

第五步:有了實例對象開始對bean對象進行屬性信息填充及依賴對象注入

第六步:對bean對象進行增強,包含Aware增強和spring提供的BeanPostProcessor增強

第七步:上述所有操作完成,即是一個完整地bean對象,此時將該bean對象緩存進ioc容器中。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容