1. 程式人生 > 實用技巧 >2020Java面試題整理

2020Java面試題整理

1.SpringAOP

spring專案中的事務,安全,日誌等功能都可以通過AOP技術實現

AOP proxy底層基於JDK的動態代理或者cglib位元組碼操作技術,執行時,動態生成被呼叫型別的子類,並例項化代理物件,實際的方法會被代理給響應的代理對

核心概念:

1)Aspect切面,橫跨多個類,Spring框架建立Advisor來指代它,內部包括切入實際(Pointercut)和切入動作(Advice)

2)Joint Point , 在Spring中只有方法可以作為切入點

3)Advice 定義切面中採取的動作,指明要做什麼 , 通過Before/After/Around來指明什麼時候做

4)Pointcut 具體定義Aspect中使用那些Join Point

2.Mybatis快取

一級快取 : 同一個sqlSession物件,在引數和SQL完全一樣的情況下,只執行一次SQL語句,預設開啟 , 不能關閉

二級快取 : 在SqlSessionFactory生命週期中 , 需要手動開啟

二級快取全域性開啟

  1. <setting name="cacheEnabled"value="true"/>

二級快取關閉預設

  1. <select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

3.servlet生命週期

1)web容器載入servlet類並例項化

2)執行init方法並初始化

3)使用者請求該servlet , 請求到達伺服器時 , 執行servlet方法

4)service方法執行與請求對應的doGet/doPost

5)呼叫destroy銷燬例項

4.springMVC的理解

核心元件:

DispatcherServlet : 作為前端控制器 , 整個流程的控制中心 , 控制其他元件的執行 , 統一排程 , 降低元件之間的耦合性 ,提高每個元件的拓展性.

Handler : (Controller)後端控制器 , 負責處理請求的控制邏輯 .

HandlerMapping : 對映器物件 , 用於管理url 和 對應Controller 的對映關係 .

HandlerAdapter : 介面卡 , 主要處理方法引數 , 相關注解 , 資料繫結 , 訊息轉換 , 返回值 , 呼叫檢視解析器 等等.

ViewResolver : 檢視解析器 , 解析對應的檢視關係 .

執行流程 :

1) 一個請求匹配前端的控制器 DispatcherServlet 的請求對映路徑

2)DispatcherServlet接收到請求後 , 根據請求資訊交給對映器處理(HandlerMapping)

3)HandlerMapping 根據使用者url 查詢匹配的handler , 並返回一個執行鏈 HandlerExecutionChain

4)DispatcherServlet 再請求介面卡 (HandlerAdapter) ,呼叫相應的Handler進行處理並返回 ModelAndView給DispatcherServlet

5)DispatcherServlet 將ModelAndView 交給viewResolver解析 , 獲取具體view

6)DispatcherServlet 對view進行渲染檢視

7)DispatcherServlet將頁面響應給使用者

5.SpringBean的生命週期

1)例項化bean物件

2)設定bean屬性

3)如果通過各種Aware介面聲明瞭依賴關係 , 則會注入bean 對容器基礎設施層的依賴. Aware介面包括BeanNameAware , BeanFactoryAware , ApplicationContext 分別注入bean Id , beanFactory 和 applicationcontext

4) 如果實現了BeanPostPorcesser , 呼叫BeanPostPorcesser 的前置初始化方法postProcessorBeforeInitialization

5)如果實現了InitializingBean介面 , 則會呼叫afterPropertiesSet方法

6)呼叫bean自身定義的init方法

7)呼叫beanPostPorcesser的後置方法postProcessorAfterInitialization

8)建立完畢

9)銷燬 , 呼叫DisPosableBean 的destroy方法和bean自身的destroy銷燬方法

6.Java實現執行緒同步的方法

主要有以下三種:

1.使用同步程式碼塊 ;

2.使用同步方法;

