java後端知識點收集整理

一、集合類

  1. ArrayList擴容機制

    • 在沒指定initialCapacity時,就是會使用延遲分配對象數組空間:當第一次插入元素時才分配10(默認)個對象空間。假如有20個數據需要添加,那麼會分別在第一次的時候,將ArrayList的容量變爲10;之後擴容會按照1.5倍增長。也就是當添加第11個數據的時候,Arraylist繼續擴容變爲10*1.5=15;當添加第16個數據時,繼續擴容變爲15 * 1.5 =22個。
    • 指定initialCapacity時,就會直接分配對象數組空間;之後擴容會按照1.5倍增長
  2. StringBuffer 與 StringBuilder 區別

    • StringBuffer 與 StringBuilder 中的方法和功能完全是等價的,

    • 只是StringBuffer 中的方法大都採用了 synchronized 關鍵字進行修飾,因此是線程安全的,而 StringBuilder 沒有這個修飾,可以被認爲是線程不安全的。

    • 在單線程程序下,StringBuilder效率更快,因爲它不需要加鎖,不具備多線程安全。而StringBuffer則每次都需要判斷鎖,效率相對更低.

    • 初始化是:

      1.StringBuffer()的初始容量可以容納16個字符,當該對象的實體存放的字符的長度大於16時,實體容量就自動增加。StringBuffer對象可以通過length()方法獲取實體中存放的字符序列長度,通過capacity()方法來獲取當前實體的實際容量。
      2.StringBuffer(int size)可以指定分配給該對象的實體的初始容量參數爲參數size指定的字符個數。當該對象的實體存放的字符序列的長度大於size個字符時,實體的容量就自動的增加。以便存放所增加的字符。
      3.StringBuffer(String s)可以指定給對象的實體的初始容量爲參數字符串s的長度額外再加16個字符。當該對象的實體存放的字符序列長度大於size個字符時,實體的容量自動的增加,以便存放所增加的字符。

    • 擴容機制

      使用append()方法在字符串後面追加東西的時候,如果長度超過了該字符串存儲空間大小了就需要進行擴容:新容量擴爲大小變成2倍+2,構建新的存儲空間更大的字符串,將舊的複製過去;

  3. 說一說jdk1.8中,對hashMap的優化
    hashmap相關的問題

  4. 說一說jdk1.8中對concurrentHashMap的優化
    JDK1.8 對ConcurrentHashMap的優化
    徹頭徹尾理解 ConcurrentHashMap

  5. Java裏多個Map的性能比較
    Java裏多個Map的性能比較(TreeMap、HashMap、ConcurrentSkipListMap)

  6. 爲什麼重寫equals時必須重寫hashCode方法?
    爲什麼重寫equals時必須重寫hashCode方法

    首先equals與hashcode間的關係是這樣的:
    1、如果兩個對象相同(即用equals比較返回true),那麼它們的hashCode值一定要相同;
    2、如果兩個對象的hashCode相同,它們並不一定相同(即用equals比較返回false)
    自我的理解:
    由於爲了提高程序的效率才實現了hashcode方法,先進行hashcode的比較,如果不同,那沒就不必在進行equals的比較了,這樣就大大減少了equals比較的次數,這對比需要比較的數量很大的效率提高是很明顯的.
    一個很好的例子就是在集合中的使用;
    我們都知道java中的List集合是有序的,因此是可以重複的,而set集合是無序的,因此是不能重複的,那麼怎麼能保證不能被放入重複的元素呢,但靠equals方法一樣比較的話,如果原來集合中以後又10000個元素了,那麼放入10001個元素,難道要將前面的所有元素都進行比較,看看是否有重複,這個效率可想而知,因此hashcode就應遇而生了,java就採用了hash表,利用哈希算法(也叫散列算法),就是將對象數據根據該對象的特徵使用特定的算法將其定義到一個地址上,那麼在後面定義進來的數據只要看對應的hashcode地址上是否有值,那麼就用equals比較,如果沒有則直接插入,只要就大大減少了equals的使用次數,執行效率就大大提高了。
    繼續上面的話題,爲什麼必須要重寫hashcode方法,其實簡單的說就是爲了保證同一個對象,保證在equals相同的情況下hashcode值必定相同。
    如果不重寫equals方法,則equals方法默認使用“==”進行比較兩個對象,即地址比較;
    如果重寫了equals而未重寫hashcode方法,可能就會出現兩個沒有關係的對象equals相同的(因爲equal都是根據對象的特徵進行重寫的),但hashcode確實不相同的。

