面試中遇到的題目,記錄復習(持續更新)
Java基礎
1.String的最大長度
https://www.cnblogs.com/wupeixuan/p/12187756.html
2.集合
Collection接口的實現:
List接口:ArraryList、LinkedList、Vector
Set接口:HashSet、TreeSet
Qeque接口:ArrayQeque
Map接口的實現:
AbstractMap接口:HashMap、TreeMap
Dictionary接口:HashTable
集合關系圖
https://img-blog.csdn.net/20160124221843905
3.集合擴容
https://www.jianshu.com/p/5a1ab09ebff1
ArrayList :
默認初始容量為10
線程不安全,查詢速度快
底層數據結構是數組結構
擴容增量:原容量的 1.5倍
如 ArrayList的容量為10,一次擴容后是容量為15
擴容:把原來的數組復制到另一個內存空間更大的數組中
添加元素:把新元素添加到擴容以后的數組中
Vector :
默認初始容量為10
指定擴容增量或2倍(取最大值)
HashMap :
capacity 即容量,默認16。
loadFactor 加載因子,默認是0.75
threshold 閾值。閾值=容量*加載因子。默認12。當元素數量超過閾值時便會觸發擴容。
4.紅黑樹
性質1. 節點是紅色或黑色。
性質2. 根節點是黑色。
性質3 每個葉節點是黑色的。
性質4 每個紅色節點的兩個子節點都是黑色。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點)
性質5. 從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。
紅黑樹相比于BST和AVL樹有什么優點?
紅黑樹是犧牲了嚴格的高度平衡的優越條件為代價,它只要求部分地達到平衡要求,降低了對旋轉的要求,從而提高了性能。紅黑樹能夠以O(log2 n)的時間復雜度進行搜索、插入、刪除操作。此外,由于它的設計,任何不平衡都會在三次旋轉之內解決。當然,還有一些更好的,但實現起來更復雜的數據結構能夠做到一步旋轉之內達到平衡,但紅黑樹能夠給我們一個比較“便宜”的解決方案。
相比于BST,因為紅黑樹可以能確保樹的最長路徑不大于兩倍的最短路徑的長度,所以可以看出它的查找效果是有最低保證的。在最壞的情況下也可以保證O(logN)的,這是要好于二叉查找樹的。因為二叉查找樹最壞情況可以讓查找達到O(N)。
紅黑樹的算法時間復雜度和AVL相同,但統計性能比AVL樹更高,所以在插入和刪除中所做的后期維護操作肯定會比紅黑樹要耗時好多,但是他們的查找效率都是O(logN),所以紅黑樹應用還是高于AVL樹的. 實際上插入 AVL 樹和紅黑樹的速度取決于你所插入的數據.如果你的數據分布較好,則比較宜于采用 AVL樹(例如隨機產生系列數),但是如果你想處理比較雜亂的情況,則紅黑樹是比較快的
5.線程和進程
1、什么是進程?
進程是程序的而一次動態執行過程。
2、什么是線程?
個進程內部的控制序列, 是進程的一個實體,是進程的一條執行路徑。程也就是一個輕量級進程(僅僅是在linux系統中。在windows系統中,進程就是經常進程,線程就是線程),每個線程都有自己的線程控制塊,即一個進程至少有一個輕量級進程。
在線程組里面,所有的線程都是對等的關系,沒有父線程的概念。
3、線程資源?
進程可以擁有資源,并且是系統擁有資源的基本單位 。線程本身并不擁有系統資源,僅有一些能保證獨立運行的資源,這塊資源的各個線程私有的。
4、線程的優點
- 創建一個線程的代價比創建一個進程小的多
- 線程之間的切換相比較進程切換需要操作系統做的工作少
- 線程占用的資源比進程少
- 可以充分利用多處理器的并行數量
5、線程的缺點
- 編程難度大(編寫和調試一個多線程程序比單線程困難的多)
- 線程之間缺乏保護(時間上的分配或是共享了不該共享的數據造成很大影響)
- 性能損耗(當計算密集型的線程的數量比可用的處理器多,那么就有可能有很大的性能損失,這里的性能損失是指增加了額外的同步和調度開銷,二可用資源不變)
5.串行、并行和并發
串行:喂?你在做什么呢?買菜啊?好的,到家了說一聲。啊?到家了?那你到幼兒園接娃吧。
串行的特點:前一個任務沒搞定,下一個任務就只能等著。
并行:來,這是你的蓋澆飯,這是我的胡辣湯。咱倆一起吃。
并行的特點:兩個任務在同一時刻互不干擾的同時執行。
并發:你去買個菜,順路把郵件發了;路過幼兒園時帶娃回家。
并發的特點:同時安排若干個任務,這些任務可以彼此穿插著進行;有些任務可能是并行的,比如買菜、發郵件和去幼兒園的某些路途是重疊的,這時你的確同時在做三件事;但進菜市場和發郵件和接娃三者是互斥的,每個時刻只能完成其中一件。換句話說,并發允許兩個任務彼此干擾。
6.用戶態和內核態
https://blog.csdn.net/justlpf/article/details/99447462
7.JVM

我們都知道 Java 源文件,通過編譯器,能夠生產相應的.Class 文件,也就是字節碼文件, 而字節碼文件又通過Java虛擬機中的解釋器,編譯成特定機器上的機器碼 。
也就是如下:
① Java源文件—->編譯器—->字節碼文件
② 字節碼文件—->JVM—->機器碼
1、 類加載器子系統(Class Loader Subsystem)
類加載器

