京東面經匯總


目錄

一、Java

Java的優勢

平台無關性、垃圾回收

Java有哪些特性,舉個多態的例子。

封裝、繼承、多態

abstract interface區別

含有abstract修飾符的class即為抽象類,abstract類不能創建的實例對象。含有abstract方法的類必須定義為abstract class,abstract class類中的方法不必是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,所以,不能有抽象構造方法或抽象靜態方法。如果的子類沒有實現抽象父類中的所有抽象方法,那么子類也必須定義為abstract類型。

接口(interface)可以說成是抽象類的一種特例,接口中的所有方法都必須是抽象的。接口中的方法定義默認為public abstract類型,接口中的成員變量類型默認為public static final。

下面比較一下兩者的語法區別:

  1. 抽象類可以有構造方法,接口中不能有構造方法。
  2. 抽象類中可以有普通成員變量,接口中沒有普通成員變量
  3. 抽象類中可以包含非抽象的普通方法,接口中的可以有非抽象方法,比如deaflut方法
  4. 抽象類中的抽象方法的訪問類型可以是public,protected和(默認類型,雖然
    eclipse下不報錯,但應該也不行),但接口中的抽象方法只能是public類型的,並且默認即為public abstract類型。
  5. 抽象類中可以包含靜態方法,接口中不能包含靜態方法
  6. 抽象類和接口中都可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型可以任意,但接口中定義的變量只能是public static final類型,並且默認即為public static final類型。
  7. 一個類可以實現多個接口,但只能繼承一個抽象類。

有抽象方法一定是抽象類嗎?抽象類一定有抽象方法嗎?

有抽象方法不一定是抽象類,也可能是接口。抽象類不一定有抽象方法,可以有非抽象的普通方法。

Java的反射機制

在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為Java語言的反射機制。

反射的核心是JVM在運行時才動態加載類或調用方法/訪問屬性,它不需要事先知道運行對象是誰。

super()和this()能不能同時使用

不能同時使用,this和super不能同時出現在一個構造函數里面,因為this必然會調用其它的構造函數,其它的構造函數必然也會有super語句的存在,所以在同一個構造函數里面有相同的語句,就失去了語句的意義,編譯器也不會通過。

hashcode,equals,Object的這兩個方法默認返回什么?描述了一下為什么重寫equals方法必須重寫hashcode方法

默認的hashCode方法會利用對象的地址來計算hashcode值,不同對象的hashcode值是不一樣的。

public boolean equals(Object obj) {
        return (this == obj);
    }

可以看出Object類中的equals方法與“==”是等價的,也就是說判斷對象的地址是否相等。Object類中的equals方法進行的是基於內存地址的比較。

一般對於存放到Set集合或者Map中鍵值對的元素,需要按需要重寫hashCode與equals方法,以保證唯一性。

final

  1. final關鍵字可以用於成員變量、本地變量、方法以及類。
  2. final成員變量必須在聲明的時候初始化或者在構造器中初始化,否則就會報編譯錯誤。
  3. 你不能夠對final變量再次賦值。
  4. 本地變量必須在聲明時賦值。
  5. 在匿名類中所有變量都必須是final變量。
  6. final方法不能被重寫。
  7. final類不能被繼承。
  8. 接口中聲明的所有變量本身是final的。
  9. final和abstract這兩個關鍵字是反相關的,final類就不可能是abstract的。
  10. final方法在編譯階段綁定,稱為靜態綁定(static binding)。
  11. 沒有在聲明時初始化final變量的稱為空白final變量(blank final variable),它們必須在構造器中初始化,或者調用this()初始化。不這么做的話,編譯器會報錯“final變量(變量名)需要進行初始化”。
  12. 將類、方法、變量聲明為final能夠提高性能,這樣JVM就有機會進行估計,然后優化。
  13. 按照Java代碼慣例,final變量就是常量,而且通常常量名要大寫。

String,StringBuffer,StringBuilder區別

  • String內容不可變,StringBuffer和StringBuilder內容可變;
  • StringBuilder非線程安全(單線程使用),String與StringBuffer線程安全(多線程使用);
  • 如果程序不是多線程的,那么使用StringBuilder效率高於StringBuffer。

String為什么不可變

public final class String
    implements java.io.Serializable, Comparable<string>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
 
    /** Cache the hash code for the string */
    private int hash; // Default to 0</string>

String 的底層實現是依靠 char[] 數組,既然依靠的是基礎類型變量,那么他一定是可變的, String 之所以不可變,是因為 Java 的開發者通過技術實現,隔絕了使用者對 String 的底層數據的操作。

String,是否可以繼承,“+”怎樣實現

String不可以繼承,因為String被final修飾,而final修飾的類是不能被繼承的。

String為不可變的,每次String對象做累加時都會創建StringBuilder對象。

// 程序編譯期即加載完成對象s1為"ab"
String s1 = "a" + "b";  
// 這種方式,JVM會先創建一個StringBuilder,然后通過其append方法完成累加操作
String s1 = "a";
String s2 = "b"; 
String s3 = s1 + s2; // 等效於 String s3 = (new StringBuilder(s1)).append(s2).toString();

字符串常量池

map、list、set的區別

List:

  1. 可以允許重復的對象。
  2. 可以插入多個null元素。
  3. 是一個有序容器,保持了每個元素的插入順序,輸出的順序就是插入的順序。
  4. 常用的實現類有 ArrayList、LinkedList 和 Vector。ArrayList 最為流行,它提供了使用索引的隨意訪問,而 LinkedList 則對於經常需要從 List中添加或刪除元素的場合更為合適。

Set:

  1. 不允許重復對象
  2. 無序容器,你無法保證每個元素的存儲順序,TreeSet通過 Comparator 或者 Comparable 維護了一個排序順序。
  3. 只允許一個 null 元素
  4. Set 接口最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基於 HashMap 實現的 HashSet;TreeSet 還實現了 SortedSet 接口,因此 TreeSet 是一個根據其 compare() 和 compareTo() 的定義進行排序的有序容器。