3.使用互斥鎖ReetrantLock(更靈活的程式碼控制);

  1. import java.util.concurrent.locks.ReentrantLock;
  2. public class SyncThreadDemo {
  3. public static void main(String[] args) {
  4. MyRunnable mr = new MyRunnable();
  5. new Thread(mr).start();
  6. new Thread(mr).start();
  7. }
  8. }
  9. class MyRunnable implements Runnable {
  10. private int tickts = 10;
  11. @Override
  12. public void run() {
  13. for (int i = 0; i < 100; i++) {
  14. if (tickts > 0) {
  15. // 方法一:使用同步程式碼塊
  16. synchronized (this) {
  17. System.out.println("第" + (tickts--) + "張票售出");
  18. try {
  19. Thread.sleep(100);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }
  25. // 方法二:使用同步方法達到執行緒安全
  26. saleTickts();
  27. // 方法三:使用ReentrantLock,更加靈活,可以加入判斷,在特定的情況下釋放鎖
  28. saleTickts2();
  29. }// for
  30. }
  31. // 同步方法:同步的是當前物件
  32. private synchronized void saleTickts() {
  33. if (tickts > 0) {
  34. System.out.println("第" + (tickts--) + "張票售出");
  35. try {
  36. Thread.sleep(100);
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. }// saleTickts
  42. ReentrantLock lock = new ReentrantLock();
  43. // 方法三:使用ReentrantLock,更加靈活,可以加入判斷,在特定的情況下釋放鎖
  44. private void saleTickts2() {
  45. lock.lock();
  46. try {
  47. if (tickts > 0) {
  48. System.out.println("第" + (tickts--) + "張票售出");
  49. try {
  50. Thread.sleep(100);
  51. } catch (InterruptedException e) {
  52. e.printStackTrace();
  53. }
  54. }
  55. // 保證鎖一定會被釋放,不會出現死鎖情況
  56. } finally {
  57. lock.unlock();// 釋放鎖
  58. }
  59. }// saleTickts2
  60. }

7.1Spring中的設計模式

1)BeanFactory和ApplicationContext應用了工廠模式;

2)在Bean的建立中,Spring提供了單例和原型等模式;

3)AOP領域使用的是代理模式 , 裝飾器模式 , 介面卡模式 ;

4)事件監聽使用的是觀察者模式 ;

5) 類似JdbcTemplate等模板物件使用的是模板模式;

7.2設計模式的應用

Builder 建造者模式 , 特點是不通過New的方式建立物件 , 而是通過其他的類來建立物件 . 例如MyBatis 的SqlSessionFactoryBuilder 建立SqlSessionFactory ;

8.Springboot 自動配置的原理

@SpringBootApplication原始碼可檢視到三個關鍵註解;

@SpringBootConfiguration代表一個配置類 , 相當於bean.xml , 在自定義配置類中的@Bean作用於方法或者註解類上 , 表示給容器注入一個bean , 型別為方法的返回值 , 預設id是方法名 , 可以指定BeanId 例如:@Bean(“beanId”)

@ComponentScan自動掃描並載入符合條件的元件或者Bean定義 , 最終將這些Bean定義載入到容器中

@EnableAutoConfiguration開啟自動裝配 .從classpath中搜尋所有的META-INF/spring.factories配置檔案,有大概130行的配置,並將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對應的配置項通過反射(Java Refletion)例項化為對應的標註了@Configuration的JavaConfig形式的IoC容器配置類,然後彙總為一個並載入到IoC容器。

9.資料庫優化

1)選擇欄位合適的屬性 , 儘可能減少定義欄位的寬度 , 儘量把欄位設定成NOTNULL , 能用數字型別 ,不用字串型別 ,建立合適的索引 , 索引列儘可能使NOTNULL;

2)使用連線查詢代替子查詢 ;

3)使用exists , not exists 代替 in not in ;

4)避免在索引使用計算 ;

5)在where 和 order by 常用列上建立索引 ;

6)避免在where 子句對欄位進行 null 值判斷 , 避免進行欄位表示式的操作 , 會導致搜尋引擎放棄使用索引進行全表掃描 ;

7) 建表的時候使用正確的儲存引擎;

8)max 函式欄位可以建立索引;

9)count(欄位) 不包含null , count(*)包含null;

10.如何讓堆疊記憶體溢位

不斷建立物件導致堆記憶體溢位;