二、數據庫

  1. 點擊數據庫系統知識整理

  2. mysql的索引,使用B+樹索引的好處
    爲什麼mysql用B+樹做索引而不用B-樹或紅黑樹

  3. mysql性能查看以及如何優化
    性能分析方法、SQL性能優化和MySQL內部配置優化

  4. 說一說事務的ACID,事務的四大隔離機制
    Innodb中的事務隔離級別實現原理

  5. 聚集索引區別

    • 聚集索引
      數據行的物理順序與聚集索引的邏輯順序相同(即,表記錄的排列順序和與聚集索引的排列順序相同),一個表中只能擁有一個聚集索引,所以查詢效率快,只要找到第一個索引值記錄,其餘就連續性的記錄在物理也一樣連續存放。聚集索引對應的缺點就是修改慢,因爲爲了保證表中記錄的物理和索引順序一致,在記錄插入的時候,會對數據頁重新排序。
    • 非聚集索引
      索引的邏輯順序與磁盤上行的物理存儲順序不同,一個表中可以擁有多個非聚集索引。兩種索引都採用B+樹結構,非聚集索引的葉子層並不和實際數據頁相重疊,而採用葉子層包含一個指向表中的記錄在數據頁中的指針方式。非聚集索引層次多,添加和刪除數據時不會造成數據重排。
      聚集索引與非聚集索引的總結

    SQL Sever索引類型有:唯一索引,主鍵索引,聚集索引,非聚集索引。
    MySQL 索引類型有:唯一索引,主鍵(聚集)索引,非聚集索引,全文索引。(主鍵就是聚集索引)