Map:

  1. Map不是collection的子接口或者實現類。Map是一個接口。
  2. Map 的 每個 Entry 都持有兩個對象,也就是一個鍵一個值,Map 可能會持有相同的值對象但鍵對象必須是唯一的。
  3. TreeMap 也通過 Comparator 或者 Comparable 維護了一個排序順序。
  4. Map 里你可以擁有隨意個 null 值但最多只能有一個 null 鍵。
  5. Map 接口最流行的幾個實現類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最常用)

有沒有有序的set?

有,LinkedHashSet和TreeSet

Set如何保證不重復?

HashSet中add()中調用了HashMap的put(),將一個key-value對放入HashMap中時,首先根據key的hashCode()返回值決定該Entry的存儲位置,如果兩個key的hash值相同,那么它們的存儲位置相同。如果這個兩個key的equals比較返回true。那么新添加的Entry的value會覆蓋原來的Entry的value,key不會覆蓋。因此,如果向HashSet中添加一個已經存在的元素,新添加的集合元素不會覆蓋原來已有的集合元素。

說一說對Java io的理解

IO,其實意味着:數據不停地搬入搬出緩沖區而已(使用了緩沖區)。

nio與bio的了解以及說一下區別

BIO:同步阻塞式IO,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。

NIO:同步非阻塞式IO,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。

Java並發的理解

Java是一種多線程編程語言,我們可以使用Java來開發多線程程序。 多線程程序包含兩個或多個可同時運行的部分,每個部分可以同時處理不同的任務,從而能更好地利用可用資源,特別是當您的計算機有多個CPU時。多線程使您能夠寫入多個活動,可以在同一程序中同時進行操作處理。

死鎖,死鎖原因

兩個或者多個線程之間相互等待,導致線程都無法執行,叫做線程死鎖。

  1. 互斥條件:使用的資源是不能共享的。
  2. 不可搶占條件:線程持有一個資源並等待獲取一個被其他線程持有的資源。
  3. 請求與保持條件:線程持有一個資源並等待獲取一個被其他線程持有的資源。
  4. 循環等待條件:線程之間形成一種首尾相連的等待資源的關系。

wait和sleep的區別

  1. wait和notify方法定義在Object類中,因此會被所有的類所繼承。 這些方法都是final的,即它們都是不能被重寫的,不能通過子類覆寫去改變它們的行為。 而sleep方法是在Thread類中是由native修飾的,本地方法。

  2. 當線程調用了wait()方法時,它會釋放掉對象的鎖。
    另一個會導致線程暫停的方法:Thread.sleep(),它會導致線程睡眠指定的毫秒數,但線程在睡眠的過程中是不會釋放掉對象的鎖的。

  3. 因為wait方法會釋放鎖,所以調用該方法時,當前的線程必須擁有當前對象的monitor,也即lock,就是鎖。要確保調用wait()方法的時候擁有鎖,即wait()方法的調用必須放在synchronized方法或synchronized塊中。

ArrayList和LinkedList有什么區別?

  1. ArrayList是實現了基於動態數組的數據結構,LinkedList基於雙向鏈表的數據結構。
  2. 對於隨機訪問get和set,ArrayList優於LinkedList,因為LinkedList要移動指針。
  3. 對於新增和刪除操作add和remove,LinedList比較占優勢,因為ArrayList要移動數據。

HashMap 的原理,hashmap的擴容問題,為什么HashMap的初始容量會是16,為什么是2倍擴容,實現簡單的 get/put操作;處理哈希沖突用的哪種方法(拉鏈),還知道什么處理哈希沖突的方法(開放地址檢測),開放地址檢測怎么實現的

從哈希表中刪除一個元素,再加入元素時恰好與原來那個哈希沖突,這個元素會放在哪

HashMap、Hashtable、concurrenthashmap

HashTable為什么是線程安全的?

synchronized鎖住了

HashMap,ConcurrentHashMap以及在什么情況下性能會不好

Thread狀態有哪些

新建、就緒、運行、阻塞、死亡

多線程實現方法

  • 繼承Thread類創建線程類,重寫run方法,run方法就是代表線程需要完成的任務,調用線程對象的start()來啟動該線程,線程類已經繼承了Thread類,所以不能再繼承其他父類。
  • 實現Runnable接口創建線程類,定義Runnable實現類,重寫run方法
  • 實現Callable接口,重寫call()方法,call()作為線程的執行體,具有返回值
  • 線程池,使用線程池產生線程對象java.util.concurrent.ExecutorService、java.util.concurrent.Executors

Java如何實現線程安全

互斥同步:推薦使用 synchronized 關鍵字進行同步, 在 concurrent包中有ReentrantLock類, 實現效果差不多. 還是推薦原生態的synchronized.

非阻塞同步:需要硬件指令完成.常用的指令有:

Test-and-Set

Fetch-and-Increment

Swap

Compare-and-Swap (CAS)

Load-Linked/Store-Conditional (LL/SC)

典型的應用在 AtomicInteger 中

無同步方案:將變量保存在本地線程中,就不會出現多個線程並發的錯誤了。

java中主要使用的就是ThreadLocal這個類。

Synchronized和lock區別

  • Lock提供了synchronized關鍵字所不具備的主要特性有:
    • 嘗試非阻塞地獲取鎖boolean tryLock():當前線程嘗試獲取鎖,如果這一時刻沒有被其他線程獲取到,則成功獲取並持有鎖
    • 能被中斷地獲取鎖void lockInterruptibly():當獲取到鎖的線程被中斷時,中斷異常拋出同時會釋放鎖
    • 超時獲取鎖boolean trylock(long time, TimeUnit unit):在指定截止時間之前獲取鎖,如果在截止時間仍舊無法獲取鎖,則返回
  • synchronized是JVM提供的加鎖,悲觀鎖;lock是Java語言實現的,而且是樂觀鎖。
  • ReentrantLock是基於AQS實現的,由於AQS是基於FIFO隊列的實現

Java中都有什么鎖

重量級鎖、顯式鎖、並發容器、並發同步器、CAS、volatile、AQS等

可重入鎖的設計思路是什么

可重入公平鎖獲取流程

