前言
梳理了好久,總算是把面試題全部匯出來了,畢竟還要上班,這次就給大家總結了一些Java開發崗位的經典面試題。
篇幅較大,閱讀過程中可能會有點繁瑣! 但請細細觀看,文章末尾有留給大家的小驚喜!!!
千萬不要錯過了~ 話不多說,咱們就直接開整!
Java開發崗面試題JavaOOPJava的資料結構有哪些?線性表(ArrayList)連結串列(LinkedList)棧(Stack)佇列(Queue)圖(Map)樹(Tree)Java中有幾種資料型別四型八種
整形:byte、short、int、long浮點型:float、double字元型:char布林型:booleanString str="aaa",與String str=new String("aaa")一樣嗎?不一樣,第一個字面量宣告字串,會從常量池裡面取,池中沒有則建立,有則複用,後面再同樣宣告一個aaa時,就從池中取出複用。第二個使用new的方式建立,是不會放到常量池中的,所以也不會複用。String、StringBuffffer 和 StringBuilder 的區別是什麼?String是隻讀字串,它不是基本資料型別,而是一個物件,它的底層是一個final的char字元陣列,一經定義,就不能增加和修改,每次對String的操作都是重新生成一個String字串物件。StringBuffffer和StringBuilder都繼承了AbstractStringBulder類,2個類都是用來進行字串操作的。StringBuffffer是執行緒安全的,而StringBuilder是非執行緒安全的,所以StringBuilder效率比StringBuffffer高,StringBuffffer類的方法大多數都加了synchronized關鍵字。抽象類和介面的區別是什麼?抽象類需要使用abstract關鍵字定義,它可以有抽象方法和例項方法,抽象方法沒有方法體,需要子類實現。包含抽象方法的類,一定是抽象類抽象只能被繼承,不能被例項化介面介面的方法全部都是抽象方法,屬性都是常量介面不能被例項化介面只能被實現,實現介面的類必須實現所有的抽象方法,除非該類是抽象類,可以選擇實現部分抽象方法,剩餘了讓子類實現介面可以多繼承有了equals(),為什麼還需要hashCode()Java集合有2類,List和Set,List集合有序可重複,而Set集合無序但不可重複。Set集合保證唯一的方法,就是插入時判斷物件的equals()方法,是否和已有元素重複,但是元素較多時,呼叫equals()方法就會很低效。所以增加了hashCode(),透過元素物件的記憶體地址計算出一個hash值,比較時,先比較hashCode,hashCode的效率非常高,當元素的hashCode相同時,再呼叫元素的equals()進行比較,這樣效率就提升了。介紹Java的強、弱、軟、虛,4種引用強引用,強引用在記憶體不足時,寧願發生OOM也不願意回收它。軟引用,使用SoftReference包裹引用,記憶體不足時,就會回收。弱引用,使用WeakReference包裹引用,只要JVM垃圾回收發現了它,就會回收。虛引用,回收機制和弱引用差不多,但是它在被回收前,會放入到ReferenceQueue佇列中,並且虛引用宣告時,必須傳ReferenceQueue佇列。因為這個機制,大部分用虛引用來做引用銷燬前的處理工作。Java建立物件有幾種方式?有4種:
new關鍵字透過反射機制透過clone克隆機制透過序列化和反序列化機制淺複製和深複製的區別是什麼?例如一個物件中有一個List,淺複製和深複製效果不同。
淺複製,只複製外層物件,它引用的List並不會複製,所以原物件和複製物件的List物件是同一個。深複製,外層物件複製,它所有引用的物件也複製,所以複製的物件,它引用的List物件是新的一個。final、finalize()、finally,它們有什麼區別?finalfinal關鍵字標記的變數為常量final關鍵字標記的類,不可繼承final關鍵字標記的方法,不可被複寫finalizefinalize()方法,在Object類上定義,用於物件被回收時,被回撥,類似C++中的解構函式,可用於對物件進行銷燬前的處理,但由於GC後再進行特殊處理,可能會出現記憶體溢位的風險,所以不推薦使用。finallyfinally用於標識程式碼塊,和try配合使用,它在return之前執行,無論try程式碼塊中是否發生異常,finally程式碼塊中的程式碼必定會執行。使用JDBC中,如何防止SQL注入使用PreparedStatement類,而不是使用Statement類。Java集合、泛型ArrayList和LinkedList的區別?ArrayList底層使用陣列,它的查詢使用索引,所以效率高,但是增、刪很慢,因為增、刪都需要重排陣列,例如刪除中間的元素,就需要把後面的元素向前挪。LinkedList底層使用連結串列,增、刪元素只需要修改元素的前節點和後節點即可,所以效率高,但查詢效率比較差。HashMap和HashTable的區別繼承關係不同HashMap是繼承自AbstractMap類,而Hashtable是繼承自Dictionary類。對null支援不同Hashtable,key和value都不能為nullHashMap,key可以為null,但是這樣的key只能有一個,而可以多個key的value值為null執行緒安全Hashtable是執行緒安全的,它的每個方法都有synchronized 關鍵字,所以多執行緒環境下可以使用它。HashMap是執行緒不安全的,但它的效率比Hashtable高,加上大部分場景都是單執行緒,如果在多執行緒環境下,推薦使用ConcurrentHashMap,因為它使用了分段鎖,並不對整個資料進行鎖定。Collection和Collections的不同Collection是集合的父介面,子介面有List、Set。Collections是集合類的工具類,提供了很多對集合操作的靜態方法,可對集合進行搜尋、排序、以及執行緒安全包裝等。List、Set、Map,3者的區別List,有序,可重複Set,無序,不可重複Map,無序,鍵值對儲存,Key不可重複,Value可重複Array和ArrayList有什麼區別?Array和ArrayList都是用來儲存資料,ArrayList的底層就是使用Array實現的,但它對Array的擴容和功能進行了拓展。說說List介面和它的實現類List介面是Collection介面的子介面。List介面的實現類都是有序,可重複的。List介面的實現類有ArrayList、Vector和LinkedList。實現類ArrayList,底層透過陣列實現,它的特點是支援索引查詢,所以支援對元素的隨機訪問。缺點是增、刪元素時,需要對陣列進行復制、移動,代價比較高,所以它適合隨機訪問,不適合插入和刪除。Vector,底層和ArrayList一樣是透過陣列實現,但它的方法都加了同步鎖,所以它可以在多執行緒環境下訪問,避免同一時段對集合併發讀寫引起不一致性問題,但同步需要效能開銷,所以它的效率比ArrayList低。LinkedList,底層使用連結串列實現,很適合元素的動態插入和刪除,但隨機訪問和遍歷效率會比較低,另外它實現了Deque介面,所以擁有操作頭元素、尾元素的方法,所以可以用來當做棧和佇列使用。說說Set介面和它的實現類Set介面,也是Collection介面的子介面,Set介面的實現類都是不可重複的。Set介面的實現類有HashSet、TreeSet、LinkedHashSet。Set集合不可重複的特性是透過呼叫元素的hashCode()進行比較,如果相同,再呼叫equals()進行比較,都相同,就視為相同,不會新增到集合中。實現類HashSet。底層透過Hash表實現,不可重複的特性是透過呼叫元素的hashCode()進行比較,如果相同,再呼叫equals()進行比較,都相同,就視為相同,不會新增到集合中。TreeSet,底層透過二叉樹實現,可對元素進行插入時就排序,它要求插入的元素比較實現Comparable介面,複寫compareTo()函式,或者在建立TreeSet時傳入自定義Comparator比較器物件,否則會在執行時丟擲java.lang.ClassCastException型別的異常。LinkedHashSet,底層是使用LinkedHashMap,但它只使用了Map中的Key的唯一特性,來保證不可重複。說說Map集合和它的實現類Map介面,專門用來實現鍵值對操作的集合類介面。它的實現類有HashMap、HashTable、TreeMap和LinkedHashMap。實現類HashMap,底層使用Hash表實現,它透過元素的hashCode來確定儲存的位置,所以有很快的查詢速度,但它遍歷時的順序是不確定的。HashMap只允許一個key為null,但可以多個key的value為null。HashMap是非執行緒安全的,所以多執行緒環境下,對HashMap進行併發寫會出現不一致性問題,可以透過Collections的synchronizedMap()方法對HashMap進行包裝,讓HashMap具有執行緒安全的能力,或者使用ConcurrentHashMap。在JDK1.8之前,HashMap底層是使用Hash表和連結串列實現,當發生hash衝突時,同一個位置的元素會形成連結串列儲存,但是元素一多時,查詢就會變為連結串列的遍歷,效率比較低。在JDK1.8時,HashMap底層就變成Hash表和連結串列\紅黑樹實現,當連結串列中的元素個數超過8時,連結串列轉換為紅黑樹,避免遍歷,優化了查詢效率。HashTable,底層和JDK1.7的HashMap類似,但它的key和value都不能為null,而且Hashtable是執行緒安全的,它的每個方法都有synchronized 關鍵字,所以多執行緒環境下可以使用它。TreeMap,底層是透過二叉樹實現,它能在元素新增時,進行排序,它要求元素必須實現Comparable介面,複寫compareTo()函式,或者在建立TreeMap時傳入自定義Comparator比較器物件,否則會在執行時丟擲java.lang.ClassCastException型別的異常。LinkedHashMap,它是HashMap的一個子類,儲存了插入順序,而其他Map實現類是無序的。什麼是泛型?什麼是泛型擦除?泛型可以對型別進行抽象,可以讓型別支援不同型別而重用,例如容器類ArrayList,透過泛型,ArrayList可以儲存不同的元素,並且泛型後的Array元素是具體型別而不是Object,避免了型別轉換的麻煩,而且編譯時會報錯,避免了型別轉換可能發生的型別轉換錯誤。泛型擦除,因為Java的泛型是透過編譯時實現的,生成的Java位元組碼中是不存在泛型資訊的,所以泛型資訊,在編譯器編譯時,會將泛型資訊去除,這個過程就是泛型擦除。所以List上附加的泛型資訊,在JVM來說是不可見的,在它眼裡都是Object型別。Java異常面試題Java異常分為哪幾種?編譯時異常執行時異常介紹一下Java的異常處理機制捕獲異常,使用try-catch-finally異常丟擲,使用throws關鍵字如果自定義一個異常繼承一個異常類,執行時異常繼承RuntimeException,編譯時異常繼承Exception。try-catch-finally,try中有return,finally還執行嗎?執行,finally的執行在return之前,不管try中有沒有異常,都會執行。另外如果finally中有return,則會在try的return之前執行,那麼try中的return就執行不到了。Excption與Error的關係Excption與Error都是Throwable的子類。Excption有執行時異常和編譯時異常,他們都是可以捕獲和處理。編譯時異常常見有:IOException、FileNotFoundException、SQLException,必須在程式碼中處理,否則編譯會報錯。執行時異常常見有:ClassCastException、IndexOutOfBoundsException、NullPointerExceptionError,和執行時異常一樣,編譯器也不會對錯誤進行檢查。當資源不足、約束失敗、或是其它程式無法繼續執行的條件發生時,就產生錯誤。程式本身無法修復這些錯誤的。常見子類有OutOfMemoryErrorJava中的IO和NIO面試題Java的IO流分為幾種按流的流向分,可以分為輸入流和輸出流按操作的單元分,可以分為位元組流和字元流按照流的角色分,可以分為節點流和處理流Java IO流中40多個類,都是從以下4個抽象基類中派生出來的:
InputStream\Reader,所有輸入流的基類,InputStream為字元輸入流,Reader為字元輸入流。OutputStream\Writer,所有輸出流的基類,OutputStream為位元組輸出流,Writer為字元輸出流。Java中IO和NIO的區別?NIO被稱為New IO,在JDK1.4中引入,NIO和IO有相同的目的和作用,但實現方式不同。NIO主要用到的是塊,而IO是位元組Byte,所以NIO的效率要比IO高很多。Java提供了2套NIO,一套針對檔案,另一套針對網路程式設計。常用io類有哪些?位元組FileInputSteam、FileOutputStreamBufferInputStream、BufferedOutputSream字元FileReader、FileWriterBufferReader、BufferedWriter物件序列化ObjectInputStream、ObjectOutputSream什麼是Java NIONIO 主要有三大核心部分: Channel(通道), Buffer(緩衝區), Selector。傳統 IO 基於位元組流和字元流進行操作, 而 NIO 基於 Channel 和 Buffer(緩衝區)進行操作,資料總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。 Selector(選擇區)用於監聽多個通道的事件(比 如:連線開啟,資料到達)。因此,單個執行緒可以監聽多個數據通道。NIO 和傳統 IO 之間第一個最大的區別是, IO 是面向流的, NIO 是 面向緩衝區的。什麼是NIO的ChannelChannel,一般翻譯為通道。 Channel 和 IO 中的 Stream(流)是差不多一個等級的。 只不過 Stream 是單向的,譬如: InputStream, OutputStream, 而 Channel 是雙向的,既可以用來進行讀操作,又可以用來進行寫操作。NIO 中的 Channel 的主要實現類FileChannel(檔案操作)DatagramChannel(UDP操作)SocketChannel(TCP客戶端)ServerSocketChannel(TCP服務端)什麼是NIO的BufferBuffer,故名思意, 緩衝區,實際上是一個容器,是一個連續陣列。 Channel 提供從檔案、網路讀取資料的渠道,但是讀取或寫入的資料 都必須經由 Buffer。從一個客戶端向服務端傳送資料,然後服務端接收資料的過程。客戶端傳送資料時,必須先將資料存入 Buffer 中,然後將 Buffer 中的內容寫入通道。服務端這邊接收資料必須透過 Channel 將資料讀入到 Buffer 中,然後再從 Buffer 中取出資料來處理。在 NIO 中, Buffer 是一個頂層父類,它是一個抽象類,常用的 Buffer 的子類有ByteBufferShortBufferIntBufferLongBufferFloatBufferDoubleBufferCharBuffer什麼是NIO的SelectorSelector 類是 NIO 的核心類, Selector 能夠檢測多個註冊的通道上是否有事件發生,如果有事件發生,便獲取事件然後針對每個事件進行 相應的響應處理。這樣一來,只是用一個單執行緒就可以管理多個通道,也就是管理多個連線。這樣使得只有在連線真正有讀寫事件發生時, 才會呼叫函式來進行讀寫,就大大地減少了系統開銷,並且不必為每個連線都建立一個執行緒,不用去維護多個執行緒,並且避免了多執行緒之間 的上下文切換導致的開銷。Java反射面試題Java反射建立物件效率高,還是new建立物件的效率高透過new建立物件的效率比較高。透過反射時,先找查詢類資源,使用類載入器建立,過程比較繁瑣,所以效率較低Java反射的作用反射機制是在執行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意個物件,都能夠呼叫它的任意一個方法。在java 中,只要給定類的名字,就可以透過反射機制來獲得類的所有資訊。哪裡會用到反射機制例如:載入MySQL的驅動類,如Hibernate、MyBatis等框架中會使用。
//載入MySQL的驅動類Class.forName('com.mysql.jdbc.Driver.class');複製程式碼
反射機制的優缺點優點能夠執行時動態獲取類的例項,提高靈活性與動態編譯結合缺點使用反射效能較低,需要解析位元組碼,將記憶體中的物件進行解析相對不安全,破壞了封裝性(因為透過反射可以獲得私有方法和屬性)解決方案透過setAccessible(true),關閉JDK的安全檢查來提升反射速度第一次反射後,會有快取,下次反射會快很多ReflflectASM工具類,透過位元組碼生成的方式加快反射速度Java註解面試題註解是什麼?Annotation(註解)是 Java 提供的一種對元程式中元素關聯資訊和元資料(metadata)的途徑和方法。 Annatation(註解)是一個介面,程 序可以透過反射來獲取指定程式中元素的 Annotation物件,然後透過該 Annotation 物件來獲取註解中的元資料資訊。4種標準元註解是哪四種?@Target,修飾的物件範圍@Target說明了Annotation所修飾的物件範圍: Annotation可被用於 packages、types(類、介面、列舉、Annotation 型別)、型別成員 (方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如迴圈變數、catch 引數)。在 Annotation 型別的宣告中使用了 target 可更加明晰其修飾的目標@Retention,定義被保留的時間長短Retention 定義了該 Annotation 被保留的時間長短:表示需要在什麼級別儲存註解資訊,用於描述註解的生命週期(即:被描述的註解在 什麼範圍內有效),取值(RetentionPoicy)由:SOURCE:在原始檔中有效(即原始檔保留)CLASS:在 class 檔案中有效(即 class 保留)RUNTIME:在執行時有效(即執行時保留)@Inherited 闡述了某個被標註的型別是被繼承的@Inherited 元註解是一個標記註解,@Inherited 闡述了某個被標註的型別是被繼承的。如果一 個使用了@Inherited 修飾的 annotation 型別被用於一個 class,則這個 annotation 將被用於該class 的子類。Java多執行緒、併發面試題Java中實現多執行緒有幾種方法一共有4種方式
繼承Thread類實現Runnable介面實現Callable介面,透過FutureTask包裝器,來建立Thread執行緒使用ExecutorService、Callable、Future實現有返回結果的多執行緒(也就是使用了ExecutorService,管理前面的三種方式)如何停止一個正在執行的執行緒使用退出標誌,使執行緒正常退出,也就是當run()方法完成後執行緒終止使用stop方法強行終止,但是不推薦這個方法,可能會導致執行緒操作的資料不一致使用interrupt方法中斷執行緒,並捕獲InterruptedException異常volatile是什麼?可以保證有序性嗎?共享變數(類的成員變數、類的靜態成員變數)被volatile修飾之後,那麼就具備了兩層語義保證不同執行緒對這個共享變數進行操作時,有可見性,就是其中一個執行緒對該變數值進行修改,其他執行緒是馬上可見的,volatile關鍵字會強制將修改的值同步到主記憶體。禁止指令重排,禁止編譯器最佳化程式碼順序,避免在單例Double Check中導致多次初始化,保證有有序性。注意,volatile不能保證原子性。Thread 類中的start() 和 run() 方法有什麼區別?start()方法被用來啟動新建立的執行緒,而且start()內部呼叫了run()方法,這和直接呼叫run()方法的效果不一樣。當你呼叫run()方法的時候,只會是在原來的執行緒中呼叫,沒有新的執行緒啟動,start()方法才會啟動新執行緒。Java中synchronized 和 ReentrantLock 有什麼不同?相似點這兩種同步方式有很多相似之處,它們都是加鎖方式同步,而且都是阻塞式的同步,也就是說當如果一個執行緒獲得了物件鎖,進入了同步塊,其他訪問該同步塊的執行緒都必須阻塞在同步塊外面等待,而進行執行緒阻塞和喚醒的代價是比較高的。區別對於Synchronized來說,它是java語言的關鍵字,是原生語法層面的互斥,需要jvm實現。而ReentrantLock它是JDK 1.5之後提供的API層面的互斥鎖,需要lock()和unlock()方法配 合try/finally語句塊來完成。Synchronized進過編譯,會在同步塊的前後分別形成monitorenter和monitorexit這個兩個位元組碼指令。在執行monitorenter指令時,首先要嘗試獲取物件鎖。如果這個物件沒被鎖定,或者當前執行緒已經擁有了那個物件鎖,把鎖的計數器加1,相應的,在執行monitorexit指令時會將鎖計數器就減1,當計數器為0時,鎖就被釋放了。如果獲取物件鎖失敗,那當前執行緒就要阻塞,直到物件鎖被另一個執行緒釋放為止 。ReentrantLock特性等待可中斷,持有鎖的執行緒長期不釋放的時候,正在等待的執行緒可以選擇放棄等待,這相當於Synchronized來說可以避免出現死鎖的情況。公平鎖,多個執行緒等待同一個鎖時,必須按照申請鎖的時間順序獲得鎖,Synchronized鎖非公平鎖, ReentrantLock預設的建構函式是建立的非公平鎖,可以透過引數true設為公平鎖,但公平鎖表現的性 能不是很好。鎖繫結多個條件,一個ReentrantLock物件可以同時繫結對個物件 。SynchronizedMap和ConcurrentHashMap有什麼區別?SynchronizedMap()和Hashtable一樣,實現上在呼叫map所有方法時,都對整個map進行同步。而 ConcurrentHashMap的實現卻更加精細,它對map中的所有桶加了鎖。所以,只要有一個執行緒訪問 map,其他執行緒就無法進入map,而如果一個執行緒在訪問ConcurrentHashMap某個桶時,其他執行緒, 仍然可以對map執行某些操作。所以,ConcurrentHashMap在效能以及安全性方面,明顯比Collections.synchronizedMap()更加有優勢。同時,同步操作精確控制到桶,這樣,即使在遍歷map時,如果其他執行緒試圖對map進行資料修改,也不會丟擲ConcurrentModificationException 。Java執行緒池中submit() 和 execute()方法有什麼區別?兩個方法都可以向執行緒池提交任務。
execute()方法,它的返回型別是void,任務執行完後,沒有返回值結果,它定義在Executor介面中。submit()方法,它可以返回持有計算結果的Future物件,它定義在ExecutorService介面中,它擴充套件了Executor介面。說一說自己對於 synchronized 關鍵字的瞭解synchronized關鍵字解決的是多個執行緒之間訪問資源的同步性,synchronized關鍵字可以保證被它修飾的方法或者程式碼塊在任意時刻只能 有一個執行緒執行。在 Java 早期版本中,synchronized屬於重量級鎖,效率低下,因為監視器鎖(monitor)是依賴於底層的作業系統的 Mutex Lock 來 實現的,Java 的執行緒是對映到作業系統的原生執行緒之上的。如果要掛起或者喚醒一個執行緒,都需要作業系統幫忙完成,而作業系統實現執行緒之間的切換時需要從使用者態轉換到核心態,這個狀態之間的 轉換需要相對比較長的時間,時間成本相對較高,這也是為什麼早期的synchronized 效率低的原因。在 Java 6 之後 Java 官方對從 JVM 層面對synchronized 較大最佳化,所以現在的 synchronized 鎖效率也最佳化得很不錯了。JDK1.6對鎖的實現引入了大量的最佳化,如自旋鎖、適應性自旋鎖、鎖消除、鎖粗化、偏向鎖、輕量級鎖等技術來減少鎖操作的開銷。volatile關鍵字的作用?一旦一個共享變數(類的成員變數、類的靜態成員變數)被volatile修飾之後,那麼就具備了兩層語義: 保證了不同執行緒對這個變數進行操作時的可見性,即一個執行緒修改了某個變數的值,這新值對其他執行緒來說是立即可見的。禁止進行指令重排序。volatile本質是在告訴jvm當前變數在暫存器(工作記憶體)中的值是不確定的,需要從主存中讀取;synchronized則是鎖定當前變數, 只有當前執行緒可以訪問該變數,其他執行緒被阻塞住。volatile僅能使用在變數級別;synchronized則可以使用在變數、方法、和類級別的。volatile僅能實現變數的修改可見性,並不能保證原子性;synchronized則可以保證變數的修改可見性和原子性。volatile不會造成執行緒的阻塞;synchronized可能會造成執行緒的阻塞。volatile標記的變數不會被編譯器最佳化;synchronized標記的變數可以被編譯器最佳化。簡述一下你對執行緒池的理解降低資源消耗。透過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗。提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。提高執行緒的可管理性。執行緒是稀缺資源,如果無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,使用執行緒池可以進行統一的分配,調優和監控。執行緒生命週期新建狀態(NEW)當程式使用 new 關鍵字建立了一個執行緒之後,該執行緒就處於新建狀態,此時僅由 JVM 為其分配記憶體,並初始化其成員變數的值。就緒狀態(RUNNABLE)當執行緒物件呼叫了 start()方法之後,該執行緒處於就緒狀態。 Java 虛擬機器會為其建立方法呼叫棧和程式計數器,等待排程執行。執行狀態(RUNNING)如果處於就緒狀態的執行緒獲得了 CPU,開始執行 run()方法的執行緒執行體,則該執行緒處於執行狀態。阻塞狀態(BLOCKED)阻塞狀態是指執行緒因為某種原因放棄了 cpu 使用權,也即讓出了 cpu timeslice,暫時停止執行。直到執行緒進入可執行(runnable)狀態,才 有機會再次獲得 cpu timeslice 轉到執行(running)狀態。阻塞的情況分三種等待阻塞(o.wait->等待對列),執行(running)的執行緒執行 o.wait()方法, JVM 會把該執行緒放入等待佇列(waitting queue)中。同步阻塞(lock->鎖池),執行(running)的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則 JVM 會把該執行緒放入鎖池(lock pool)中。其他阻塞(sleep/join),執行(running)的執行緒執行 Thread.sleep(long ms)或 t.join()方法,或者發出了 I/O 請求時,JVM 會把該執行緒置為阻塞狀態。當 sleep()狀態 超時、 join()等待執行緒終止或者超時、或者 I/O處理完畢時,執行緒重新轉入可執行(runnable)狀態。執行緒死亡(DEAD)執行緒會以下面三種方式結束,結束後就是死亡狀態。run()或 call()方法執行完成,執行緒正常結束。執行緒丟擲一個未捕獲的 Exception 或 Error,執行緒異常結束。呼叫stop停止。直接呼叫該執行緒的 stop()方法來結束該執行緒—該方法通常容易導致死鎖,不推薦使用。什麼是樂觀鎖?樂觀鎖是一種樂觀思想,即認為讀多寫少,遇到併發寫的可能性低,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新 的時候會判斷一下在此期間別人有沒有去更新這個資料,採取在寫時先讀出當前版本號,然後加鎖操作(比較跟上一次的版本號,如果一樣則更新),如果失敗則要重複,讀 比較 寫的操作。Java中的樂觀鎖基本都是透過 CAS 操作實現的, CAS 是一種更新的原子操作, 比較當前值跟傳入值是否一樣,一樣則更新,否則失敗。什麼是悲觀鎖?悲觀鎖是就是悲觀思想,即認為寫多,遇到併發寫的可能性高,每次去拿資料的時候都認為別人會修改,所以每次在讀寫資料的時候都會上 鎖,這樣別人想讀寫這個資料就會 block 直到拿到鎖。Java中的悲觀鎖就是Synchronized,AQS框架下的鎖則是先嚐試CAS樂觀鎖去獲取鎖, 獲取不到,才會轉換為悲觀鎖,如RetreenLock。什麼是可重入鎖(遞迴鎖)本文裡面講的是廣義上的可重入鎖,而不是單指 JAVA 下的 ReentrantLock。 可重入鎖,也叫 做遞迴鎖,指的是同一執行緒 外層函式獲得鎖之後 ,內層遞迴函式仍然有獲取該鎖的程式碼,但不受 影響。在 JAVA 環境下 ReentrantLock 和 synchronized 都是 可重入鎖。公平鎖與非公平鎖公平鎖(Fair)加鎖前檢查是否有排隊等待的執行緒,優先排隊等待的執行緒,先到先得。非公平鎖(Nonfair)加鎖時不考慮排隊等待問題,直接嘗試獲取鎖,獲取不到自動到隊尾等待對比非公平鎖效能比公平鎖高 5~10 倍,因為公平鎖需要在多核的情況下維護一個佇列。Java 中的 synchronized 是非公平鎖, ReentrantLock 預設的 lock()方法採用的是非公平鎖。在 Java 中 Executor 和 Executors 的區別?Executors 工具類的不同方法按照我們的需求建立了不同的執行緒池,來滿足業務的需求。Executor 介面物件能執行我們的執行緒任務。ExecutorService 介面繼承了 Executor 介面並進行了擴充套件,提供了更多的方法我們能獲得任務執行的狀態並且可以獲取任務的返回值。 使用 ThreadPoolExecutor 可以建立自定義執行緒池。Future 表示非同步計算的結果,他提供了檢查計算是否完成的方法,以等待計算的 完成,並可以使用 get()方法獲取計算的結果。MySQL面試題什麼是資料庫引擎?資料庫儲存引擎是資料庫底層軟體組織,資料庫管理系統(DBMS)使用資料引擎進行建立、查詢、更新和刪除資料。不同的儲存引擎提供 不同的儲存機制、索引技巧、鎖定水平等功能,使用不同的儲存引擎,還可以 獲得特定的功能。現在許多不同的資料庫管理系統都支援多 種不同的資料引擎。儲存引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。InnoDB底層資料結構是什麼?適用什麼場景?InnoDB底層資料結構為B+樹,B+樹的每個節點對應InnoDB的一個page,page的大小是固定的,一般設為16k,其中非葉子節點只有鍵值,葉子節點包含資料。適用場景經常更新的表,適合處理多重併發的更新請求。支援事務。可以從災難中恢復(透過bin-log日誌等)外來鍵約束(只有它支援外來鍵約束)支援自動增加列屬性(auto_increment)MyIASM的優點和缺點是什麼?MyIASM是 MySQL預設的引擎優點ISAM 執行讀取操作的速度很快,而且不佔用大量的記憶體和儲存資源。缺點不支援事務。表級鎖,不支援行級鎖和外來鍵,因此當INSERT(插入)或UPDATE(更新)資料 時即寫操作需要鎖定整個表,效率會低一些。InnoDB與MyISAM的區別InnoDB支援事務,MyISAM不支援,InnoDB將每條SQL語句都預設新增事務,自動提交,這樣會影響效率,所以最好將多條SQL語句放在begin和commit之間,組成一個事務。InnoDB支援外來鍵,MyISAM不支援,如果將一個包含外來鍵的InnoDB錶轉為MyISAM會失敗。InnoDB是聚集索引,資料檔案和索引是綁在一起的,必須有主鍵,透過主鍵查詢效率會很高。MyISAM是非聚集索引,資料檔案和索引是分離的。InnoDB不儲存表的具體行數,執行select count(*) from table時需要全表掃描,而MyISAM用一個變數儲存,執行上面這個語句時,只要讀出該變數即可,速度很快。InnoDB不支援全文索引,而MyISAM支援,所以MyISAM的查詢效率比較高。什麼是索引?有幾種索引?索引越多越好嗎?索引是加快檢索表中資料的方法,資料庫的索引類似書籍的索引,在書籍中,允許使用者不必翻閱整本書就能迅速的找到需要的資訊,在資料庫中,索引也允許資料庫迅速地找到表中的資料,而不必掃描整個資料庫。MySQL中有4種不同的索引主鍵索引唯一索引普通索引全文索引索引不是越多越好,建立索引也需要消耗資源,一是增加了資料庫的儲存空間,二是插入和刪除表資料,都要花較多的時間維護索引。常見索引原則欄位是唯一的,建立唯一索引,可以更快速透過索引來確定某條記錄。經常需要排序、分組、聯合操作的欄位建立索引。為常作為查詢條件的欄位建立索引。刪除不再使用或很少使用的索引。索引列不能參加計算,帶函式的查詢,不參與索引。最左字首匹配原則。資料庫的三正規化是什麼?第一正規化,列不可再分第二正規化,行有唯一區分的欄位,主鍵約束第三正規化,表的非主屬性不能依賴與其他表的非主屬性外來鍵約束什麼是資料庫事務?事務(TRANSACTION)是作為單個邏輯工作單元執行的一系列操作, 這些操作作為一個整體一起向系統提交,要麼都執行、要麼都不執行。事務是一個不可分割的工作邏輯單元,事務必須具備以下四個屬性,簡稱 ACID 屬性原子性(Atomicity)事務是一個完整的操作。事務的各步操作是不可分的(原子的);要麼都執行,要麼都不執 行。一致性(Consistency)當事務完成時,資料必須處於一致狀態。隔離性(Isolation)對資料進行修改的所有併發事務是彼此隔離的, 這表明事務必須是獨立的,它不應以任何方 式依賴於或影響其他事務。永久性(Durability)事務完成後,它對資料庫的修改被永久保持,事務日誌能夠保持事務的永久性。SQL最佳化查詢語句中不要使用select *儘量減少子查詢,使用關聯查詢(left join、right join、inner join)替代減少使用IN或者NOT IN ,使用exists,not exists或者關聯查詢語句替代or 的查詢儘量用 union 或者 union all 代替(在確認沒有重複資料或者不用剔除重複資料時,union all會更好)應儘量避免在 where 子句中使用!=或<>運算子,否則將引擎放棄使用索引而進行全表掃描。應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如: select id from t where num is null 可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢: select id from t where num = 0drop、delete與truncate的區別delete和truncate只刪除表的資料不刪除表的結構。delete刪除記錄,不刪除表結構,delete語句可以加where,刪除操作會記錄在日誌,可以回滾,刪除時,會啟用觸發器。truncate刪除記錄,不刪除表結構,truncate語句不可以加where,刪除操作不記錄在日誌,不能回滾,不會啟用觸發器。drop會刪除記錄和結構,不會啟用觸發器。刪除速度來講,drop > truncate > delete什麼是內聯接、左外聯接、右外聯接?內聯接(Inner Join):匹配2張表中相關聯的記錄。(2張表中沒有關聯的部分拋棄)左外聯接(Left Outer Join):除了匹配2張表中相關聯的記錄外,還會匹配左表中剩餘的記錄,右表中未匹配到的欄位用NULL表示。(以左邊記錄匹配,如果右表中沒有記錄與其匹配,欄位值為NULL)右外聯接(Right Outer Join):除了匹配2張表中相關聯的記錄外,還會匹配右表中剩餘的記錄,左表中未匹配到的欄位用NULL表示。(以右邊記錄匹配,如果左表中沒有記錄與其匹配,欄位值為NULL)併發事務帶來哪些問題?髒讀(Dirty read),當一個事務讀取了資料,並且修改了,但還未提交到資料庫中,另外一個事務也讀取了資料,並且使用了該資料,這時另外一個數據讀取到的資料就是“髒資料”,根據“髒資料”所做的處理可能是不正確的。丟失修改(Lost to modify),當一個事務讀取了資料,另外一個事務也讀取了資料,在第一個事務修改了資料後,第二個事務也修改了資料,這樣第一個事務的修改則被丟失,因為為“丟失修”,例如事務1讀取了資料A=20,事務2也讀取了A=20,事務1修改A=A1,事務2也修改A=A-1,最終結果為A=19,事務1的修改被丟失了。不可重複讀(Unrepeatableread),指一個事務多次讀取1個數據,在這個事務還未結束時,另外一個事務也訪問該資料,如果第二個事務修改了資料,導致第一個事務的多次讀取的資料結果可能是不一樣的,因此成為不可重複讀。幻讀(Phantom read),幻讀和不可重複讀類似,它發生在一個事務讀取了幾行資料,接著另外一個事務插入了一些資料,在隨後的查詢中,第一個事務發現多了一些原本不存在的資料,就像產生了幻覺一樣,所以稱為幻讀。不可重複讀和幻讀的區別不可重複讀的重點是修改,例如多次讀取一條記錄,發現記錄的某一列的值被修改。幻讀的重點是新增或減少,例如多次讀取,發現記錄增多或減少了。事務隔離級別有哪些?MySQL的預設隔離級別是?SQL 標準定義了四個隔離級別
READ-UNCOMMITTED(讀取未提交):最低的隔離級別,允許讀取尚未提交的資料變更,可能會導致髒讀、幻讀或不可重複讀。READ-COMMITTED(讀取已提交): 允許讀取併發事務已經提交的資料,可以阻止髒讀,但是幻讀或不可重複讀仍有可能發生。REPEATABLE-READ(可重複讀): 對同一欄位的多次讀取結果都是一致的,除非資料是被事務本身自己所修改,可以阻止髒讀和不可重複 讀,但幻讀仍有可能發生。SERIALIZABLE(可序列化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止髒讀、不可重複讀以及幻讀。MySQL InnoDB 儲存引擎的預設支援的隔離級別是 REPEATABLE-READ(可重讀),我們可以透過 SELECT @@tx_isolation; 命令來檢視。但要注意,MySQL InnoDB在 REPEATABLE-READ(可重讀)隔離級別下,使用的是Next-Key Lock 鎖演算法,因此可以避免幻讀的產生,所以MySQL預設的的隔離級別,REPEATABLE-READ級別也達到了SERIALIZABLE(可序列化)級別的隔離要求。因為級別越高,事務請求的鎖越多,所以大部分的資料庫隔離級別都是READ-COMMITTED(讀取已提交)。
大表如何最佳化?限定查詢資料的範圍,例如查詢訂單歷史時,控制查詢一個月內的訂單。讀、寫分離,主庫複寫寫,從庫負責讀。垂直分割槽,例如使用者表既有使用者的登入資訊,也有使用者的基本資訊,可以進行垂直拆分,把使用者表拆分為2張表,就是把資料列拆分到多張表。水平分割槽,保持表結構不變,透過某種策略將資料分散到不同的表或庫中,例如根據年、月,一年一張表,一月一張表,水平分割槽可以支援非常大的資料量。水平分割槽的分表只能解決單張表的儲存資料量過大問題,但由於資料還是在一臺機器上,對於提供併發能力並沒有什麼意義,所以水平拆分最好分庫。分庫分表後,主鍵id如何處理分庫分表後,每個表的id都是從1開始累加,這樣是不對的,我們需要一個全域性唯一id來支援。
不過,驚喜還是要有的,關於面試題已經給大家整理完畢了~
如下圖:
還有驚喜禮包~
開個玩笑,有需要獲取如上資料的粉絲朋友,評論轉發後後臺私信:面試獲取即可,是不是很方便,對,就是這麼的方便,私信後,我會在後臺一一回復大家!
那麼,今天的分享就到這裡,寫完累的不要不要的,期待給大家的下次分享!!!
最新評論