啟動類加載器(Bootstrap ClassLoader) :負責加載 JAVA_HOME\lib 目錄中的,或通過-Xbootclasspath參數指定路徑中的,且被虛擬機認可(按文件名識別,如rt.jar)的類。
擴展類加載器(Extension ClassLoader) :負責加載 JAVA_HOME\lib\ext 目錄中的,或通過java.ext.dirs系統變量指定路徑中的類 庫。
應用程序類加載器(Application ClassLoader): 負責加載用戶路徑(classpath)上的類庫。
雙親委派機制:當一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父 類去完成,每一個層次類加載器都是如此,因此所有的加載請求都應該傳送到啟動類加載其中, 只有當父類加載器反饋自己無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的 Class),子類加載器才會嘗試自己去加載。
采用雙親委派的一個好處是比如加載位于 rt.jar 包中的類 java.lang.Object,不管是哪個加載 器加載這個類,最終都是委托給頂層的啟動類加載器進行加載,這樣就保證了使用不同的類加載 器最終得到的都是同樣一個Object對象。
JVM類加載機制分為五個部分:加載、連接(包括:驗證、準備、解析)、初始化、使用、卸載

1.1、加載
這個階段會在內存中生成代表這個類的java.lang.Class對象,作為方法區的這個類的各個數據數據入口
1.2、 驗證
確保.Class文件的字節流中的信息符合JVM的要求
1.3、準備
在方法區中分配這個變量所使用的內存空間
// 實際上變量a在準備階段后初始化為0,將a賦值80的put static指令時程序被編譯后,存放在類構造器<client>方法中
public static int a = 80;
// 在編譯階段會為a生成ConstantValue屬性,在準備階段虛擬機會根據ConstantValue屬性將a賦值為80
public static final int a = 80;
1.4、解析
解析階段是指JVM將常量池中的符號引用替換為直接引用的過程
符號引用:符號引用與虛擬機實現的布局無關,引用的目標并不一定要已經加載到內存中。各種虛擬 機實現的內存布局可以各不相同,但是它們能接受的符號引用必須是一致的,因為符號引用的字面量形式明確定義在Java虛擬機規范的Class文件格式中
- CONSTANT_Class_info
- CONSTANT_Field_info
- CONSTANT_Method_info
直接引用:直接引用可以是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄。如果有 了直接引用,那引用的目標必定已經在內存中存在。
1.5、初始化
初始化是類加載最后一個階段 ,前面的類加載階段之后,除了在加載階段可以自定義類加載 器以外,其它操作都由JVM主導。到了初始階段,才開始真正執行類中定義的Java程序代碼。
注意以下幾種情況不會執行類初始化:
- 通過子類引用父類的靜態字段,只會觸發父類的初始化,而不會觸發子類的初始化。
- 定義對象數組,不會觸發該類的初始化。
- 常量在編譯期間會存入調用類的常量池中,本質上并沒有直接引用定義常量的類,不會觸發定義常量所在的類。
- 通過類名獲取Class對象,不會觸發類的初始化。
- 通過Class.forName加載指定類時,如果指定參數initialize為false時,也不會觸發類初 始化,其實這個參數是告訴虛擬機,是否要對類進行初始化。
- 通過ClassLoader默認的loadClass方法,也不會觸發初始化動作。
2、運行時數據區(Runtime Data Area)

6.1、程序計數器(線程私有)
一塊較小的內存空間,是當前線程執行字節碼的行號指示器,每個線程都要有一個獨立的程序計數器。它在虛擬機中沒有規定任何OutOfMemoryError情況的區域。
6.2、虛擬機棧或JVM棧(線程私有)
是描述java方法執行的內存模型,每個方法執行的同時都會創建一個和棧幀(Stack Frame)用于存儲局部變量表、操作數棧、動態鏈接、返回地址。每一個方法從調用直至執行完成 的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。
棧幀( Frame)是用來存儲數據和部分過程結果的數據結構,同時也被用來處理動態鏈接 (Dynamic Linking)、 方法返回值和異常分派( Dispatch Exception)。棧幀隨著方法調用而創建,隨著方法結束而銷毀——無論方法是正常完成還是異常完成(拋出了在方法內未被捕獲的異常)都算作方法結束。

6.3、本地方法區(線程私有)
本地方法區和JVM棧作用相似。區別是JVM棧是入棧java方法服務,而本地方法棧是入棧Native方法服務
6.4、堆(線程共享)
是被線程共享的一塊內存區。創建的 對象和 數組都保存在Java堆中,也是垃圾收集器進行 垃圾收集的最重要的內存區域。從GC角度考慮,還可以將堆細分為: 新生代(Eden區、From Survivor區和To Survivor區)和 老年代。
6.5、方法區或永久代(線程共享)
永久代(Permanent Generation), 用于存儲被 JVM 加載的 類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據
運行時常量池(Runtime Constant Pool)是方法區的一部分。Class文件中除了有類的版 本、字段、方法、接口等描述等信息外,還有一項信息是常量池
8. 線程池
線程池優點
- 減少資源創建(線程的反復使用)
- 降低系統時間開銷(線程的創建需要時間,延遲請求)
- 提高系統穩定性(避免無限創建線程導致的OutOfMemoryError)
一般創建方式有兩種
- 通過Executors工廠方法創建
- 通過
new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
自定義創建
線程池的創建不推薦使用Executors,容易內存溢出
Executors創建線程池
-
newFixedThreadPool
創建一個指定數量的線程池。當提交任務就創建一個工作線程,如果工作線程數量達到初始線程最大數,將提交的任務存入池隊列中
newFixedThreadPool -
newCachedThreadPool
創建一個可緩存的線程池,如果線程池長度超出處理需要,可靈活回收空閑線程池,若無可回收,則創建新線程.
這種類型的線程池特點是:
工作線程的創建數量幾乎沒有限制(其實也有限制的,數目為Interger. MAX_VALUE), 這樣可靈活的往線程池中添加線程。
如果長時間沒有往線程池中提交任務,即如果工作線程空閑了指定的時間(默認為1分鐘),則該工作線程將自動終止。終止后,如果你又提交了新的任務,則線程池重新創建一個工作線程。
在使用CachedThreadPool時,一定要注意控制任務的數量,否則,由于大量線程同時運行,很有會造成系統癱瘓。
newCachedThreadPool -
newSingleThreadExecutor
創建一個單線程化的Executor,即只創建唯一的工作者線程來執行任務,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO,優先級)執行。如果這個線程異常結束,會有另一個取代它,保證順序執行。單工作線程最大的特點是可保證順序地執行各個任務,并且在任意給定的時間不會有多個線程是活動的。
newSingleThreadExecutor -
newScheduleThreadPool
創建一個定長的線程池,而且支持定時的以及周期性的任務執行,支持定時及周期性任務執行。
newScheduleThreadPool
ThreadPoolExecutor自定義創建線程池
四種構造方法