三、多線程

  1. 有多少種方法可以讓線程阻塞,能說多少說多少

    如果線程是因爲調用了wait()、sleep()或者join()方法而導致的阻塞,可以中斷線程,並且通過拋出InterruptedException來喚醒它;如果線程遇到了IO阻塞,無能爲力,因爲IO是操作系統實現的,Java代碼並沒有辦法直接接觸到操作系統。以下是詳細的喚醒方法:
      1. sleep() 方法
      sleep(毫秒),指定以毫秒爲單位的時間,使線程在該時間內進入線程阻塞狀態,期間得不到cpu的時間片,等到時間過去了,線程重新進入可執行狀態。(暫停線程,不會釋放鎖)
      2.suspend() 和 resume() 方法:。
      掛起和喚醒線程,suspend e()使線程進入阻塞狀態,只有對應的resume e()被調用的時候,線程纔會進入可執行狀態。(不建議用,容易發生死鎖)
      3. yield() 方法:
      會使的線程放棄當前分得的cpu時間片,但此時線程任然處於可執行狀態,隨時可以再次分得cpu時間片。yield()方法只能使同優先級的線程有執行的機會。調用 yield()的效果等價於調度程序認爲該線程已執行了足夠的時間從而轉到另一個線程。(暫停當前正在執行的線程,並執行其他線程,且讓出的時間不可知)
      4.wait() 和 notify() 方法
      兩個方法搭配使用,wait()使線程進入阻塞狀態,調用notify()時,線程進入可執行狀態。wait()內可加或不加參數,加參數時是以毫秒爲單位,當到了指定時間或調用notify()方法時,進入可執行狀態。(屬於Object類,而不屬於Thread類,wait()會先釋放鎖住的對象,然後再執行等待的動作。由於wait()所等待的對象必須先鎖住,因此,它只能用在同步化程序段或者同步化方法內,否則,會拋出異常IllegalMonitorStateException.)
      5.join()方法
      也叫線程加入。是當前線程A調用另一個線程B的join()方法,當前線程轉A入阻塞狀態,直到線程B運行結束,線程A才由阻塞狀態轉爲可執行狀態。

  2. 鎖是如何升級的
    鎖升級,其實就是從偏向鎖 < 輕量級鎖(自旋鎖) < 重量級鎖
    https://blog.csdn.net/tongdanping/article/details/79647337
    https://blog.csdn.net/qq838642798/article/details/64439761

  3. 實現消費者生產者模型
    Java 實現生產者 – 消費者模型

  4. 線程間的通信方式
    https://blog.csdn.net/lovewebeye/article/details/79659312

  5. 多線程wait 和 notify的判斷條件(if 和 while)和假死

  • 用while而不用if判斷條件的原因

    經典的生產者和消費模式,使用wait和notify實現,判斷條件爲什麼要用while而不能使用if呢?
    因爲當線程wait之後,又被喚醒的時候,是從wait後面開始執行,而不是又從頭開始執行的,所以如果用if的話,被喚醒之後就不會在判斷if中的條件,而是繼續往下執行了。舉個例子,如果list只是添加了一個數據,而存在兩個消費者被喚醒的話,就會出現溢出的問題了,因爲不會在判斷size是否==0,就直接執行remove了。但是如果使用while的話,從wait下面繼續執行,還會返回執行while的條件判斷,size>0了纔會執行remove操作,所以這個必須使用while,而不能使用if來作爲判斷。
    基於以上認知,下面這個是使用wait和notify函數的規範代碼模板:

synchronized (sharedObject) {   
    while (condition) {   
    	sharedObject.wait();   
        // (Releases lock, and reacquires on wakeup)   
    }   
    // do action based upon condition e.g. take or put into queue   
    sharedObject.notifyAll();
} 
  • wait 和 notify的假死問題

    多線程消費或者多線程生產時候,當調到notify()方法的時候,可能只叫醒同類的線性,即,生產者叫醒了另外一個生產者(或消費者叫醒了另外一個消費者),然後因爲條件沒有發生變化(如:本來一個生產者發現緩衝隊列滿了,調用wait()方法堵塞自己,然後想要notify()喚醒消費者消費是的隊列減少,然後消費者再次notify()消費者進行生產;但生產者調用notify()時喚醒了另外一個生產者2,而不是消費者,這個生產者2被喚醒後判斷while條件,發現隊列還是滿的,又調用wait()堵塞自己,那麼現在所有生產者和消費者都被堵塞了。。。),所以又進入到wait中,造成系統假死狀態。解決辦法把notify()方法改成 notifyAll()

  1. 線程池的submit和execute的區別
    線程池的submit和execute的區別

  2. 線程池的實現原理,拒絕策略的時機
    線程池的實現原理,拒絕策略的時機