在獲取鎖的時候,如果當前線程之前已經獲取到了鎖,就會把state加1,在釋放鎖的時候會先減1,這樣就保證了同一個鎖可以被同一個線程獲取多次,而不會出現死鎖的情況。這就是ReentrantLock的可重入性。

對於非公平鎖而言,調用lock方法后,會先嘗試搶占鎖,在各種判斷的時候會先忽略等待隊列,如果鎖可用,就會直接搶占使用。

樂觀鎖和悲觀鎖

悲觀鎖:假定會發生並發沖突,則屏蔽一切可能違反數據完整性的操作

樂觀鎖:假定不會發生並發沖突,只在數據提交時檢查是否違反了數據完整性(不能解決臟讀問題)

juc包內有哪些類

CountDownLatch 同步計數器,主要用於線程間的控制,但計數無法被重置,如果需要重置計數,請考慮使用 CyclicBarrier 。

CAS如何實現

BlockQueue見過沒?

(線程池的排隊策略)

線程池原理

線程池的排隊策略和拒絕策略的試用條件和具體內容。

線程池的類型,詳細介紹cached和fixed

corePoolSize參數的意義

核心線程數

  • 核心線程會一直存活,即使沒有任務需要執行
  • 當線程數小於核心線程數時,即使有線程空閑,線程池也會優先創建新線程處理
  • 設置allowCoreThreadTimeout=true(默認false)時,核心線程會超時關閉

線程池新任務到達時會先使用空閑線程還是加入阻塞隊列

Java並發包里面的CountdownLatch怎么使用

這個類是一個同步計數器,主要用於線程間的控制,當CountDownLatch的count計數>0時,await()會造成阻塞,直到count變為0,await()結束阻塞,使用countDown()會讓count減1。CountDownLatch的構造函數可以設置count值,當count=1時,它的作用類似於wait()和notify()的作用。如果我想讓其他線程執行完指定程序,其他所有程序都執行結束后我再執行,這時可以用CountDownLatch,但計數無法被重置,如果需要重置計數,請考慮使用 CyclicBarrier 。

volatile和synchronized區別

  • volatile是變量修飾符,其修飾的變量具有可見性,Java的做法是將該變量的操作放在寄存器或者CPU緩存上進行,之后才會同步到主存,使用volatile修飾符的變量是直接讀寫主存,volatile不保證原子性,同時volatile禁止指令重排
  • synchronized作用於一段代碼或者方法,保證可見性,又保證原子性,可見性是synchronized或者Lock能保證通一個時刻只有一個線程獲取鎖然后執行不同代碼,並且在釋放鎖之前會對變量的修改刷新到主存中去,原子性是指要么不執行,要執行就執行到底

線程池使用時一般要考慮哪些問題

一般線程和守護線程的區別

java中的線程分為兩種:守護線程(Daemon)和用戶線程(User)。

任何線程都可以設置為守護線程和用戶線程,通過方法Thread.setDaemon(bool on);true則把該線程設置為守護線程,反之則為用戶線程。Thread.setDaemon()必須在Thread.start()之前調用,否則運行時會拋出異常。

唯一的區別是判斷虛擬機(JVM)何時離開,Daemon是為其他線程提供服務,如果全部的User Thread已經撤離,Daemon 沒有可服務的線程,JVM撤離。也可以理解為守護線程是JVM自動創建的線程(但不一定),用戶線程是程序創建的線程;比如JVM的垃圾回收線程是一個守護線程,當所有線程已經撤離,不再產生垃圾,守護線程自然就沒事可干了,當垃圾回收線程是Java虛擬機上僅剩的線程時,Java虛擬機會自動離開。

一致性Hash原理,實現負載均衡

異常

servlet流程

forward redirect 二次請求

序列化,以及json傳輸

tomcat均衡方式

netty

二、JVM

JVM內存划分

JVM內存模型

程序計數器:記錄正在執行的虛擬機字節碼指令的地址(如果正在執行的是本地方法則為空)。

Java虛擬機棧:每個 Java 方法在執行的同時會創建一個棧幀用於存儲局部變量表、操作數棧、常量池引用等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在 Java 虛擬機棧中入棧和出棧的過程。

本地方法棧:與 Java 虛擬機棧類似,它們之間的區別只不過是本地方法棧為本地方法服務。

Java堆:幾乎所有對象實例都在這里分配內存。是垃圾收集的主要區域("GC 堆"),虛擬機把 Java 堆分成以下三塊:

  • 新生代
  • 老年代
  • 永久代

新生代又可細分為Eden空間、From Survivor空間、To Survivor空間,默認比例為8:1:1。

方法區:方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域。Object Class Data(類定義數據)是存儲在方法區的,此外,常量、靜態變量、JIT編譯后的代碼也存儲在方法區。

運行時常量池:運行時常量池是方法區的一部分。Class 文件中的常量池(編譯器生成的各種字面量和符號引用)會在類加載后被放入這個區域。除了在編譯期生成的常量,還允許動態生成,例如 String 類的 intern()。這部分常量也會被放入運行時常量池。

直接內存:直接內存(Direct Memory)並不是虛擬機運行時數據區的一部分,也不是Java虛擬機規范中定義的內存區域,但是這部分內存也被頻繁地使用,而且也可能導致OutOfMemoryError 異常出現。避免在Java堆和Native堆中來回復制數據。

GC

垃圾回收算法包括:標記-清除算法,復制算法,標記-整理算法,分代收集算法。

標記—清除算法:

標記/清除算法,分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統一回收所有被標記的對象。

標記階段:標記的過程其實就是前面介紹的可達性分析算法的過程,遍歷所有的GC Roots對象,對從GC Roots對象可達的對象都打上一個標識,一般是在對象的header中,將其記錄為可達對象;

清除階段:清除的過程是對堆內存進行遍歷,如果發現某個對象沒有被標記為可達對象,則將其回收。

標記-清除

復制算法:

將內存划分為大小相等的兩塊,每次只使用其中一塊,當這一塊內存用完了就將還存活的對象復制到另一塊上面,然后再把使用過的內存空間進行一次清理。