corePoolSize:核心池的大小,這個參數跟后面講述的線程池的實現原理有非常大的關系。在創建了線程池后,默認情況下,線程池中并沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建了線程池后,線程池中的線程數為0,當有任務來之后,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize后,就會把到達的任務放到緩存隊列當中;
maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程;
keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大于corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數不大于corePoolSize,即當線程池中的線程數大于corePoolSize時,如果一個線程空閑的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大于corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數為0;
unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小時
TimeUnit.MINUTES; //分鐘
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //納秒
workQueue:一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這里的阻塞隊列有以下幾種選擇:
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和SynchronousQueue。線程池的排隊策略與BlockingQueue有關。
threadFactory:用于設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程做些更有意義的事情,比如設置daemon和優先級等等
handler:表示當拒絕處理任務時的策略,有以下四種取值:
1、AbortPolicy:直接拋出異常。
2、CallerRunsPolicy:只用調用者所在的主線程來運行任務。
3、DiscardOldestPolicy:丟棄隊列里最近的一個任務,并執行當前任務。
4、DiscardPolicy:不處理,丟棄掉。
5、也可以根據應用場景需要來實現RejectedExecutionHandler接口自定義策略。如記錄日志或持久化不能處理的任務。
線程池的submit()和execute()
當一個線程池里面的線程異常后:
1、當執行方式是execute時,可以看到堆棧異常的輸出
原因:ThreadPoolExecutor.runWorker()方法中,task.run(),即執行我們的方法,如果異常的話會throw x;所以可以看到異常。
2、當執行方式是submit時,堆棧異常沒有輸出。但是調用Future.get()方法時,可以捕獲到異常
原因:ThreadPoolExecutor.runWorker()方法中,task.run(),其實還會繼續執行FutureTask.run()方法,再在此方法中c.call()調用我們的方法,
如果報錯是setException(),并沒有拋出異常。當我們去get()時,會將異常拋出。
3、不會影響線程池里面其他線程的正常執行
4、線程池會把這個線程移除掉,并創建一個新的線程放到線程池中
當線程異常,會調用ThreadPoolExecutor.runWorker()方法最后面的finally中的processWorkerExit(),會將此線程remove,并重新addworker()一個線程。
線程池大小,線程數計算公式

9.IO流

字節流:InputStream、OutputStream
字符流:Reader、Writer(這四個都是抽象類)
IO類設計時使用了裝飾者設計模式
什么是比特(Bit),什么是字節(Byte),什么是字符(Char),它們長度是多少,各有什么區別
Bit最小的二進制單位 ,是計算機的操作部分 取值0或者1
Byte是計算機操作數據的最小單位由8位bit組成 取值(-128-127)
Char是用戶的可讀寫的最小單位,在java里面由16位bit組成 取值(0-65535)
按功能不同分為節點流、處理流
節點流:以從或向一個特定的地方(節點)讀寫數據。如FileInputStream
處理流:是對一個已存在的流的連接和封裝,通過所封裝的流的功能調用實現數據讀寫。如BufferedReader。處理流的構造方法總是要帶一個其他的流對象做參數。一個流對象經過其他流的多次包裝,
6.序列化
將保存在內存中的對象數據轉化為二進制數據流進行傳輸,任何對象都可以序列化
實現方法:實現java.io.Serializable接口
作用:把一個Java對象寫入到硬盤或者傳輸到網路上面的其它計算機,這時我們就需要自己去通過java把相應的對象寫成轉換成字節流。對于這種通用的操作,我們為什么不使用統一的格式呢?沒錯,這里就出現了java的序列化的概念。在Java的OutputStream類下面的子類ObjectOutput-Stream類就有對應的WriteObject(Object object) 其中要求對應的object實現了java的序列化的接口。
7.反序列化
將二進制數據換回原對象
構造:ObjectInputStream(InputStream in)
方法:Object readObject() 從 ObjectInputStream 讀取對象
8.transient關鍵字
以上序列化和反序列化實現了的對象序列化,但是可以發現,操作時是將整個對象的所有屬性序列化,那么transient關鍵字可以將某些內容不需要保存,就可以通過transient關鍵字來定義
private transient string title;
此時title屬性無法被序列化,
10.鎖
1、ReentrantLock

(1)ReentrantLock特性
- 依賴于AQS
- 支持響應中斷、超時、嘗試獲取鎖
- 必須手動調用unlock()釋放鎖
- 支持公平鎖和非公平鎖
- 可關聯多個條件隊列
- 可重入鎖
(2)ReentrantLock與AQS關聯
非公平鎖加密流程
static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;/*** Performs lock. Try immediate barge, backing up to normal* acquire on failure.*/final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}
- 若通過CAS設置變量State(同步狀態)成功,也就是獲取鎖成功,則將當前線程設置為獨占線程。
- 若通過CAS設置變量State(同步狀態)失敗,也就是獲取鎖失敗,則進入Acquire方法進行后續處理。
其中Acquire為AQS核心方法
2、AQS
AQS中的隊列是CLH(Craig、Landin and Hagersten隊列)變體的虛擬雙向隊列(FIFO),AQS是通過將每條請求共享資源的線程封裝成一個節點來實現鎖的分配。