四、java虛擬機JVM

  1. Tomcat 類加載器之爲何違背雙親委派模型

  2. 什麼時候觸發MinorGC?什麼時候觸發FullGC?

    觸發MinorGC(Young GC)

    對象在新生代EDen區中分配時,當Eden去沒有足夠空間進行分配時,虛擬機將發起一次Minor GC。

    觸發FullGC

    • 老年代空間不足
      如果創建一個大對象,Eden區域當中放不下這個大對象,會直接保存在老年代當中,如果老年代空間也不足,就會觸發Full GC。爲了避免這種情況,最好就是不要創建太大的對象。
    • 持久代空間不足
      如果有持久代空間的話,系統當中需要加載的類,調用的方法很多,同時持久代當中沒有足夠的空間,就觸發一次Full GC。
    • 年輕代出現空間分配擔保失敗
      空間分配擔保失敗發生在Minor GC, 如果Survivor區當中存活對象的年齡達到了設定值,會就將Survivor區當中的對象拷貝到老年代,如果老年代的空間不足,判斷是否開啓HandlerPromotionFailure(是否允許擔保失敗),沒有開啓直接FullGC;如果開啓了HanlerPromotionFailure, JVM會判斷老年代的最大可用的連續內存空間是否大於歷次晉升到老年代對象的平均大小,如果小於,則直接執行FullGC 。
    • 對象大小大於To Survivor 可用內存,並且大於年老代的空閒空間
      由Eden區、From Survivor 區向To Survivor 區複製時,對象大小大於To Survivor 可用內存,則把該對象轉存到年老代,且年老代的可用內存小於該對象大小,則觸發一次Full GC。
    • 顯示調用System.gc
  3. GCroot可以爲哪些?

    a. java虛擬機棧(棧幀中的本地變量表)中的引用的對象。
    b.方法區中的類靜態屬性引用的對象。
    c.方法區中的常量引用的對象。
    d.本地方法棧中JNI本地方法的引用對象。

五、redis緩存

  1. redis是怎麼做緩存的

  2. redis緩存穿透和雪崩效應
    Redis面試三大知識點:緩存雪崩、緩存穿透、緩存更新

  3. redis的持久化操作
    RDB與AOF

  4. 如何利用redis處理熱點數據

  5. 分佈式鎖怎麼實現
    分佈式鎖的三種實現的對比
    分佈式鎖的實現

  6. 鍵的淘汰策略,過期鍵的清除方式

    • 內存淘汰機制:

      1. volatile-lru:設置了過期時間的鍵空間中,優先移除最近未使用的key。
      2. volatile-random: 在設置了過期時間的鍵空間中,隨機移除某個key。
      3. volatile-ttl:在設置了過期時間的鍵空間中,具有更早過期時間的key優先移除。
      4. allkeys-lru:在所有鍵空間中,優先移除最近未使用的key。
      5. allkeys-random: 在所有鍵空間中,隨機移除某個key。
      6. noeviction:當達到內存限額後,所有引起申請內存的命令會返回錯誤。
    • 過期鍵的清除方式

      1. 定期刪除
      2. 惰性刪除

六、Spring、Hibernate、Mybatis

  1. Java進階面試精選系列:Spring+Hibernate+Mybatis+設計模式

  2. 循環依賴問題
    Spring-bean的循環依賴(單例模式)以及解決方式
    淺談Spring解決循環依賴的三種方式

Prototype作用域的bean會導致在每次對該bean請求(將其注入到另一個bean中,或者以程序的方式調用容器的getBean()方法–只有getbean纔會在容器中生成一個
bean)時都會創建一個新的bean實例。根據經驗,對有狀態的bean應該使用prototype作用域,而對無狀態的bean則應該使用singleton作用域。

  1. spring使用的設計模式
    Spring中涉及的設計模式總結

  2. bean的生命週期

  3. SpringMVC執行流程

    • 客戶端發起一個http請求,web應用服務器接收到這個請求,如果匹配DispatcherServlet的請求映射路徑(在web.xml中指定),web容器將該請求轉交給DispatcherServlet處理。
    • DispatcherServlet接收到這個請求後,根據URL和HandlerMapping的配置找到處理請求的處理器(Handler)。
    • DispatcherServlet根據HandlerMapping得到處理當前請求的Handler後,通過HandlerAdapter對Handler進行封裝,再以統一的適配器接口調用Handler。(HandlerAdapter是一個適配器,它以統一的接口對各種Handler方法進行調用)
    • 處理器完成業務邏輯的處理後將返回一個ModelAndView給DispatcherServlet,ModelAndView包含了視圖邏輯名和邏輯數據信息。
    • DispatcherServlet通過視圖解析器ViewResolver對ModelAndView進行視圖解析,得到真實的視圖對象View。
    • DispatcherServlet對View進行視圖渲染,然後將視圖response到客戶端。
    • 客戶端得到服務器端響應的視圖。
      在這裏插入圖片描述
  4. 淺談Session與Cookie的區別與聯繫
    https://blog.csdn.net/bwh0520/article/details/78808181

七、Hibernate

23.爲什麼要使用 hibernate?

24.什麼是 ORM 框架?

25.hibernate 中如何在控制檯查看打印的 sql 語句?

26.hibernate 有幾種查詢方式?

27.hibernate 實體類可以被定義爲 final 嗎?

28.在 hibernate 中使用 Integer 和 int 做映射有什麼區別?

29.hibernate 是如何工作的?

30.get()和 load()的區別?
Hibernate get和load區別

31.說一下 hibernate 的緩存機制?

32.hibernate 對象有哪些狀態?

33.在 hibernate 中 getCurrentSession 和 openSession 的區別是什麼?

34.hibernate 實體類必須要有無參構造函數嗎?爲什麼?

八、消息隊列

  1. 消息中間件部署及比較
    消息中間件部署及比較:rabbitMQ、activeMQ、zeroMQ、rocketMQ、Kafka、redis
    RabbitMQ和kafka從幾個角度簡單的對比–轉

  2. 消息隊列常見問題
    ++ 消息隊列保證不重複消費
    ++ 如何保證消息的可靠性傳輸?或者說,如何處理消息丟失的問題?
    ++ 如何保證消息的順序性

    RabbitMQ
    拆分多個 queue,每個 queue 一個 consumer,就是多一些 queue 而已,確實是麻煩點;或者就一個 queue 但是對應一個 consumer,然後這個 consumer 內部用內存隊列做排隊,然後分發給底層不同的 worker 來處理。

    ++ 如何保證消息隊列的高可用

九、計算機網絡

https://github.com/huihut/interview

  1. TCP三次連接、四次釋放
    在這裏插入圖片描述
  • 爲什麼要三次握手
  • 爲什麼要四次釋放
  • 爲什麼要傳回 SYN

    接收端傳回發送端所發送的 SYN 是爲了告訴發送端,我接收到的信息確實就是你所發送的信號了。
    SYN 是 TCP/IP 建立連接時使用的握手信號。在客戶機和服務器之間建立正常的 TCP 網絡連接時,客戶機首先發出一個 SYN 消息,服務器使用 SYN-ACK 應答表示接收到了這個消息,最後客戶機再以 ACK(Acknowledgement[漢譯:確認字符 ,在數據通信傳輸中,接收站發給發送站的一種傳輸控制字符。它表示確認發來的數據已經接受無誤。 ])消息響應。這樣在客戶機和服務器之間才能建立起可靠的TCP連接,數據纔可以在客戶機和服務器之間傳遞。
    傳了 SYN,爲啥還要傳 ACK
    雙方通信無誤必須是兩者互相發送信息都無誤。傳了 SYN,證明發送方到接收方的通道沒有問題,但是接收方到發送方的通道還需要 ACK 信號來進行驗證。

  1. 網絡編程nio和netty相關,netty的線程模型,零拷貝實現
    大話 Select、Poll、Epoll
    Linux IO模式及 select、poll、epoll詳解
    epoll比select和poll高效的原因

  2. tcp與http協議的聯繫與區別
    HTTP、TCP和Socket的概念和原理及其區別

  3. tcp的可靠是指什麼
    tcp的可靠是指什麼

  4. 在TCP的三次握手中,後採用隨機產生的初始化序列號進行請求。

    這樣做主要是出於網絡安全的因素着想。
    如果不是隨機產生初始序列號,黑客將會以很容易的方式獲取到你與其他主機之間通信的初始化序列號,並且僞造序列號進行攻擊,這已經成爲一種很常見的網絡攻擊手段。

十、操作系統

  1. 操作系統的用戶態和核心態切換條件以及爲什麼要切換
    操作系統的用戶態和核心態切換條件:中斷
    https://blog.csdn.net/u012333003/article/details/29384807

  2. 操作系統的虛擬內存

  3. 併發容器

十一、算法編程

  1. 二分查找變種
    你真的會寫二分查找嗎

  2. 在紙上寫一個一個鏈表排序

/**
 * Created by 凌 on 2019/1/18.
 * 註釋:148. Sort List
 */
public class SortList {
    /**
     * 將兩個有序鏈表合併
     * 鏈表的二路歸併排序
     * @param head
     * @return
     */
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null){
            return head;
        }
        ListNode slow = head;
        ListNode fast = head.next;

        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }

        ListNode mid = slow.next;
        //將鏈表切斷
        slow.next = null;
        head = sortList(head);//第一條鏈表head - slow
        mid = sortList(mid);//第二條鏈表mid(slow-next) - end
        return mergeSort(head,mid);
    }
    public ListNode mergeSort(ListNode first,ListNode anotherFirst){
        ListNode newHead = new ListNode(-1);
        ListNode curr = newHead;
        while (first != null || anotherFirst != null){
            if (first == null || (anotherFirst != null && first.val >= anotherFirst.val)){
                curr.next = anotherFirst;
                curr = curr.next;
                anotherFirst = anotherFirst.next;
            }else if(anotherFirst == null || (first != null && anotherFirst.val >= first.val)){
                curr.next = first;
                curr = curr.next;
                first = first.next;
            }
        }
        return newHead.next;
    }
}
  1. 在紙上寫一個Binary Search Tree的建立函數
package com.practice.binarytree;

/**
 * Created by 凌 on 2019/3/19.
 * 描述:構建二叉查找樹
 */
public class BinarySearchTree {
    class TreeNode {
        int data;
        TreeNode left;//左節點
        TreeNode right;//右節點

        public TreeNode() {
        }

        public TreeNode(int data) {
            this.data = data;
            this.left = null;
            this.right = null;
        }
    }

    /**
     * 構建二叉查找樹
     * @param root
     * @param data
     */
    TreeNode insert(TreeNode root, int data){
        if (root == null){
            root = new TreeNode(data);
            return root;
        }
        if (root.data < data){
            root.right = insert(root.right, data);
        }else {
            root.left = insert(root.left, data);
        }
        return root;
    }

    /**
     * 中序遍歷,如果是二叉查找樹,那麼遍歷完可以得到有序的序列
     * @param root
     */
    void inOrderTranversal(TreeNode root){
        if (root == null){
            return;
        }
        inOrderTranversal(root.left);
        System.out.printf(root.data + "\t");
        inOrderTranversal(root.right);
    }

    /**
     * 二叉樹查找
     * @param root
     * @param data
     * @return
     */
    boolean search(TreeNode root, int data){
        if (root == null){
            return false;
        }
        if (root.data == data){
            return true;
        }else if (root.data < data){
            return search(root.right, data);
        }else{
            return search(root.left, data);
        }
    }

    public static void main(String[] args) {
        int[] nums = {3, 6, 1, 8, 5, 2};
        BinarySearchTree binarySearchTree = new BinarySearchTree();
        TreeNode root = null;
        for (int i = 0; i < nums.length; i++) {
            root = binarySearchTree.insert(root, nums[i]);
        }
        System.out.println("中序遍歷二叉排序樹:");
        binarySearchTree.inOrderTranversal(root);
        System.out.println();

        int data = 5;
        System.out.printf("查找是否存在值爲%d的節點:\n",data);
        boolean isExist = binarySearchTree.search(root, data);
        System.out.println(isExist);
    }
}

  1. 二叉樹中兩個節點的最近公共祖先節點
    二叉樹中兩個節點的最近公共祖先節點
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章