京東面經匯總
一、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為什麽不可變
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的區別
有沒有有序的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並發的理解
死鎖,死鎖原因
兩個或者多個線程之間相互等待,導致線程都無法執行,叫做線程死鎖。
- 互斥條件:使用的資源是不能共享的。
- 不可搶占條件:線程持有一個資源並等待獲取一個被其他線程持有的資源。
- 請求與保持條件:線程持有一個資源並等待獲取一個被其他線程持有的資源。
- 循環等待條件:線程之間形成一種首尾相連的等待資源的關系。
wait和sleep的區別
ArrayList和LinkedList有什麽區別?
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
垃圾回收器
Java對象頭
HotSpot虛擬機中,對象在內存中的布局分為三塊區域:對象頭、實例數據和對齊填充。
對象頭包括兩部分:Mark Word 和 類型指針。
Mark Word:Mark Word用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等等,占用內存大小與虛擬機位長一致。
類型指針:類型指針指向對象的類元數據,虛擬機通過這個指針確定該對象是哪個類的實例。
內存泄漏
類加載過程
雙親委派模型,為什麽要使用雙親委派模型
Java虛擬機的一些參數配置
為什麽jvm調優經常會將-Xms和-Xmx參數設置成一樣
三、數據結構與算法
常見的排序算法時間復雜度
快排算法 寫代碼
堆排序怎麽實現
鏈表,數組的優缺點,應用場景,查找元素的復雜度
入棧出棧的時間復雜度,鏈表插入和刪除的時間復雜度
如何用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
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
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 有什麽區別?
知道 HTTPS 通信過程嗎?
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之間可以開始傳輸數據了。
為什麽三次和四次
TCP與HTTP有什麽關系
Tcp連接4次揮手的原因。Time_wait等待超時了會怎樣?
Server在LISTEN狀態下,收到建立連接請求的SYN報文後,可以直接把ACK和SYN放在一個報文裏發送給Client。而關閉連接時,當收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,己方也未必全部數據都發送給對方了,所以己方可以立即close,也可以發送一些數據給對方後,再發送FIN報文給對方來表示同意現在關閉連接,因此,己方ACK和FIN一般都會分開發送。
SSL 握手
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工作原理
Get和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的幾種操作的區別
union和union all的區別,誰的效率更高
用distinct和用group by去重,誰的效率更高
sql中的優化,怎麽提高查詢效率
分布式緩存的理解
緩存的穿透和雪崩,解決辦法
redis的排序算法?
redis集群
redis過期策略
Redis如何解決key沖突
redis數據類型+redis是單線程的麽,為什麽呢
redis和memcache區別
redis與mysql的區別以及優缺點
回答存儲機制以及持久化
七、設計模式
單例模式裏面的雙重檢查鎖定的原理,以及為什麽使用volatile
生產者消費者,工廠,說下原理和應用
策略模式
適配器模式,裝飾模式,代理模式
線程池使用了什麽設計模式
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中你擅長的地方
如果學習一門技術,你會怎麽學習
對國內互聯網公司目前的開源生態有沒有什麽了解
舉出三個以上的國內開源框架,越多越好,dubbo、fastjson、sharding-jdbc、Elastic-job...
你對京東的看法
說出三個京東不如淘寶或者天貓的地方
淘寶是C2C,京東和天貓是B2C,淘寶門檻低,種類,國際市場布局
看過啥書。
深入理解Java虛擬機&HEAD FIRST設計模式&高性能MYSQL,看博客比較多,感覺博客更有針對性
京東面經匯總