AQS使用一個Volatile的int類型的成員變量來表示同步狀態,通過內置的FIFO隊列來完成資源獲取的排隊工作,通過CAS完成對State值的修改。
CLH隊列中等待的節點為AQS中的一個數據結構(Node)
static final class Node {static final Node SHARED = new Node();static final Node EXCLUSIVE = null;static final int CANCELLED = 1;static final int SIGNAL = -1;static final int CONDITION = -2;static final int PROPAGATE = -3;//當前節點在隊列中的狀態volatile int waitStatus;//前驅指針volatile Node prev;//后繼指針volatile Node next;//表示處于該節點的線程volatile Thread thread;//指向下一個處于CONDITION狀態的節點(由于本篇文章不講述Condition Queue隊列,這個指針不多介紹)Node nextWaiter;....
}
線程鎖的兩種模式:
- SHARED 表示線程以共享的模式等待鎖
- EXCLUSIVE 表示線程正在以獨占的方式等待鎖
waitStatus有下面幾個枚舉值:
- 0 當一個Node被初始化的時候的默認值
- CANCELLED 為1,表示線程獲取鎖的請求已經取消了
- CONDITION 為-2,表示節點在等待隊列中,節點線程等待喚醒
- PROPAGATE 為-3,當前線程處在SHARED情況下,該字段才會使用
- SIGNAL 為-1,表示線程已經準備好了,就等資源釋放了
3、synchronized與lock

- synchronized是關鍵字,是JVM層面的底層啥都幫我們做了,而Lock是一個接口,是JDK層面的有豐富的API。
- synchronized會自動釋放鎖,而Lock必須手動釋放鎖。
- synchronized是不可中斷的,Lock可以中斷也可以不中斷。
- 通過Lock可以知道線程有沒有拿到鎖,而synchronized不能。
- synchronized能鎖住方法和代碼塊,而Lock只能鎖住代碼塊。 Lock可以使用讀鎖提高多線程讀效率。
- synchronized是非公平鎖,ReentrantLock可以控制是否是公平鎖
4、產生死鎖的四個條件
- 互斥:某種資源一次只允許一個進程訪問,即該資源一旦分配給某個進程,其他進程就不能再訪問,直到該進程訪問結束。
- 占有且等待:一個進程本身占有資源(一種或多種),同時還有資源未得到滿足,正在等待其他進程釋放該資源。
- 不可搶占:別人已經占有了某項資源,你不能因為自己也需要該資源,就去把別人的資源搶過來。
- 循環等待:存在一個進程鏈,使得每個進程都占有下一個進程所需的至少一種資源。
synchronized的底層實現
鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態,這幾個狀態會隨著競爭情況逐漸升級。鎖可以升級但不能降級,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖。這種鎖升級卻不能降級的策略,目的是為了提高獲得鎖和釋放鎖的效率。
鎖升級的概念
synchronized(object)
唯一線程使用時:markword 記錄這個線程Id 【偏向鎖】
出現線程爭用時:升級為【自旋鎖CAS】,理解為繞著wc轉圈
當自旋超過10次:升級為【重量級鎖】,向操作系統(os)申請鎖
(加鎖代碼)執行時間短,線程數少 使用自旋鎖
(加鎖代碼)執行時間長,線程數多 使用系統鎖
線程同步
Synchronized(object)
不能使用String常量 Integer Long....
鎖的是對象不是代碼
this xx.class
鎖定方法 非鎖定方法 可以同時執行
鎖升級 偏向鎖 自旋鎖 重量級鎖
https://www.cnblogs.com/wangwudi/p/12302668.html
5、volatile關鍵字
作用:使用valatile修飾的成員變量,就是告知程序任何對該變量的訪問均需要從共享內存中獲取,而對它的改變必須同步刷新回共享內存,它能保證所有線程對變量訪問的可見性。
11.NIO(Non-blocking/New I/O)與BIO(傳統IO)
BIO(傳統IO):
BIO是一個同步并阻塞的IO模式,傳統的 java.io 包,它基于流模型實現,提供了我們最熟知的一些 IO 功能,比如File抽象、輸入輸出流等。交互方式是同步、阻塞的方式,也就是說,在讀取輸入流或者寫入輸出流時,在讀、寫動作完成之前,線程會一直阻塞在那里,它們之間的調用是可靠的線性順序。
NIO(Non-blocking/New I/O)
NIO 是一種同步非阻塞的 I/O 模型,于 Java 1.4 中引入,對應 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解為 Non-blocking,不單純是 New。它支持面向緩沖的,基于通道的 I/O 操作方法。 NIO 提供了與傳統 BIO 模型中的 Socket
和 ServerSocket
相對應的 SocketChanne
l 和 ServerSocketChannel
兩種不同的套接字通道實現,兩種通道都支持阻塞和非阻塞兩種模式。對于高負載、高并發的(網絡)應用,應使用 NIO 的非阻塞模式來開發
IO模型 | BIO | NIO |
---|---|---|
通信 | 面向流 | 面向緩沖 |
處理 | 阻塞IO | 非阻塞IO |
觸發 | 無 | 選擇器 |


NIO的特點:
- 一個線程可以處理多個通道,減少線程創建數量;
- 讀寫非阻塞,節約資源:沒有可讀/可寫數據時,不會發生阻塞導致線程資源的浪費
12. Netty