將內存分為一塊較大的 Eden 空間和兩塊較小的 Survior 空間,每次使用 Eden 空間和其中一塊 Survivor。在回收時,將 Eden 和 Survivor 中還存活着的對象一次性復制到另一塊 Survivor 空間上,最后清理 Eden 和 使用過的那一塊 Survivor。HotSpot 虛擬機的 Eden 和 Survivor 的大小比例默認為 8:1,保證了內存的利用率達到 90 %。如果每次回收有多於 10% 的對象存活,那么一塊 Survivor 空間就不夠用了,此時需要依賴於老年代進行分配擔保,也就是借用老年代的空間。

復制

標記—整理算法:

標記—整理算法和標記—清除算法一樣,但是標記—整理算法不是把存活對象復制到另一塊內存,而是把存活對象往內存的一端移動,然后直接回收邊界以外的內存,因此其不會產生內存碎片。標記—整理算法提高了內存的利用率,並且它適合在收集對象存活時間較長的老年代。

標記—整理

分代收集算法:

分代回收算法實際上是把復制算法和標記整理法的結合,並不是真正一個新的算法,一般分為:老年代和新生代,老年代就是很少垃圾需要進行回收的,新生代就是有很多的內存空間需要回收,所以不同代就采用不同的回收算法,以此來達到高效的回收算法。

新生代:由於新生代產生很多臨時對象,大量對象需要進行回收,所以采用復制算法是最高效的。

老年代:回收的對象很少,都是經過幾次標記后都不是可回收的狀態轉移到老年代的,所以僅有少量對象需要回收,故采用標記清除或者標記整理算法

垃圾回收器

Java對象頭

HotSpot虛擬機中,對象在內存中的布局分為三塊區域:對象頭、實例數據和對齊填充。

對象頭包括兩部分:Mark Word 和 類型指針。

  • Mark Word:Mark Word用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程ID、偏向時間戳等等,占用內存大小與虛擬機位長一致。

  • 類型指針:類型指針指向對象的類元數據,虛擬機通過這個指針確定該對象是哪個類的實例。

內存泄漏

類加載過程

類加載的過程主要分為三個部分:

  • 加載:指的是把class字節碼文件從各個來源通過類加載器裝載入內存中。
  • 鏈接
  • 初始化:對類變量初始化,是執行類構造器的過程。

鏈接又可以細分為

  • 驗證:為了保證加載進來的字節流符合虛擬機規范,不會造成安全錯誤。
  • 准備:為類變量(注意,不是實例變量)分配內存,並且賦予初值。
  • 解析:將常量池內的符號引用替換為直接引用的過程。

雙親委派模型,為什么要使用雙親委派模型

Java虛擬機的一些參數配置

為什么jvm調優經常會將-Xms和-Xmx參數設置成一樣

三、數據結構與算法

常見的排序算法時間復雜度

快排算法 寫代碼

    /**
     * 快速排序
     *
     * @param array
     * @param _left
     * @param _right
     */
    private static void quickSort(int[] array, int _left, int _right) {
        int left = _left;//
        int right = _right;
        int pivot;//基准線
        if (left < right) {
            pivot = array[left];
            while (left != right) {
                //從右往左找到比基准線小的數
                while (left < right && pivot <= array[right]) {
                    right--;
                }
                //將右邊比基准線小的數換到左邊
                array[left] = array[right];
                //從左往右找到比基准線大的數
                while (left < right && pivot >= array[left]) {
                    left++;
                }
                //將左邊比基准線大的數換到右邊
                array[right] = array[left];
            }
            //此時left和right指向同一位置
            array[left] = pivot;
            quickSort(array, _left, left - 1);
            quickSort(array, left + 1, _right);
        }
    }

堆排序怎么實現

public class HeapSort {
    /**
     * 構建大頂堆
     */
    public static void adjustHeap(int[] a, int i, int len) {
        int temp, j;
        temp = a[i];
        for (j = 2 * i; j < len; j *= 2) {// 沿關鍵字較大的孩子結點向下篩選
            if (j < len && a[j] < a[j + 1])
                ++j; // j為關鍵字中較大記錄的下標
            if (temp >= a[j])
                break;
            a[i] = a[j];
            i = j;
        }
        a[i] = temp;
    }

    public static void heapSort(int[] a) {
        int i;
        for (i = a.length / 2 - 1; i >= 0; i--) {// 構建一個大頂堆
            adjustHeap(a, i, a.length - 1);
        }
        for (i = a.length - 1; i >= 0; i--) {// 將堆頂記錄和當前未經排序子序列的最后一個記錄交換
            int temp = a[0];
            a[0] = a[i];
            a[i] = temp;
            adjustHeap(a, 0, i - 1);// 將a中前i-1個記錄重新調整為大頂堆
        }
    }
} 

鏈表,數組的優缺點,應用場景,查找元素的復雜度

入棧出棧的時間復雜度,鏈表插入和刪除的時間復雜度

如何用LinkedList實現堆棧操作

Arraylist如何實現排序

利用數組,實現一個循環隊列類

兩個有序數組,有相同的元素,找出來

二叉樹怎么實現的

二叉樹前中后序遍歷 深度 廣度

二叉樹深度

遞歸

    public int TreeDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return Math.max(TreeDepth(root.left) + 1, TreeDepth(root.right) + 1);
    }

非遞歸,層次遍歷

    public int TreeDepth_2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int start = 0;
        int end = 1;
        int depth = 0;
        while (!queue.isEmpty()) {
            TreeNode temp = queue.poll();
            start++;
            if (temp.left != null) {
                queue.offer(temp.left);
            }
            if (temp.right != null) {
                queue.offer(temp.right);
            }
            if (start == end) {
                start = 0;
                end = queue.size();
                depth++;
            }
        }
        return depth;
    }

層序遍歷二叉樹

  • 思路:
  • 訪問根節點,並將根節點入隊。
  • 當隊列不空的時候,重復以下操作。
  • 1、彈出一個元素。作為當前的根節點。
  • 2、如果根節點有左孩子,訪問左孩子,並將左孩子入隊。
  • 3、如果根節點有右孩子,訪問右孩子,並將右孩子入隊。
    public void levelOrder(TreeNode root) {
        //使用隊列,先進先出
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            TreeNode temp = queue.poll();
            System.out.print(temp.val + "  ");
            if (temp.left != null) {
                queue.offer(temp.left);
            }
            if (temp.right != null) {
                queue.offer(temp.right);
            }
        }
    }