遞迴呼叫導致棧記憶體溢位;

11.Mybatis工作原理

Mybatis 核心元件:

  • Configuration MyBatis所有的配置資訊都儲存在Configuration物件之中,配置檔案中的大部分配置都會儲存到該類中
  • SqlSession 作為MyBatis工作的主要頂層API,表示和資料庫互動時的會話,完成必要資料庫增刪改查功能
  • Executor MyBatis執行器,是MyBatis 排程的核心,負責SQL語句的生成和查詢快取的維護
  • StatementHandler 封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設定引數等
  • ParameterHandler 負責對使用者傳遞的引數轉換成JDBC Statement 所對應的資料型別
  • ResultSetHandler 負責將JDBC返回的ResultSet結果集物件轉換成List型別的集合
  • TypeHandler 負責java資料型別和jdbc資料型別(也可以說是資料表列型別)之間的對映和轉換
  • MappedStatement MappedStatement維護一條
  • SqlSource 負責根據使用者傳遞的parameterObject,動態地生成SQL語句,將資訊封裝到BoundSql物件中,並返回
  • BoundSql 表示動態生成的SQL語句以及相應的引數資訊

工作流程:

1.在 MyBatis 執行開始時需要先通過 Resources 載入全域性配置檔案.

2.下面 需要例項化 SqlSessionFactoryBuilder 構建器.幫助 SqlSessionFactory 接 口實現類 DefaultSqlSessionFactory.

3.在例項化 DefaultSqlSessionFactory 之前需要先建立 XmlConfigBuilder 解析全域性配置檔案流,並把解析結果存放在 Configuration 中.之後把 Configuratin 傳遞給 DefaultSqlSessionFactory.到此 SqlSessionFactory 工 廠建立成功.

4.由 SqlSessionFactory 工廠建立 SqlSession. 每次建立 SqlSession 時,都需要由 TransactionFactory 建立 Transaction 物件,同時還需要建立 SqlSession 的執行器 Excutor,最後例項化 DefaultSqlSession,傳遞給 SqlSession 介面.

5.根據專案需求使用 SqlSession 介面中的 API 完成具體的事務操作. 如果事務執行失敗,需要進行 rollback 回滾事務. 如果事務執行成功提交給資料庫.關閉 SqlSession 到此就是 MyBatis 的執行原理

12.Java動態代理

分兩種 : JDK 代理 和 cglib

JDK代理:

1、因為利用JDKProxy生成的代理類實現了介面,所以目標類中所有的方法在代理類中都有。
2、生成的代理類的所有的方法都攔截了目標類的所有的方法。而攔截器中invoke方法的內容正好就是代理類的各個方法的組成體。
3、利用JDKProxy方式必須有介面的存在。
4、invoke方法中的三個引數可以訪問目標類的被呼叫方法的API、被呼叫方法的引數、被呼叫方法的返回型別。
cgLib代理:

1、 CGlib是一個強大的,高效能,高質量的Code生成類庫。它可以在執行期擴充套件Java類與實現Java介面。
2、 用CGlib生成代理類是目標類的子類。
3、 用CGlib生成 代理類不需要介面
4、 用CGLib生成的代理類重寫了父類的各個方法。
5、 攔截器中的intercept方法內容正好就是代理類中的方法體

區別 :

JDK動態代理智慧對實現了介面的類生成代理物件 ;

cglib可以對任意類生成代理物件,它的原理是對目標物件進行繼承代理,如果目標物件被final修飾,那麼該類無法被cglib代理。

spring兩種代理方式

  1. 若目標物件實現了若干介面,spring使用JDK的java.lang.reflect.Proxy類代理。
    優點:因為有介面,所以使系統更加鬆耦合
    缺點:為每一個目標類建立介面
  2. 若目標物件沒有實現任何介面,spring使用CGLIB庫生成目標物件的子類。
    優點:因為代理類與目標類是繼承關係,所以不需要有介面的存在。
    缺點:因為沒有使用介面,所以系統的耦合性沒有使用JDK的動態代理好。

13.JVM相關