引用: https://juejin.cn/post/6921858121774137352#heading-17
13.面向對象
一、面向對象(OOP):把數據及對數據的操作方法放在一起,作為一個相互依存的整體。
二、面向對象對象的三大特征
(1)封裝
兩層含義:一層含義是把對象的屬性和行為看成一個密不可分的整體,將這兩者“封裝”在一個不可分割的獨立單元(即對象)中;另一層含義指“信息隱藏”,把不需要讓外界知道的信息隱藏起來,有些對象的屬性及行為允許外界用戶知道或使用,但不允許更改,而另一些屬性或行為,則不允許外界知曉,或只允許使用對象的功能,而盡可能隱藏對象的功能實現細節。
封裝的優點
- 良好的封裝能夠減少耦合,符合程序設計追求“高內聚,低耦合”。
- 類內部的結構可以自由修改。
- 可以對成員變量進行更精確的控制。
- 隱藏信息實現細節。
(2)繼承
繼承就是子類繼承父類的特征和行為,使得子類對象(實例)具有父類的實例域和方法,或子類從父類繼承方法,使得子類具有父類相同的行為。
繼承的優點
- 提高類代碼的復用性
- 提高了代碼的維護性
- 使得類和類產生了關系,是多態的前提(它也是繼承的一個弊端,類的耦合性提高了)
(3)多態
多態是同一個行為具有多個不同表現形式或形態的能力。
Java語言中含有方法重載與對象多態兩種形式的多態:
方法重載:在一個類中,允許多個方法使用同一個名字,但方法的參數不同,完成的功能也不同。
對象多態:子類對象可以與父類對象進行轉換,而且根據其使用的子類不同完成的功能也不同(重寫父類的方法)。
面試題:什么是多態?實現多態的方法有哪些?
多態是面向對象的最后一個主要特征,它本身主要分為兩個方面:
方法的多態性:重載與覆寫
重載:同一個方法名稱,根據不同的參數類型及個數可以完成不同的功能。
覆寫:同一個方法,根據操作的子類不同,所完成的功能也不同。
對象的多態:父子類對象的轉換。
向上轉型:子類對象變為父類對象,格式:父類 父類對象 = 子類實例,自動;(Parent parent = new Child())
向下轉型:父類對象變為子類對象,格式:子類 子類對象 = (子類)父類實例,強制。(前提條件是Parent parent = new Child();,若是Parent parent = new Parent()會報錯)
多態的優點
- 消除類型之間的耦合關系
- 可替換性
- 可擴充性
- 接口性
- 靈活性
- 簡化性
14.Final關鍵字
Final用于修飾類、成員變量和成員方法。final修飾的類,不能被繼承(String、StringBuilder、StringBuffer、Math,不可變類),其中所有的方法都不能被重寫(這里需要注意的是不能被重寫,但是可以被重載,這里很多人會弄混),所以不能同時用abstract和final修飾類(abstract修飾的類是抽象類,抽象類是用于被子類繼承的,和final起相反的作用);Final修飾的方法不能被重寫,但是子類可以用父類中final修飾的方法;Final修飾的成員變量是不可變的,如果成員變量是基本數據類型,初始化之后成員變量的值不能被改變,如果成員變量是引用類型,那么它只能指向初始化時指向的那個對象,不能再指向別的對象,但是對象當中的內容是允許改變的。
- final方法比非final快一些
- final關鍵字提高了性能。JVM和Java應用都會緩存final變量。
- final變量可以安全的在多線程環境下進行共享,而不需要額外的同步開銷。
- 使用final關鍵字,JVM會對方法、變量及類進行優化。
https://blog.csdn.net/qq_42651904/article/details/87708198
15.Java序列化
定義:Java平臺允許我們在內存中創建可復用的Java對象,但一般情況下,只有當JVM處于運行時,這些對象才可能存在,即,這些對象的生命周期不會比JVM的生命周期更長。但在現實應用中,就可能要求在JVM停止運行之后能夠保存(持久化)指定的對象,并在將來重新讀取被保存的對象。Java對象序列化就能夠幫助我們實現該功能。
持久化對象時會用到對象序列化之外,當使用RMI(遠程方法調用),或在網絡中傳遞對象時,都會用到對象序列化。
http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html
16.cookie和session
在程序中,會話跟蹤是很重要的事情。理論上,一個用戶的所有請求操作都應該屬于同一個會話,而另一個用戶的所有請求操作則應該屬于另一個會話,二者不能混淆。
而Web應用程序是使用HTTP協議傳輸數據的。HTTP協議是無狀態的協議。一旦數據交換完畢,客戶端與服務器端的連接就會關閉,再次交換數據需要建立新的連接。這就意味著服務器無法從連接上跟蹤會話。
https://www.cnblogs.com/l199616j/p/11195667.html
Spring
1. Bean的生命周期
執行調用Aware接口對應的方法
1.Spring對Bean進行實例化(相當于程序中的new Xx())
2.Spring將值和Bean的引用注入進Bean對應的屬性中
3.如果Bean實現了BeanNameAware接口,Spring將Bean的ID傳遞給setBeanName()方法(實現BeanNameAware清主要是為了通過Bean的引用來獲得Bean的ID,一般業務中是很少有用到Bean的ID的)和BeanClassLoaderAware
4.如果Bean實現了BeanFactoryAware接口,Spring將調用setBeanFactory(BeanFactory bf)方法并把BeanFactory容器實例作為參數傳入。(實現BeanFactoryAware 主要目的是為了獲取Spring容器,如Bean通過Spring容器發布事件等)
5.如果Bean實現了ApplicationContextAware接口,Spring容器將調用setApplicationContext(ApplicationContext ctx)方法,把y應用上下文作為參數傳入.
(作用與BeanFactory類似都是為了獲取Spring容器,不同的是Spring容器在調用setApplicationContext方法時會把它自己作為setApplicationContext 的參數傳入,而Spring容器在調用setBeanFactory前需要程序員自己指定(注入)setBeanFactory里的參數BeanFactory )
執行before方法
6.如果Bean實現了BeanPostProcess接口,Spring將調用它們的postProcessBeforeInitialization(預初始化)方法
(作用是在Bean實例創建成功后對進行增強處理,如對Bean進行修改,增加某個功能;例如CommonAnnoationBeanPostProcessor)
調用執行init-method
7.如果Bean實現了InitializingBean接口,Spring將調用它們的afterPropertiesSet方法,作用與在配置文件中對Bean使用init-method聲明初始化的作用一樣,都是在Bean的全部屬性設置成功后執行的初始化方法。
執行after方法
8.如果Bean實現了BeanPostProcess接口,Spring將調用它們的postProcessAfterInitialization(后初始化)方法
(作用與6的一樣,只不過6是在Bean初始化前執行的,而這個是在Bean初始化后執行的,時機不同;例如AbstractAutoProxyCreator AOP )
9.經過以上的工作后,Bean將一直駐留在應用上下文中給應用使用,直到應用上下文被銷毀
10.如果Bean實現了DispostbleBean接口,Spring將調用它的destory方法,作用與在配置文件中對Bean使用destory-method屬性的作用一樣,都是在Bean實例銷毀前執行的方法。
2.Spring事務
事務具備ACID四種特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔離性)和Durability(持久性)的英文縮寫。
Spring的事務傳播行為
- propagation_required:如果當前沒有事務,就新建一個事務;如果當前存在事務,則加入事務。
- propagation_required_new:如果當前沒有事務,就新建一個事務;如果當前存在事務,則把當前事務掛起,新建一個事務。
- propagation_supports:支持當前事務,如果沒有事務,以非事務的方式執行。
- propagation_not_supported:當前存在事務,把事務掛起,以非事務方式執行。
- propagation_mandatory:如果當前存在事務,加入當前事務。否則拋出異常。
- propagation_never:當前存在事務拋出異常。
- propagation_nested:當前存在事務,則在嵌套事務內執行。如果當前沒有事務,和propagation_required操作相似
3.Spring中使用的設計模式
- 工廠模式:BeanFactory就是簡單工廠模式體現
- 單例模式: 提供全局使用的BeanFactory
- 代理模式:AOP的功能就是用代理模式
- 裝飾者模式:Bean創建時,使用的Bean包裝類BeanWrapper進行依賴注入
- 觀察者模式:spring中Listener的實現(1、提前生成多個事件 2、初始化事件多播器 3、準備好監聽器 4、向多播器中注冊監聽器 5、事件發布,通知多播器進行相關邏輯)
- 策略模式:Bean采用何種方法進行實例化(反射、工廠方法和cglib代理)
SpringMVC
SpringMVC請求流程
- 用戶請求發送給DispatcherServlet,DispatcherServlet調用HandlerMapping處理器映射器;
- HandlerMapping根據xml或者注解找到相應的處理器,生成處理器對象返回給DispatcherServlet;
- DispatcherServlet會調用相應的HandlerAdapter;
- HandlerAdapter經過設配器調用具體的處理器去處理請求,生成ModleAndView返回給DispatcherServlet;
- DispatcherServlet將ModelAndView傳給ViewReslover解析生成View返回給DispatcherServlet;
- DispatcherServlet根據View進行渲染試圖
SpringBoot
Redis
1. 五大數據類型
- String
- Hash(數組+鏈表)
- List(雙向鏈表)
- Set(value為空的HashMap,計算hash快速排重)
- Zset(HashMap+跳表,HashMap里放的時成員到score的映射,跳表存放的是所有成員,查詢效率高)
2.RDB與AOF區別
(1):R文件格式緊湊,方便數據恢復,保存rdb文件時父進程會fork出子進程由其完成具體持久化工作,最大化redis性能,恢復大數據集速度更快,只有手動提交save命令或關閉命令時才觸發備份操作;
(2):A記錄對服務器的每次寫操作(默認1s寫入一次),保存數據更完整,在redis重啟是會重放這些命令來恢復數據,操作效率高,故障丟失數據更少,但是文件體積更大;
3.redis單線程為什么執行速度這么快?
(1):純內存操作,避免大量訪問數據庫,減少直接讀取磁盤數據,redis將數據儲存在內存里面,讀寫數據的時候都不會受到硬盤 I/O 速度的限制,所以速度快
(2):單線程操作,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導致的性能消耗
(3):采用了非阻塞I/O多路復用機制
4.緩存雪崩以及處理辦法
同一時刻大量緩存失效;
處理方法:
(1):緩存數據增加過期標記
(2):設置不同的緩存失效時間
(3):雙層緩存策略C1為短期,C2為長期
(4):定時更新策略
5.緩存擊穿原因以及處理辦法
頻繁請求查詢系統中不存在的數據導致;
處理方法:
(1):cache null策略,查詢反饋結果為null仍然緩存這個null結果,設置不超過5分鐘過期時間
(2):布隆過濾器,所有可能存在的數據映射到足夠大的bitmap中
google布隆過濾器:基于內存,重啟失效不支持大數據量,無法在分布式場景
redis布隆過濾器:可擴展性,不存在重啟失效問題,需要網絡io,性能低于google
Mysql
1、數據庫三范式
一、確保每列的原子性
二、非主鍵列不存在對主鍵的部分依賴(要求每個表只描述一件事)
三、滿足第二范式,并且表中的列不存在對非主鍵的傳遞依賴
2、儲存引擎
Mysql在V5.1之前默認存儲引擎是MyISAM;在此之后默認存儲引擎是InnoDB
MyISAM:
- MyISAM不支持數據庫事務,不支持外鍵。采用非聚集索引(非聚集索引主索引的子節點保存的是內存地址)。
- MyISAM用一個變量保存整個表的行數,執行select count(*) from table 時讀出變量即可。
- MyISAM最小的鎖力度是表鎖,更新語句會鎖住整張表,導致其他查詢和更新會被阻塞。
InnoDB:
- InnoDB支持數據庫事務,采用聚集索引(聚集索引文件存放在主索引的葉子節點上),因此必須要有主鍵。
- InnoDB最小的鎖粒度是行鎖
非聚集索引和聚集索引區別
(1)聚集索引就是以主鍵創建的索引,非聚集索引是以除了主鍵外的索引創建的
(2)每張表只能創建一個聚集索引,可以創建多個非聚集索引
(3)表記錄的排序與聚集索引排序一致
(4)聚集索引存儲記錄是物理上連續的,非聚集索引是邏輯上連續的
(5)聚集索引適合排序,非聚集索引不適合排序場合(聚集索引的子節點就是數據,和數據按相同位置放在一起,索引即使數據。非聚集索引子節點只是保留了數據指針,數據并未排序)
(6)更新聚集索引代價比非聚集索引高
3.數據庫事務隔離級別