樹的中序遍歷,除了遞歸和棧還有什么實現方式

二叉搜索樹轉換成一個排好序的雙向鏈表

判斷平衡二叉樹

從下往上遍歷,如果子樹是平衡二叉樹,則返回子樹高度,否則返回-1

    public boolean IsBalanced_Solution(TreeNode root) {
        return MaxDepth(root) != -1;
    }
    
    public int MaxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = MaxDepth(root.left);
        if (leftHeight == -1) {
            return -1;
        }
        int rightHeight = MaxDepth(root.right);
        if (rightHeight == -1) {
            return -1;
        }
        return Math.abs(leftHeight - rightHeight) > 1 ? -1 : 1 + Math.max(leftHeight, rightHeight);
    }

給定一個2叉樹,打印每一層最右邊的結點

一棵普通樹(非二叉搜索樹),找出一條路徑和最大

一棵樹,求所有路徑之和

最長公共子序列

反轉鏈表

將當前節點和下一節點保存起來,然后將當前節點反轉。

    public ListNode ReverseList(ListNode head) {
        //head為當前節點,如果當前節點為空的話,那就什么也不做,直接返回null
        ListNode pre = null;//pre為當前節點的前一節點
        ListNode next = null;//next為當前節點的下一節點
        //需要pre和next的目的是讓當前節點從pre.head.next1.next2變成pre<-head next1.next2
        //即pre讓節點可以反轉所指方向,但反轉之后如果不用next節點保存next1節點的話,此單鏈表就此斷開了
        //所以需要用到pre和next兩個節點
        //1.2.3.4.5
        //1<-2<-3 4.5
        //做循環,如果當前節點不為空的話,始終執行此循環,此循環的目的就是讓當前節點從指向next到指向pre
        while (head != null) {
            //先用next保存head的下一個節點的信息,保證單鏈表不會因為失去head節點的原next節點而就此斷裂
            next = head.next;
            //保存完next,就可以讓head從指向next變成指向pre了
            head.next = pre;
            //head指向pre后,就繼續依次反轉下一個節點
            //讓pre,head,next依次向后移動一個節點,繼續下一次的指針反轉
            pre = head;
            head = next;
        }
        //如果head為null的時候,pre就為最后一個節點了,但是鏈表已經反轉完畢,pre就是反轉后鏈表的第一個節點
        //直接輸出pre就是我們想要得到的反轉后的鏈表
        return pre;
    }

利用遞歸走到鏈表的末端,然后再更新每一個節點的next值 ,實現鏈表的反轉。

    public ListNode ReverseList(ListNode head) {
        //如果鏈表為空或者鏈表中只有一個元素 
        if (head == null || head.next == null) return head;
        //先遞歸找到到鏈表的末端結點,從后依次反轉整個鏈表
        ListNode reverseHead = ReverseList(head.next);
        //再將當前節點設置為后面節點的后續節點 
        head.next.next = head;
        head.next = null;
        return reverseHead;
    }

判斷一個數是不是丑數

找出一個字符串中字符連續相同的最長子串,如aabbccc,結果就是ccc

蓄水池抽樣算法

尋找一個字符串中第一個只出現一次的字符

用LinkedHashMap記錄字符出現的次數

	public Character firstNotRepeating(String str){
		if(str == null)
			return null;
		char[] strChar = str.toCharArray();
		LinkedHashMap<Character,Integer> hash = new LinkedHashMap<Character,Integer>();
		for(char item:strChar){
			if(hash.containsKey(item))
				hash.put(item, hash.get(item)+1);
			else
				hash.put(item, 1);
		}
		for(char key:hash.keySet())
		{
			if(hash.get(key)== 1)
				return key;
		}
		return null;
	}

給定一個數組,里面只有一個數出現了一次,其他都出現了兩次。怎么得到這個出現了一次的數?

利用HashSet的元素不能重復,如果有重復的元素,則刪除重復元素,如果沒有則添加,最后剩下的就是只出現一次的元素

    public void FindNumsAppearOnce(int[] array, int num[]) {
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < array.length; i++) {
            if (!set.add(array[i])) {
                set.remove(array[i]);
            }
        }
        Iterator<Integer> iterator = set.iterator();
        num[0] = iterator.next();
    }

用HashMap<K,V>保存數組的值,key為數組值,value為布爾型表示是否有重復

    public void FindNumsAppearOnce_2(int[] array, int num[]) {
        HashMap<Integer, Boolean> map = new HashMap<>();
        for (int i = 0; i < array.length; i++) {
            if (!map.containsKey(array[i])) {
                map.put(array[i], true);
            } else {
                map.put(array[i], false);
            }
        }
        for (int i = 0; i < array.length; i++) {
            if (map.get(array[i])) {
                num[0] = array[i];
            }
        }
    }

給定一個數組,如果有兩個不同數的出現了一次,其他出現了兩次,怎么得到這兩個數?

利用HashSet的元素不能重復,如果有重復的元素,則刪除重復元素,如果沒有則添加,最后剩下的就是只出現一次的元素

    public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < array.length; i++) {
            if (!set.add(array[i])) {
                set.remove(array[i]);
            }
        }
        Iterator<Integer> iterator = set.iterator();
        num1[0] = iterator.next();
        num2[0] = iterator.next();
    }

用HashMap<K,V>保存數組的值,key為數組值,value為布爾型表示是否有重復

    public void FindNumsAppearOnce_2(int[] array, int num1[], int num2[]) {
        HashMap<Integer, Boolean> map = new HashMap<>();
        for (int i = 0; i < array.length; i++) {
            if (!map.containsKey(array[i])) {
                map.put(array[i], true);
            } else {
                map.put(array[i], false);
            }
        }
        int index = 0;//區分是第幾個不重復的值
        for (int i = 0; i < array.length; i++) {
            if (map.get(array[i])) {
                index++;
                if (index == 1) {
                    num1[0] = array[i];
                } else {
                    num2[0] = array[i];
                }
            }
        }
    }

