1. 程式人生 > >京東面經匯總

京東面經匯總

manage 計數器 .exe 程序編譯 mvc sent 適配器 ebo 單例

一、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為什麽不可變

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並發的理解

死鎖,死鎖原因

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

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

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保存數組的值,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保存數組的值,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 有什麽區別?

知道 HTTPS 通信過程嗎?

TCP三次握手

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

技術分享圖片

  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之間可以開始傳輸數據了。

為什麽三次和四次

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的運行流程

  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中你擅長的地方

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

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

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

你對京東的看法

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

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

看過啥書。

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

京東面經匯總