mysql默認事務隔離級別是可重復讀
1、臟讀:事務A讀取了事務B更新的數據,然后B回滾操作,那么A讀取到的數據是臟數據
2、不可重復讀:事務 A 多次讀取同一數據,事務B在事務A多次讀取的過程中,對數據作了更新并提交,導致事務A多次讀取同一數據時,結果不一致。
3、幻讀:系統管理員A將數據庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員B就在這個時候插入了一條具體分數的記錄,當系統管理員A改結束后發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。
小結:不可重復讀的和幻讀很容易混淆,不可重復讀側重于修改,幻讀側重于新增或刪除。解決不可重復讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表
計算機網絡
1.網絡模型
國際標準化組織(ISO)制定了OSI模型,OSI模型把網絡通信分成7層,上從到下分別是應用層、表示層、會話層、傳輸層、網絡層、數據鏈路層、物理層。之后又將七層通信模型簡化合并為四層模型,分別是應用層、傳輸層、網絡層、網絡接口層(各層之間的模型、協議統稱為TCP/IP協議簇)。

OSI中的層 | 功能 | TCP/IP協議族 |
---|---|---|
應用層 | 文件傳輸,電子郵件,文件服務,虛擬終端 | TFTP,HTTP,SNMP,FTP,SMTP,DNS,RIP,Telnet |
表示層 | 數據格式化,代碼轉換,數據加密 | 無 |
會話層 | 控制應用程序之間會話能力;如不同軟件數據分發給不同軟件 | ASAP、TLS、SSH、ISO 8327 / CCITT X.225、RPC、NetBIOS、ASP、Winsock、BSD sockets |
傳輸層 | 端到端傳輸數據的基本功能 | TCP、UDP |
網絡層 | 定義IP編址,定義路由功能;如不同設備的數據轉發 | IP,ICMP,RIP,OSPF,BGP,IGMP |
數據鏈路層 | 定義數據的基本格式,如何傳輸,如何標識 | SLIP,CSLIP,PPP,ARP,RARP,MTU |
物理層 | 以二進制數據形式在物理媒體上傳輸數據 | ISO2110,IEEE802 |
2.TCP/IP與HTTP
TCP/IP協議不僅僅指的是TCP和IP兩個協議,而是指FTP、SMTP、TCP、UDP、IP等協議構成的協議簇
HTTP是應用層協議,主要解決如何包裝數據
“IP”代表網際協議,TCP 和 UDP 使用該協議從一個網絡傳送數據包到另一個網絡。把IP想像成一種高速公路,它允許其它協議在上面行駛并找到到其它電腦的出口。TCP和UDP是高速公路上的“卡車”,它們攜帶的貨物就是像HTTP,文件傳輸協議FTP這樣的協議等。
3.TCP和UDP
- 都屬于傳輸層協議
- TCP(Transmission Control Protocol,傳輸控制協議)是面向連接的協議。在收發數據前,必須和對方建立可靠的連接。一個TCP連接需要有三次握手、四次揮手。
- UDP(User Data Protocol,用戶數據報協議)是一個非連接協議,傳輸數據之前源端和終端不建立連接,當它想傳輸時就簡單的抓取應用程序的數據,盡可能快的扔到網上。