位運算 異或,兩個不相等的元素在位級表示上必定會有一位存在不同。

    public void FindNumsAppearOnce_3(int[] array, int num1[], int num2[]) {
        int diff = 0;
        for (int num : array) diff ^= num;
        // 得到最右一位
        diff &= -diff;
        for (int num : array) {
            if ((num & diff) == 0) num1[0] ^= num;
            else num2[0] ^= num;
        }
    }

海量數據topk問題

四、操作系統

進程和線程區別

進程:進程是操作系統資源分配的基本單位。每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1–n個線程。

線程:線程是CPU獨立調度的基本單位。同一類線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。

線程和進程的生命周期:新建、就緒、運行、阻塞、死亡

不同進程打開了同一個文件,那么這兩個進程得到的文件描述符(fd)相同嗎?

不同進程打開同一個文件,文件描述符可能相同可能不同。

操作系統如何實現輸出

進程通信

  • 消息傳遞
    • 管道
    • 消息隊列
    • 套接字
  • 共享內存

五、網絡

OSI七層網絡模型中,你對哪層最了解?了解哪些協議?做過web開發?

image

OSI七層網絡模型 對應網絡協議
應用層 HTTP、TFTP、FTP、NFS、WAIS、SMTP
表示層 Telnet、Rlogin、SNMP、Gopher
會話層 SMTP、DNS
傳輸層 TCP、UDP
網絡層 IP、ICMP、ARP、RARP、AKP、UUCP
數據鏈路層 FDDI、Ethernet、Arpanet、PDN、SLIP、PPP
物理層 IEEE 802.1A、IEEE 802.2到IEEE 802.11

HTTP 0.9/1.0/1.1/2

HTTP/0.9只支持客戶端發送Get請求,且不支持請求頭。HTTP具有典型的無狀態性。

HTTP/1.0在HTTP/0.9的基礎上支持客戶端發送POST、HEAD。HTTP 1.0需要使用keep-alive參數來告知服務器端要建立一個長連接,但默認是短連接。

HTTP 和 HTTPS 有什么區別?

HTTP(Hypertext Transfer Protocol)超文本傳輸協議是用來在Internet上傳送超文本的傳送協議,它可以使瀏覽器更加高效,使網絡傳輸減少。但HTTP協議采用明文傳輸信息,存在信息竊聽、信息篡改和信息劫持的風險。

HTTPS(Secure Hypertext Transfer Protocol) 安全超文本傳輸協議是一個安全的通信通道,它基於HTTP開發,用於在客戶計算機和服務器之間交換信息。HTTPS使用安全套接字層(SSL)進行信息交換,簡單來說HTTPS是HTTP的安全版,是使用TLS/SSL加密的HTTP協議。

HTTPS和HTTP的區別主要如下:

  1. https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。
  2. http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
  3. http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
  4. http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全.

知道 HTTPS 通信過程嗎?

  • 客戶端發送請求到服務器端
  • 服務器端返回證書和公開密鑰,公開密鑰作為證書的一部分而存在
  • 客戶端驗證證書和公開密鑰的有效性,如果有效,則生成共享密鑰並使用公開密鑰加密發送到服務器端
  • 服務器端使用私有密鑰解密數據,並使用收到的共享密鑰加密數據,發送到客戶端
  • 客戶端使用共享密鑰解密數據
  • SSL加密建立

TCP三次握手

所謂三次握手(Three-Way Handshake)即建立TCP連接,就是指建立一個TCP連接時,需要客戶端和服務端總共發送3個包以確認連接的建立。整個流程如下圖所示:

TCP三次握手

  1. 第一次握手:Client將標志位SYN置為1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
  2. 第二次握手:Server收到數據包后由標志位SYN=1知道Client請求建立連接,Server將標志位SYN和ACK都置為1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。
  3. 第三次握手:Client收到確認后,檢查ack是否為J+1,ACK是否為1,如果正確則將標志位ACK置為1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨后Client與Server之間可以開始傳輸數據了。

為什么三次握手和四次揮手

Server在LISTEN狀態下,收到建立連接請求的SYN報文后,可以直接把ACK和SYN放在一個報文里發送給Client。而關閉連接時,當收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,己方也未必全部數據都發送給對方了,所以己方可以立即close,也可以發送一些數據給對方后,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送。

TCP與HTTP有什么關系

http是要基於TCP連接基礎上的,簡單的說,TCP就是單純建立連接,不涉及任何我們需要請求的實際數據,簡單的傳輸。http是用來收發數據,即實際應用上的。

Tcp連接4次揮手的原因。Time_wait等待超時了會怎樣?

Server在LISTEN狀態下,收到建立連接請求的SYN報文后,可以直接把ACK和SYN放在一個報文里發送給Client。而關閉連接時,當收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,己方也未必全部數據都發送給對方了,所以己方可以立即close,也可以發送一些數據給對方后,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送。

SSL 握手

  • 客戶端發送隨機數1,支持的加密方法(如RSA公鑰加密)
  • 服務端發送隨機數2,和服務器公鑰,並確認加密方法
  • 客戶端發送用服務器公鑰加密的隨機數3
  • 服務器用私鑰解密這個隨機數3,用加密方法計算生成對稱加密的密鑰給客戶端,
  • 接下來的報文都用雙方協定好的加密方法和密鑰,進行加密

session/cookie

常用的會話跟蹤技術是Cookie與Session。Cookie通過在客戶端記錄信息確定用戶身份,Session通過在服務器端記錄信息確定用戶身份。

聯系:

  • Cookie與Session都是用來跟蹤瀏覽器用戶身份的會話方式。

區別:

  • Cookie數據存放在客戶的瀏覽器上,Session數據放在服務器上。
  • Cookie不是很安全,別人可以分析存放在本地的Cookie並進行Cookie欺騙,如果主要考慮到安全應當使用加密的Cookie或者Session。
  • Session會在一定時間內保存在服務器上。當訪問增多,會比較占用你服務器的性能,如果主要考慮到減輕服務器性能方面,應當使用Cookie。
  • 單個Cookie在客戶端的限制是4K,很多瀏覽器都限制一個站點最多保存20個Cookie。