Jvm包括堆 , 棧 , 本地方法棧 , 程式計數器 , 方法區 (1.8後叫元空間 , 加入堆中,又要永久代 , 持久代)

jvm 棧 , 本地方法棧是執行緒不共享的 , 每個方法 , 每個執行緒 建立一個棧幀 , 棧幀中有 區域性變量表 , 運算元棧 , 方法出口.

程式計數器 是執行緒記錄執行緒的停止 , 開始 位置

堆包括新生代 , 老年代 , 元空間 ,優化主要是優化堆

新生代 預設佔堆記憶體的1/3 包括 eden , fromservivor , toservivor , 新建立的物件預設放入Eden ,如果太大 , 直接放入老年代

GC 分younggc (minor GC) 和fullgc (major GC)

GC常用標記演算法 可達性分析法 , GCroots 物件 , 當物件沒有引用相連GCRoots ,將物件標記為不可用

GCroot是根節點物件 : 虛擬機器棧的區域性變量表 , 方法區靜態屬性引用的物件 , 方法區中常量引用的物件 , 本地方法引用的物件

垃圾回收演算法 : 複製演算法 , 標記-清除演算法 , 標記-整理演算法 , 分帶收集法

標記清除演算法 缺點 : 效率問題 , 標記和清除 兩個過程的效率不高 ; 空間問題 , 標記清除後產生大量不連續碎片

新生代採用複製演算法 將未回收的物件從fromservivor 到 toservivor , 每遷移一次年齡+1 , 當年齡15的時候 , 將物件從新生代移動到老年代 , 老年代採用標記整理演算法 ;

垃圾回收器 , JDK1.8預設指定的垃圾收集器Paramllel Scavenge

還有G1 和CMS (多用於老年代)垃圾收集器 , G1收集量比CMS大 , 官方推薦G1收集器 , zgc 是目前最優秀的垃圾收集器 , 官方推薦G1收集器

JDK效能調優監控工具

命令 jps 檢視所有Java 程式

jstart -gc pid 記憶體使用情況

jmap 可以在記憶體溢位的時候到處dump 檔案

java visualVM 檢視java 程序的視覺化工具

jstack 用於生成執行緒快照

JVM調優思路

1.列印GC日誌

-XX:+PrintGCDetails -XX:+PrintGCTimestamps -XX:+PrintGCDateStamps +Xloggc: ./gc.log

2.分析日誌得到的關鍵性指標

3.分析GC原因 , 調優JVM引數

調優主要是調整一下兩個指標 :

1.停頓時間 : 垃圾回收器做垃圾回收中段應用執行的時間 -XX:MaxGCPauseMills

2.吞吐量 : 垃圾收集的時間和總時間的佔比 -XX:GCTimeRatio

調優方法 :

  1. 設定元空間Metaspace 大小 , 增大元空間 -XX:Metaspace.size=128M -XX:MaxMetaspace.size=128M
  2. 增大老年代動態擴容量, 一般是20%
  3. 配置垃圾回收器 -XX:+UseConcMarkSweepGC/-XX:+UseG1GC

14.執行緒池建立引數

執行緒池建立拒絕用Excutors 而使用方法:

ExecutorService pool1 = new ThreadPoolExecutor(

​ corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

ExecutorService pool2 = new ThreadPoolExecutor(

​ corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);

ExecutorService pool3 = new ThreadPoolExecutor(

​ corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);

ExecutorService pool4 = new ThreadPoolExecutor(

​ corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);

其引數入下:

corePoolSize:指定了執行緒池中的執行緒數量,它的數量決定了新增的任務是開闢新的執行緒去執行,還是放到workQueue任務佇列中去;

maximumPoolSize:指定了執行緒池中的最大執行緒數量,這個引數會根據你使用的workQueue任務佇列的型別,決定執行緒池會開闢的最大執行緒數量;

keepAliveTime:當執行緒池中空閒執行緒數量超過corePoolSize時,多餘的執行緒會在多長時間內被銷燬;

unit:keepAliveTime的單位

workQueue:任務佇列,被新增到執行緒池中,但尚未被執行的任務;它一般分為直接提交佇列、有界任務佇列、無界任務佇列、優先任務佇列幾種;

threadFactory:執行緒工廠,用於建立執行緒,一般用預設即可;

handler:拒絕策略;當任務太多來不及處理時,如何拒絕任務;

拒絕策略 :

1、AbortPolicy策略:該策略會直接丟擲異常,阻止系統正常工作;

2、CallerRunsPolicy策略:如果執行緒池的執行緒數量達到上限,該策略會把任務佇列中的任務放在呼叫者執行緒當中執行;

3、DiscardOledestPolicy策略:該策略會丟棄任務佇列中最老的一個任務,也就是當前任務佇列中最先被新增進去的,馬上要被執行的那個任務,並嘗試再次提交;

4、DiscardPolicy策略:該策略會默默丟棄無法處理的任務,不予任何處理。當然使用此策略,業務場景中需允許任務的丟失;

15.面向物件的六原則一法則

(1)單一職責原則:一個類只做它該做的事情。其核心就是我們常說的”高內聚”,寫程式碼最終極的原則只有六個字”高內聚、低耦合”,一個物件如果承擔太多的職責,那麼註定它什麼都做不好。

(2)開閉原則:軟體實體應當對擴充套件開放,對修改關閉。(在理想的狀態下,當我們需要為一個軟體系統增加新功能時,只需要從原來的系統派生出一些新類就可以,不需要修改原來的任何一行程式碼。要做到開閉有兩個要點:

①抽象是關鍵,一個系統中如果沒有抽象類或介面系統就沒有擴充套件點;

②封裝可變性,將系統中的各種可變因素封裝到一個繼承結構中,如果多個可變因素混雜在一起,系統將變得複雜而換亂。

(3)依賴倒轉原則:面向介面程式設計。(該原則說得直白和具體一些就是宣告方法的引數型別、方法的返回型別、變數的引用型別時,儘可能使用抽象型別而不用具體型別,因為抽象型別可以被它的任何一個子型別所替代,請參考下面的里氏替換原則。)

(4)里氏替換原則:任何時候都可以用子型別替換掉父型別。簡單的說就是能用父型別的地方就一定能使用子型別。里氏替換原則可以檢查繼承關係是否合理,如果一個繼承關係違背了里氏替換原則,那麼這個繼承關係一定是錯誤的,需要對程式碼進行重構。

需要注意的是:子類一定是增加父類的能力而不是減少父類的能力,因為子類比父類的能力更多,把能力多的物件當成能力少的物件來用當然沒有任何問題。

(5)介面隔離原則:介面要小而專,絕不能大而全。

(6)聚合複用原則:優先使用聚合關係複用程式碼。(通過繼承來複用程式碼是面向物件程式設計中被濫用得最多的東西,因為所有的教科書都無一例外的對繼承進行了鼓吹從而誤導了初學者,類與類之間簡單的說有三種關係,Is-A關係、Has-A關係、Use-A關係,分別代表繼承、關聯和依賴。其中,關聯關係根據其關聯的強度又可以進一步劃分為關聯、聚合和合成,但說白了都是Has-A關係,合成聚合複用原則想表達的是優先考慮Has-A關係而不是Is-A關係複用程式碼。例如Properties類繼承了Hashtable類,API中原話:因為 Properties繼承於 Hashtable,所以可對Properties 物件應用put 和putAll 方法。但不建議使用這兩個方法,因為它們允許呼叫者插入其鍵或值不是String 的項。相反,應該使用setProperty 方法。這個繼承明顯就是錯誤的,更好的做法是在Properties類中放置一個Hashtable型別的成員並且將其鍵和值都設定為字串來儲存資料。

(7)迪米特法則:迪米特法則又叫最少知識原則,一個物件應當對其他物件有儘可能少的瞭解。(迪米特法則簡單的說就是如何做到”低耦合”,門面模式和調停者模式就是對迪米特法則的踐行。

16.一次Http請求的全過程

1、TCP建立連線

HTTP協議是基於TCP協議來實現的,因此首先就是要通過TCP三次握手與伺服器端建立連線,一般HTTP預設的埠號為80;

2、瀏覽器傳送請求命令

在與伺服器建立連線後,Web瀏覽器會想伺服器傳送請求命令

3、瀏覽器傳送請求頭訊息

在瀏覽器傳送請求命令後,還會發送一些其它資訊,最後以一行空白內容告知伺服器已經完成頭資訊的傳送;

4、伺服器應答

在收到瀏覽器傳送的請求後,伺服器會對其進行迴應,應答的第一部分是協議的版本號和應答狀態碼;

5、伺服器迴應頭資訊

與瀏覽器端同理,伺服器端也會將自身的資訊傳送一份至瀏覽器

6、伺服器傳送資料

在完成所有應答後,會以Content-Type應答頭資訊所描述的格式傳送使用者所需求的資料資訊

7、斷開TCP連線

在完成此次資料通訊後,伺服器會通過TCP四次揮手主動斷開連線。但若此次連線為長連線,那麼瀏覽器或伺服器的頭資訊會加入keep-alive的資訊,會保持此連線狀態,在有其它資料傳送時,可以節省建立連線的時間;

17. js 閉包

閉包是指有權訪問另外一個函式作用域中的變數的函式。可以理解為(能夠讀取另一個函式作用域的變數的函式)

  1. function outer() {
  2. var a = '變數1'
  3. var inner = function () {
  4. console.info(a)
  5. }
  6. return inner // inner 就是一個閉包函式,因為他能夠訪問到outer函式的作用域
  7. }

使用場景

1、讀取函式內部的變數
2、讓這些變數的值始終保持在記憶體中。不會再f1呼叫後被自動清除。
3、方便呼叫上下文的區域性變數。利於程式碼封裝。
原因:outer是inner的父函式,inner被賦給了一個全域性變數,inner始終存在記憶體中,inner的存在依賴outer,因此outer也始終存在記憶體中,不會在呼叫結束

18.Runnable()和Callable()的區別

1.區別點:

Callable規定的方法是call(),Runnable規定的方法是run();
Callable的任務執行後可返回值,而Runnable的任務是不能返回值;
call方法可以丟擲異常,run方法不可以;
2.相同點:

兩者都是介面;
兩者都需要呼叫Thread.start()啟動執行緒;

19.concurrentHashMap資料結構

20.Java CAS

cas是compareandswap的簡稱,從字面上理解就是比較並更新,簡單來說:從某一記憶體上取值V,和預期值A進行比較,如果記憶體值V和預期值A的結果相等,那麼我們就把新值B更新到記憶體,如果不相等,那麼就重複上述操作直到成功為止

21.nginx負載均衡策略

a. 輪詢(預設)
每個請求按時間順序逐一分配到不同的後端伺服器,如果後端伺服器 down 掉,能自動剔除。
配置方式:

b. weight
weight 代表權重, 預設為 1,權重越高被分配的客戶端越多

upstream myserver {
server 208.208.128.122:8081 weight=10; # 在這兒
server 208.208.128.122:8082 weight=10;
}
server {
listen 80;
server_name 208.208.128.122;
location / {
root html;
proxy_passhttp://myserver;
index index.html index.htm;
}

c. ip_hash
ip_hash 每個請求按訪問 ip 的 hash 結果分配,這樣每個訪客固定訪問一個後端伺服器

upstream myserver {
ip_hash; // 在這兒
server 208.208.128.122:8081 ;
server 208.208.128.122:8082 ;
}
server {
listen 80;
server_name 208.208.128.122;
location / {
root html;
proxy_passhttp://myserver;
index index.html index.htm;
}

d. fair(第三方)
fair(第三方),按後端伺服器的響應時間來分配請求,響應時間短的優先分配。

upstream myserver {
server 208.208.128.122:8081 ;
server 208.208.128.122:8082 ;
fair; # 在這兒
}
server {
listen 80;
server_name 208.208.128.122;
location / {
root html;
proxy_passhttp://myserver;
index index.html index.htm;
}

Redis 記憶體滿了

Java 用過哪些鎖

ArrayList擴容機制

eureka缺點