TCP和UDP協議的一些應用

4.TCP三次握手
三次握手就是指TCP建立連接需要發送3個包。目的是連接服務器指定端口,建立TCP連接,并同步連接雙方的序列號和確認號,交換TCP窗口信息大小。

- 第一次握手(SYN=1,seq=x)
建立連接,客服端發送請求報文,報文首部中的同步位SYN=1,同時選擇一個初始化序列號seq=x。此時客戶端進入SYN-SENT(同步已發送狀態)。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號; - 第二次握手(SYN=1,ACK=1,seq=y,ACKnum=x+1)
服務器收到客戶端的SYN報文段,如果同意連接,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號ACKnum=x+1,同時,自己還要發送SYN請求信息,SYN=1,為自己初始化一個序列號 seq=y,服務器端將上述所有信息放到一個報文段(即SYN+ACK報文段)中,一并發送給客戶端,此時,TCP服務器進程進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但是同樣要消耗一個序號 - 第三次握手(ACK=1,ACKnum=y+1)
客戶端收到服務器的SYN+ACK報文段,再次發送確認包(ACK),SYN 標志位為0,ACK 標志位為1,確認號 ACKnum = y+1,這個報文段發送完畢以后,客戶端和服務器端都進入ESTABLISHED(已建立連接)狀態,完成TCP三次握手。
為什么需要三次握手呢?兩次不行嗎?
為了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤。
具體例子:“已失效的連接請求報文段”的產生在這樣一種情況下:client發出的第一個連接請求報文段并沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后,就誤認為是client再次發出的一個新的連接請求。于是就向client發出確認報文段,同意建立連接。假設不采用“三次握手”,那么只要server發出確認,新的連接就建立了。由于現在client并沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送數據。但server卻以為新的運輸連接已經建立,并一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。采用“三次握手”的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的確認發出確認。server由于收不到確認,就知道client并沒有要求建立連接。”
5.四次揮手
TCP 的連接的拆除需要發送四個包,因此稱為四次揮手(Four-way handshake),也叫做改進的三次握手。客戶端或服務器均可主動發起揮手動作。