當你在瀏覽器地址欄輸入一個URL后回車,將會發生的事情?

域名解析 --> 發起TCP的3次握手 --> 建立TCP連接后發起http請求 --> 服務器響應http請求,瀏覽器得到html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶

DNS域名解析過程

瀏覽器緩存 --> 系統緩存 --> 路由器緩存 --> ISP(互聯網服務提供商)DNS緩存 --> 根域名服務器 --> 頂級域名服務器 --> 主域名服務器 --> 保存結果至緩存

ping工作原理

Ping程序的實質是利用了ICMP請求回顯和回顯應答報文,但ARP請求和應答報文也在其中起了非常重要的作用。

Get和Post請求

GET 請求:

  • GET 請求可被緩存
  • GET 請求保留在瀏覽器歷史記錄中
  • GET 請求可被收藏為書簽
  • GET 請求不應在處理敏感數據時使用
  • GET 請求有長度限制
  • GET 請求只應當用於取回數據

POST 請求 :

  • POST 請求不會被緩存
  • POST 請求不會保留在瀏覽器歷史記錄中
  • POST 不能被收藏為書簽
  • POST 請求對數據長度沒有要求

HTTP狀態碼

  • 1XX 信息,服務器收到請求,需要請求者繼續執行操作
  • 2XX 成功,操作被成功接收並處理
  • 3XX 重定向,需要進一步的操作以完成請求
  • 4XX 客戶端錯誤,請求包含語法錯誤或無法完成請求
  • 5XX 服務器錯誤,服務器在處理請求的過程中發生了錯誤

六、數據庫

數據庫事務的四個隔離級別,MySql在哪一個級別

  • 未提交讀(READ UNCOMMITTED):事務中的修改,即使沒有提交,對其它事務也是可見的。最低級別,任何情況都無法保證。
  • 提交讀(READ COMMITTED):一個事務只能讀取已經提交的事務所做的修改。換句話說,一個事務所做的修改在提交之前對其它事務是不可見的。可避免臟讀的發生。
  • 可重復讀(REPEATABLE READ):保證在同一個事務中多次讀取同樣數據的結果是一樣的。可避免臟讀、不可重復讀的發生。
  • 可串行化(SERIALIXABLE):強制事務串行執行。可避免臟讀、不可重復讀、幻讀的發生。

在MySQL數據庫中,支持上面四種隔離級別,默認的為REPEATABLE READ(可重復讀)。

數據庫死鎖/如何防止

mysql索引,索引機制,聚集索引和非聚集索引,如何創建索引,實現原理,建立准則,優缺點,注意事項,

索引在什么情況下失效

說一下對B+樹的了解

innodb建立的索引,如果字段重復率很高索引,索引是什么樣,查找效率如何

innodb在插入的時候,是否會給行上鎖

說一下innodb的默認隔離級別

數據庫設計(訂單、購物車和商品)

sql中join的幾種操作的區別

left join / inner join / right join

union和union all的區別,誰的效率更高

用distinct和用group by去重,誰的效率更高

sql中的優化,怎么提高查詢效率

緩存的穿透和雪崩,解決辦法

redis的排序算法

redis集群

redis過期策略

惰性刪除+定期刪除

惰性刪除

  • 在進行get或set等操作時,先檢查key是否過期,
  • 若過期,刪除key,然后執行相應操作;
  • 若沒過期,直接執行相應操作

定期刪除

  • 遍歷每個數據庫(就是redis.conf中配置的"database"數量,默認為16)
  • 檢查當前庫中的指定個數個key(默認是每個庫檢查20個key,注意相當於該循環執行20次,循環體時下邊的描述)
  • 如果當前庫中沒有一個key設置了過期時間,直接執行下一個庫的遍歷
  • 隨機獲取一個設置了過期時間的key,檢查該key是否過期,如果過期,刪除key
  • 判斷定期刪除操作是否已經達到指定時長,若已經達到,直接退出定期刪除。

Redis如何解決key沖突

redis數據類型+redis是單線程的么,為什么呢

redis和memcache區別

redis與mysql的區別以及優缺點

回答存儲機制以及持久化

七、設計模式

單例模式里面的雙重檢查鎖定的原理,以及為什么使用volatile

確保一個類最多只有一個實例,並提供一個全局訪問點。

public class Singleton {
    private volatile static Singleton instance = null;

    private Singleton() {

    }

    /**
     * 當第一次調用getInstance()方法時,instance為空,同步操作,保證多線程實例唯一
     * 當第一次后調用getInstance()方法時,instance不為空,不進入同步代碼塊,減少了不必要的同步
     */
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

生產者消費者

工廠,說下原理和應用

策略模式

適配器模式

裝飾模式

代理模式

線程池使用了什么設計模式

單例模式

JDK中哪些體現了命令模式

八、框架

介紹下SpringBoot

SpringBoot就是對各種框架的整合,讓框架集成在一起更加簡單,簡化了開發過程、配置過程、部署過程、監控過程。

Spring IOC AOP

IOC:控制反轉也叫依賴注入,IOC利用java反射機制。所謂控制反轉是指,本來被調用者的實例是有調用者來創建的,這樣的缺點是耦合性太強,IOC則是統一交給spring來管理創建,將對象交給容器管理,你只需要在spring配置文件總配置相應的bean,以及設置相關的屬性,讓spring容器來生成類的實例對象以及管理對象。在spring容器啟動的時候,spring會把你在配置文件中配置的bean都初始化好,然后在你需要調用的時候,就把它已經初始化好的那些bean分配給你需要調用這些bean的類。

AOP是對OOP的補充和完善。AOP利用的是代理,分為CGLIB動態代理和JDK動態代理。OOP引入封裝、繼承和多態性等概念來建立一種對象層次結構。OOP編程中,會有大量的重復代碼。而AOP則是將這些與業務無關的重復代碼抽取出來,然后再嵌入到業務代碼當中。實現AOP的技術,主要分為兩大類:一是采用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行為的執行;二是采用靜態織入的方式,引入特定的語法創建“方面”,從而使得編譯器可以在編譯期間織入有關“方面”的代碼,屬於靜態代理。

Spring IOC有哪些好處

降低了組件之間的耦合性 ,實現了軟件各層之間的解耦

IOC涉及到的設計模式

工廠模式

AOP的應用場景,具體介紹,配置文件中需要寫什么?具體注解需要寫啥?

權限管理、日志、事務管理等。

切面通過帶有@Aspect注解的類實現。

Spring中定義了四個advice:BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。

Before Advice:在方法執行前執行。

AfterAdvice:在方法執行之后調用的通知,無論方法執行是否成功。

After ReturningAdvice:在方法執行后返回一個結果后執行。

After ThrowingAdvice:在方法執行過程中拋出異常的時候執行。

說說靜態代理和動態代理

代理分為靜態代理和動態代理,靜態代理是在編譯時就將接口、實現類、代理類全部手動完成,但如果我們需要很多的代理,每一個都這么手動的去創建實屬浪費時間,而且會有大量的重復代碼。動態代理可以在程序運行期間根據需要動態的創建代理類及其實例,來完成具體的功能。

Spring事務傳播,隔離級別

