- 一、Java
- Java的優勢
- Java有哪些特性,舉個多態的例子。
- abstract interface區別
- 有抽象方法一定是抽象類嗎?抽象類一定有抽象方法嗎?
- Java的反射機制
- super()和this()能不能同時使用
- hashcode,equals,Object的這兩個方法默認返回什么?描述了一下為什么重寫equals方法必須重寫hashcode方法
- final
- String,StringBuffer,StringBuilder區別
- String為什么不可變
- String,是否可以繼承,“+”怎樣實現
- 字符串常量池
- map、list、set的區別
- 有沒有有序的set?
- Set如何保證不重復?
- 說一說對Java io的理解
- nio與bio的了解以及說一下區別
- Java並發的理解
- 死鎖,死鎖原因
- wait和sleep的區別
- ArrayList和LinkedList有什么區別?
- HashMap 的原理,hashmap的擴容問題,為什么HashMap的初始容量會是16,為什么是2倍擴容,實現簡單的 get/put操作;處理哈希沖突用的哪種方法(拉鏈),還知道什么處理哈希沖突的方法(開放地址檢測),開放地址檢測怎么實現的
- 從哈希表中刪除一個元素,再加入元素時恰好與原來那個哈希沖突,這個元素會放在哪
- HashMap、Hashtable、concurrenthashmap
- HashTable為什么是線程安全的?
- HashMap,ConcurrentHashMap以及在什么情況下性能會不好
- Thread狀態有哪些
- 多線程實現方法
- Java如何實現線程安全
- Synchronized和lock區別
- Java中都有什么鎖
- 可重入鎖的設計思路是什么
- 樂觀鎖和悲觀鎖
- juc包內有哪些類
- CAS如何實現
- BlockQueue見過沒?
- 線程池原理
- 線程池的排隊策略和拒絕策略的試用條件和具體內容。
- 線程池的類型,詳細介紹cached和fixed
- corePoolSize參數的意義
- 線程池新任務到達時會先使用空閑線程還是加入阻塞隊列
- Java並發包里面的CountdownLatch怎么使用
- volatile和synchronized區別
- 線程池使用時一般要考慮哪些問題
- 一般線程和守護線程的區別
- 一致性Hash原理,實現負載均衡
- 異常
- servlet流程
- forward redirect 二次請求
- 序列化,以及json傳輸
- tomcat均衡方式
- netty
- 二、JVM
- 三、數據結構與算法
- 常見的排序算法時間復雜度
- 快排算法 寫代碼
- 堆排序怎么實現
- 鏈表,數組的優缺點,應用場景,查找元素的復雜度
- 入棧出棧的時間復雜度,鏈表插入和刪除的時間復雜度
- 如何用LinkedList實現堆棧操作
- Arraylist如何實現排序
- 利用數組,實現一個循環隊列類
- 兩個有序數組,有相同的元素,找出來
- 二叉樹怎么實現的
- 二叉樹前中后序遍歷 深度 廣度
- 二叉樹深度
- 層序遍歷二叉樹
- 樹的中序遍歷,除了遞歸和棧還有什么實現方式
- 二叉搜索樹轉換成一個排好序的雙向鏈表
- 判斷平衡二叉樹
- 給定一個2叉樹,打印每一層最右邊的結點
- 一棵普通樹(非二叉搜索樹),找出一條路徑和最大
- 一棵樹,求所有路徑之和
- 最長公共子序列
- 反轉鏈表
- 判斷一個數是不是丑數
- 找出一個字符串中字符連續相同的最長子串,如aabbccc,結果就是ccc
- 蓄水池抽樣算法
- 尋找一個字符串中第一個只出現一次的字符
- 給定一個數組,里面只有一個數出現了一次,其他都出現了兩次。怎么得到這個出現了一次的數?
- 給定一個數組,如果有兩個不同數的出現了一次,其他出現了兩次,怎么得到這兩個數?
- 海量數據topk問題
- 四、操作系統
- 五、網絡
- 六、數據庫
- 數據庫事務的四個隔離級別,MySql在哪一個級別
- 數據庫死鎖/如何防止
- mysql索引,索引機制,聚集索引和非聚集索引,如何創建索引,實現原理,建立准則,優缺點,注意事項,
- 索引在什么情況下失效
- 說一下對B+樹的了解
- innodb建立的索引,如果字段重復率很高索引,索引是什么樣,查找效率如何
- innodb在插入的時候,是否會給行上鎖
- 說一下innodb的默認隔離級別
- 數據庫設計(訂單、購物車和商品)
- sql中join的幾種操作的區別
- union和union all的區別,誰的效率更高
- 用distinct和用group by去重,誰的效率更高
- sql中的優化,怎么提高查詢效率
- 緩存的穿透和雪崩,解決辦法
- redis的排序算法
- redis集群
- redis過期策略
- Redis如何解決key沖突
- redis數據類型+redis是單線程的么,為什么呢
- redis和memcache區別
- redis與mysql的區別以及優缺點
- 七、設計模式
- 八、框架
- 相當於對數據加上雙引號, $ 相當於直接顯示數據
- 方式能夠很大程度防止sql注入
- 九、分布式
- 十、Linux
- 十一、雜項
一、Java
Java的優勢
平台無關性、垃圾回收
Java有哪些特性,舉個多態的例子。
封裝、繼承、多態
abstract interface區別
含有abstract修飾符的class即為抽象類,abstract類不能創建的實例對象。含有abstract方法的類必須定義為abstract class,abstract class類中的方法不必是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,所以,不能有抽象構造方法或抽象靜態方法。如果的子類沒有實現抽象父類中的所有抽象方法,那么子類也必須定義為abstract類型。
接口(interface)可以說成是抽象類的一種特例,接口中的所有方法都必須是抽象的。接口中的方法定義默認為public abstract類型,接口中的成員變量類型默認為public static final。
下面比較一下兩者的語法區別:
- 抽象類可以有構造方法,接口中不能有構造方法。
- 抽象類中可以有普通成員變量,接口中沒有普通成員變量
- 抽象類中可以包含非抽象的普通方法,接口中的可以有非抽象方法,比如deaflut方法
- 抽象類中的抽象方法的訪問類型可以是public,protected和(默認類型,雖然
eclipse下不報錯,但應該也不行),但接口中的抽象方法只能是public類型的,並且默認即為public abstract類型。 - 抽象類中可以包含靜態方法,接口中不能包含靜態方法
- 抽象類和接口中都可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型可以任意,但接口中定義的變量只能是public static final類型,並且默認即為public static final類型。
- 一個類可以實現多個接口,但只能繼承一個抽象類。
有抽象方法一定是抽象類嗎?抽象類一定有抽象方法嗎?
有抽象方法不一定是抽象類,也可能是接口。抽象類不一定有抽象方法,可以有非抽象的普通方法。
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
- final關鍵字可以用於成員變量、本地變量、方法以及類。
- final成員變量必須在聲明的時候初始化或者在構造器中初始化,否則就會報編譯錯誤。
- 你不能夠對final變量再次賦值。
- 本地變量必須在聲明時賦值。
- 在匿名類中所有變量都必須是final變量。
- final方法不能被重寫。
- final類不能被繼承。
- 接口中聲明的所有變量本身是final的。
- final和abstract這兩個關鍵字是反相關的,final類就不可能是abstract的。
- final方法在編譯階段綁定,稱為靜態綁定(static binding)。
- 沒有在聲明時初始化final變量的稱為空白final變量(blank final variable),它們必須在構造器中初始化,或者調用this()初始化。不這么做的話,編譯器會報錯“final變量(變量名)需要進行初始化”。
- 將類、方法、變量聲明為final能夠提高性能,這樣JVM就有機會進行估計,然后優化。
- 按照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:
- 可以允許重復的對象。
- 可以插入多個null元素。
- 是一個有序容器,保持了每個元素的插入順序,輸出的順序就是插入的順序。
- 常用的實現類有 ArrayList、LinkedList 和 Vector。ArrayList 最為流行,它提供了使用索引的隨意訪問,而 LinkedList 則對於經常需要從 List中添加或刪除元素的場合更為合適。
Set:
- 不允許重復對象
- 無序容器,你無法保證每個元素的存儲順序,TreeSet通過 Comparator 或者 Comparable 維護了一個排序順序。
- 只允許一個 null 元素
- Set 接口最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基於 HashMap 實現的 HashSet;TreeSet 還實現了 SortedSet 接口,因此 TreeSet 是一個根據其 compare() 和 compareTo() 的定義進行排序的有序容器。
Map:
- Map不是collection的子接口或者實現類。Map是一個接口。
- Map 的 每個 Entry 都持有兩個對象,也就是一個鍵一個值,Map 可能會持有相同的值對象但鍵對象必須是唯一的。
- TreeMap 也通過 Comparator 或者 Comparable 維護了一個排序順序。
- Map 里你可以擁有隨意個 null 值但最多只能有一個 null 鍵。
- 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時。多線程使您能夠寫入多個活動,可以在同一程序中同時進行操作處理。
死鎖,死鎖原因
兩個或者多個線程之間相互等待,導致線程都無法執行,叫做線程死鎖。
- 互斥條件:使用的資源是不能共享的。
- 不可搶占條件:線程持有一個資源並等待獲取一個被其他線程持有的資源。
- 請求與保持條件:線程持有一個資源並等待獲取一個被其他線程持有的資源。
- 循環等待條件:線程之間形成一種首尾相連的等待資源的關系。
wait和sleep的區別
-
wait和notify方法定義在Object類中,因此會被所有的類所繼承。 這些方法都是final的,即它們都是不能被重寫的,不能通過子類覆寫去改變它們的行為。 而sleep方法是在Thread類中是由native修飾的,本地方法。
-
當線程調用了wait()方法時,它會釋放掉對象的鎖。
另一個會導致線程暫停的方法:Thread.sleep(),它會導致線程睡眠指定的毫秒數,但線程在睡眠的過程中是不會釋放掉對象的鎖的。 -
因為wait方法會釋放鎖,所以調用該方法時,當前的線程必須擁有當前對象的monitor,也即lock,就是鎖。要確保調用wait()方法的時候擁有鎖,即wait()方法的調用必須放在synchronized方法或synchronized塊中。
ArrayList和LinkedList有什么區別?
- ArrayList是實現了基於動態數組的數據結構,LinkedList基於雙向鏈表的數據結構。
- 對於隨機訪問get和set,ArrayList優於LinkedList,因為LinkedList要移動指針。
- 對於新增和刪除操作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內存划分
程序計數器:記錄正在執行的虛擬機字節碼指令的地址(如果正在執行的是本地方法則為空)。
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開發?
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的區別主要如下:
- https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。
- http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
- http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。
- http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全.
知道 HTTPS 通信過程嗎?
- 客戶端發送請求到服務器端
- 服務器端返回證書和公開密鑰,公開密鑰作為證書的一部分而存在
- 客戶端驗證證書和公開密鑰的有效性,如果有效,則生成共享密鑰並使用公開密鑰加密發送到服務器端
- 服務器端使用私有密鑰解密數據,並使用收到的共享密鑰加密數據,發送到客戶端
- 客戶端使用共享密鑰解密數據
- SSL加密建立
TCP三次握手
所謂三次握手(Three-Way Handshake)即建立TCP連接,就是指建立一個TCP連接時,需要客戶端和服務端總共發送3個包以確認連接的建立。整個流程如下圖所示:
- 第一次握手:Client將標志位SYN置為1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
- 第二次握手:Server收到數據包后由標志位SYN=1知道Client請求建立連接,Server將標志位SYN和ACK都置為1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。
- 第三次握手: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的運行流程
- 客戶端發送HTTP請求到服務器
- SpringMVC的核心DispatcherServlet將請求交給HandlerMapping處理
- HandlerMapping通過查詢機制找到處理當前請求的Handler
- DispatcherServlet將請求交給這個Handler處理
- Handler處理完成后返回一個ModleAndView對象,這個對象包含視圖邏輯名和數據對象
- 返回的視圖邏輯名會通過視圖解析器解析成真正的視圖,並交給DispatcherServlet處理
- 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並發編程實戰,看博客比較多,感覺博客更有針對性