- 第一次揮手(FIN=1,seq=x)
主機1(可以使客戶端,也可以是服務器端),設置seq=x,向主機2發送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有數據要發送給主機2了; - 第二次揮手(ACK=1,ACKnum=x+1)
主機2收到了主機1發送的FIN報文段,向主機1回一個ACK報文段,Acknnum=x+1,主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我“同意”你的關閉請求; - 第三次揮手(FIN=1,seq=y)
主機2向主機1發送FIN報文段,請求關閉連接,同時主機2進入LAST_ACK 狀態 - 第四次揮手(ACK=1,ACKnum=y+1)
主機1收到主機2發送的FIN報文段,向主機2發送ACK報文段,然后主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段以后,就關閉連接;此時,主機1等待2MSL后依然沒有收到回復,則證明Server端已正常關閉,那好,主機1也可以關閉連接了,進入 CLOSED 狀態。
主機 1 等待了某個固定時間(兩個最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,沒有收到服務器端的 ACK ,認為服務器端已經正常關閉連接,于是自己也關閉連接,進入 CLOSED 狀態。
為什么連接的時候是三次握手,關閉的時候卻是四次握手?
因為當Server端收到Client端的SYN連接請求報文后,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能并不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。
由于 TCP 協議是全雙工的,也就是說客戶端和服務端都可以發起斷開連接。兩邊各發起一次斷開連接的申請,加上各自的兩次確認,看起來就像執行了四次揮手。
6.HTTP
URI 和 URL
每個Web 服務器資源都有一個名字,這樣客戶端就可以說明他們感興趣的資源是什么了,服務器資源名被稱為統一資源標識符(Uniform Resource Identifier,URI)。URI 就像因特網上的郵政地址一樣,在世界范圍內唯一標識并定位信息資源。
統一資源定位符(URL)是資源標識符最常見的形式。 URL 描述了一臺特定服務器上某資源的特定位置。
現在幾乎所有的 URI 都是 URL。
URI 的第二種形式就是統一資源名(URN)。URN 是作為特定內容的唯一名稱使用的,與目前的資源所在地無關
方法
Http協議定義了很多與服務器交互的方法,最基本的有4種,分別是GET,POST,PUT,DELETE. 一個URL地址用于描述一個網絡上的資源,而HTTP中的GET, POST, PUT, DELETE就對應著對這個資源的查,改,增,刪4個操作。 我們最常見的就是GET和POST了。GET一般用于獲取/查詢資源信息,而POST一般用于更新資源信息。
狀態碼
每條HTTP響應報文返回時都會攜帶一個狀態碼。狀態碼是一個三位數字的代碼,告知客戶端請求是否成功,或者是都需要采取其他動作。
- 1xx:表明服務端接收了客戶端請求,客戶端繼續發送請求;
- 2xx:客戶端發送的請求被服務端成功接收并成功進行了處理;
- 3xx:服務端給客戶端返回用于重定向的信息;
- 4xx:客戶端的請求有非法內容;
- 5xx:服務端未能正常處理客戶端的請求而出現意外錯誤。
200 OK:表示從客戶端發送給服務器的請求被正常處理并返回;
204 No Content:表示客戶端發送給客戶端的請求得到了成功處理,但在返回的響應報文中不含實體的主體部分(沒有資源可以返回)
206 Patial Content:表示客戶端進行了范圍請求,并且服務器成功執行了這部分的GET請求,響應報文中包含由Content-Range指定范圍的實體內容。
301 Moved Permanently:永久性重定向,表示請求的資源被分配了新的URL,之后應使用更改的URL;
302 Found:臨時性重定向,表示請求的資源被分配了新的URL,希望本次訪問使用新的URL;
303 See Other:表示請求的資源被分配了新的URL,應使用GET方法定向獲取請求的資源
304 Not Modified:表示客戶端發送附帶條件(是指采用GET方法的請求報文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的請求時,服務器端允許訪問資源,但是請求為滿足條件的情況下返回改狀態碼;
400 Bad Request:表示請求報文中存在語法錯誤;
401 Unauthorized:經許可,需要通過HTTP認證;
403 Forbidden:服務器拒絕該次訪問(訪問權限出現問題)
404 Not Found:表示服務器上無法找到請求的資源,除此之外,也可以在服務器拒絕請求但不想給拒絕原因時使用;
500 Inter Server Error:表示服務器在執行請求時發生了錯誤,也有可能是web應用存在的bug或某些臨時的錯誤時;
503 Server Unavailable:表示服務器暫時處于超負載或正在進行停機維護,無法處理請求;
參考:https://www.cnblogs.com/lazyegg/p/12710890.html
數據結構
鏈表
1、判斷一個鏈表是否有環?
初始化兩個指針slow、fast?分別前進一步和兩步。再此當兩指針再次相遇就是有環,fast到達null就是無環。
2、判斷環形鏈表長度
初始化兩個指針slow、fast?分別前進一步和兩步。再此當兩指針再次相遇的點就是環長度即slow的步數
樹
1、AVLtree
定義:平衡二叉搜索樹(Self-balancing binary search tree)又被稱為AVL樹(有別于AVL算法),且具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。
平衡因子
某結點的左子樹與右子樹的高度(深度)差即為該結點的平衡因子(BF,Balance Factor)。平衡二叉樹上所有結點的平衡因子只可能是 -1,0 或 1。如果某一結點的平衡因子絕對值大于1則說明此樹不是平衡二叉樹。為了方便計算每一結點的平衡因子我們可以為每個節點賦予height這一屬性,表示此節點的高度。
https://blog.csdn.net/qq_25343557/article/details/89110319
2、哈夫曼樹
目的:該樹結構是找出存放一串字符所需的最少的二進制編碼。
https://blog.csdn.net/qq_29519041/article/details/81428934
3、B樹(B-tree)
概念:B樹和平衡二叉樹稍有不同的是B樹屬于多叉樹又名平衡多路查找樹(查找路徑不只兩個),數據庫索引技術里大量使用者B樹和B+樹的數據結構。
4、KMP
子串每次移動的位置=已經匹配上的字符-最后一個匹配上的字符的最大對稱數;
5、折半查找平均查找長度
n為長度
得平均查找長度為:((n+1)/n ) *log2(n+1)-1 (其中對數中的2為底數:即log以2為底(n+1)的對數)
注 : 當n很大時 ,可近似為 log2(n+1)-1
6、系統采用頁式存儲管理方案,若頁號塊號對應關系存于內存中,且內存的訪問時間為1μs,則當快表命中率為50%和85%時,有效的存取時間分別為( )
當向內存中命中快表后,不要再去訪問內存;
50%1μs+(1-50%)(1μs+1μs)=1.5μs
80%1μs+(1-80%)(1μs+1μs)=1.15μs
注有效時間=命中+未命中的時間
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務

喜歡的朋友記得點贊、收藏、關注哦!!!