  • Spring事務管理高層抽象主要包括3個接口:
    • PlatformTransactionManager(事務管理器)
    • TransactionDefinition(事務定義信息,包含隔離級別、事務傳播行為、超時、只讀)
    • TransactionStatus(事務具體運行狀態)
  • Spring事務的本質其實就是數據庫對事務的支持
  • 獲取連接->開啟事務 -> 執行CRUD -> 提交事務/回滾事務 -> 關閉連接

Spring bean初始化過程

Spring如何生成一個Bean?配置文件寫完了之后又怎么生成?

Mybatis 傳參

  • map
  • @Param注解
  • JavaBean

Mybatis中 # 和 $ 區別

  • 相當於對數據加上雙引號, $ 相當於直接顯示數據

  • 方式能夠很大程度防止sql注入

Mybatis緩存

SpringMVC的運行流程

  1. 客戶端發送HTTP請求到服務器
  2. SpringMVC的核心DispatcherServlet將請求交給HandlerMapping處理
  3. HandlerMapping通過查詢機制找到處理當前請求的Handler
  4. DispatcherServlet將請求交給這個Handler處理
  5. Handler處理完成后返回一個ModleAndView對象,這個對象包含視圖邏輯名和數據對象
  6. 返回的視圖邏輯名會通過視圖解析器解析成真正的視圖,並交給DispatcherServlet處理
  7. DispatcherServlet將請求分派給真正的視圖對象,並反映到客戶端

說幾個SpringMVC的幾個注解,都是干啥的?

@Controller:用於標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。

@RequestMapping:是一個用來處理請求地址映射的注解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。

@Resource和@Autowired:@Resource和@Autowired都是做bean的注入時使用,其實@Resource並不是Spring的注解,它的包是javax.annotation.Resource,需要導入,但是Spring支持該注解的注入。

@ResponseBody:返回的數據不是html標簽的頁面,而是其他某種格式的數據時(如json、xml等)使用。

@Repository:DAO層

@Service:服務層

@autireware和@resource的區別

@Autowired注解是按類型裝配依賴對象,默認情況下它要求依賴對象必須存在,如果允許null值,可以設置它required屬性為false。

@Resource注解和@Autowired一樣,也可以標注在字段或屬性的setter方法上,但它默認按名稱裝配。名稱可以通過@Resource的name屬性指定,如果沒有指定name屬性,當注解標注在字段上,即默認取字段的名稱作為bean名稱尋找依賴對象,當注解標注在屬性的setter方法上,即默認取屬性名作為bean名稱尋找依賴對象。

@Resources按名稱,是JDK的,@Autowired按類型,是Spring的。

@PathVariable是干啥的?

@PathVariable是用來對指定請求的URL路徑里面的變量。

說說filter、servlet、listener。

Listener我是這樣理解他的,他是一種觀察者模式的實現。

Filter的使用戶可以改變一 個request或修改一個response。 Filter 不是一個servlet,它不能產生一個response,但是他能夠在一個request到達servlet之前預先處理request,也可以在一個響應離開 servlet時處理response。

消息隊列了解嗎?

通俗的說,就是一個容器,把消息丟進去,不需要立即處理。然后有個程序去從容器里面把消息一條條讀出來處理。

九、分布式

Raft協議的leader選舉,正常情況下,網絡抖動造成follower發起leader選舉,且該follower的Term比現有leader高。集群中所有結點的日志信息當前一致,這種情況下會選舉成功嗎?

分布式框架知道哪些?

dubbo

dubbo怎么用的,有沒有參與部署

分布式緩存的理解

十、Linux

linux查詢Java進程

ps -ef | grep java

linux查看內存占用情況

  • top命令提供了實時的運行中的程序的資源使用統計。你可以根據內存的使用和大小來進行排序。
  • vmstat命令顯示實時的和平均的統計,覆蓋CPU、內存、I/O等內容。例如內存情況,不僅顯示物理內存,也統計虛擬內存。

十一、雜項

設計一個秒殺系統,如何保證不超賣,還要保證服務可用

如何設計一個定時器定時完成某個任務?

如何保證集群環境下搶購的並發安全?

Java中你擅長的地方

多線程,JVM

如果學習一門技術,你會怎么學習

書籍+博客+視頻

對國內互聯網公司目前的開源生態有沒有什么了解

舉出三個以上的國內開源框架,越多越好,dubbo、fastjson、sharding-jdbc、Elastic-job...

你對京東的看法

電商,突出質量

說出三個京東不如淘寶或者天貓的地方

淘寶是C2C,京東和天貓是B2C,淘寶門檻低,種類,國際市場布局

看過啥書。

深入理解Java虛擬機&HEAD FIRST設計模式&高性能MYSQL&Java並發編程實戰,看博客比較多,感覺博客更有針對性


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM