最新Java面試題和答案

Java基礎

1. JDK 和 JRE 有什麼區別?

JDK:Java Development Kit 的簡稱,java 開發工具包,提供了 java 的開發環境和運行環境。
JRE:Java Runtime Environment 的簡稱,java 運行環境,爲 java 的運行提供了所需環境。
具體來說 JDK 其實包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了很多 java 程序調試和分析的工具。簡單來說:如果你需要運行 java 程序,只需安裝 JRE 就可以了,如果你需要編寫 java 程序,需要安裝 JDK。

2. == 和 equals 的區別是什麼?
== 解讀
對於基本類型和引用類型 == 的作用效果是不同的,如下所示:
基本類型:比較的是值是否相同;
引用類型:比較的是引用是否相同;
代碼示例:

String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

代碼解讀:因爲 x 和 y 指向的是同一個引用,所以 == 也是 true,而 new String()方法則重寫開闢了內存空間,所以 == 結果爲 false,而 equals 比較的一直是值,所以結果都爲 true。

equals 解讀
equals 本質上就是 ==,只不過 String 和 Integer 等重寫了 equals 方法,把它變成了值比較。看下面的代碼就明白了。
首先來看默認情況下 equals 比較一個有相同值的對象,代碼如下:

class Cat {
    public Cat(String name) {
        this.name = name;
    }
 
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
 
Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false

輸出結果出乎我們的意料,竟然是 false?這是怎麼回事,看了 equals 源碼就知道了,源碼如下:

public boolean equals(Object obj) {
    return (this == obj);
}

原來equals 本質上就是 ==
那問題來了,兩個相同值的 String 對象,爲什麼返回的是 true?代碼如下:

String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

同樣的,當我們進入 String 的 equals 方法,找到了答案,代碼如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

原來是 String 重寫了 Object 的 equals 方法,把引用比較改成了值比較。

總結 :== 對於基本類型來說是值比較,對於引用類型來說是比較的是引用;而 equals 默認情況下是引用比較,只是很多類重新了 equals 方法,比如 String、Integer 等把它變成了值比較,所以一般情況下 equals 比較的是值是否相等。

3. 兩個對象的 hashCode()相同,則 equals()也一定爲 true,對嗎?
不對,兩個對象的 hashCode()相同,equals()不一定 true。
代碼示例:

String str1 = "通話";
String str2 = "重地";
System.out.println(String.format("str1:%d | str2:%d",  str1.hashCode(),str2.hashCode()));
System.out.println(str1.equals(str2));

執行的結果:

str1:1179395 | str2:1179395
false

代碼解讀:很顯然“通話”和“重地”的 hashCode() 相同,然而 equals() 則爲 false,因爲在散列表中,hashCode()相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不一定能得出鍵值對相等。

4. final 在 java 中有什麼作用?

final 修飾的類叫最終類,該類不能被繼承。
final 修飾的方法不能被重寫。
final 修飾的變量叫常量,常量必須初始化,初始化之後值就不能被修改。

5. Java的重載和重寫的區別?

  • 重寫:父類中定義的方法,在子類中重新實現。
  • 重載:相同方法名稱相同,參數類型、參數個數、參數順序不同。重載對返回類型沒有要求,可以相同也可以不同,但不能通過返回類型是否相同來判斷重載。

6. Java面向對象的三大特徵

封裝、繼承、多態

7. String 屬於基礎的數據類型嗎?

String 不屬於基礎類型,基礎類型有 8 種:byte、boolean、char、short、int、float、long、double,而 String 屬於對象。

8. java 中操作字符串都有哪些類?它們之間有什麼區別?

操作字符串的類有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的區別在於 String 聲明的是不可變的對象,每次操作都會生成新的 String 對象,然後將指針指向新的 String 對象,而 StringBuffer、StringBuilder 可以在原有對象的基礎上進行操作,所以在經常改變字符串內容的情況下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的區別在於,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高於 StringBuffer,所以在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。

9. String str="i"與 String str=new String(“i”)一樣嗎?

不一樣,因爲內存的分配方式不一樣。String str="i"的方式,java 虛擬機會將其分配到常量池中;而 String str=new String(“i”) 則會被分到堆內存中。

10. 抽象類必須要有抽象方法嗎?
不需要,抽象類不一定非要有抽象方法。
示例代碼:

abstract class Cat {
    public static void sayHi() {
        System.out.println("hi~");
    }
}

上面代碼,抽象類並沒有抽象方法但完全可以正常運行。
11. 普通類和抽象類有哪些區別?

普通類不能包含抽象方法,抽象類可以包含抽象方法。
抽象類不能直接實例化,普通類可以直接實例化。

12. 抽象類能使用 final 修飾嗎?

不能,定義抽象類就是讓其他類繼承的,如果定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,所以 final 不能修飾抽象類,如下圖所示,編輯器也會提示錯誤信息:

13. 接口和抽象類有什麼區別?

實現:抽象類的子類使用 extends 來繼承;接口必須使用 implements 來實現接口。
構造函數:抽象類可以有構造函數;接口不能有。
main 方法:抽象類可以有 main 方法,並且我們能運行它;接口不能有 main 方法。
實現數量:類可以實現很多個接口;但是隻能繼承一個抽象類。
訪問修飾符:接口中的方法默認使用 public 修飾;抽象類中的方法可以是任意訪問修飾符。

14. java 中 IO 流分爲幾種?

按功能來分:輸入流(input)、輸出流(output)。
按類型來分:字節流和字符流。
字節流和字符流的區別是:字節流按 8 位傳輸以字節爲單位輸入輸出數據,字符流按 16 位傳輸以字符爲單位輸入輸出數據。

15. BIO、NIO、AIO 有什麼區別?

BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,併發處理能力低。
NIO:New IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端通過 Channel(通道)通訊,實現了多路複用。
AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操作基於事件和回調機制。


集合

1. 常用的集合類型有哪些?這些集合的區別?
在這裏插入圖片描述
在這裏插入圖片描述

答:常用集合類型:List、Set、Map、Stack、Queue。
區別:1. List、Set、Stack和Queue是存儲單列數據的集合,Map是存儲鍵值對的雙列數據的集合;
List、Set和Queue集合是實現的Collection接口,Stack是繼承的Vector,Map是單獨實現的一個接口。
List中存儲的數據是有順序的,並且值允許重複;Map中存儲的數據是無序的,它的鍵是不允許重複的,但是值是允許重複的;Set中存儲的數據是無順序的,並且不允許重複。

2. 集合中常用的實現類?

  • List:
    1. Vector:數組數據結構,對數組的操作都加上了synchronized,線程安全。增刪,查詢都很慢。
    2. ArrayList:數組數據結構,線程不安全,替代Vector,查詢速度快,增刪速度慢。
    3. LinkedList:數據結構,線程不安全,增刪速度快,查詢速度慢。
  • Set:
    1. HashSet:使用HashMap的key進行存儲,所以不允許重複。運行爲null。通過hash算法,排列順序和插入順序無關。
    2. LinkedHashSet:使用HashMap的key進行存儲,用鏈表實現,排列順序就是插入順序。
    3. TreeSet:使用NavigableMap的key進行存儲,底層使用紅黑樹進行排序。
  • Map:
    1. HashMap:jdk1.7底層使用數組+單鏈表的形式存儲數據,而jdk1.8是用數組+單鏈表+紅黑樹存儲數據。線程不安全。兩倍擴容。無序,key不允許重複,key和value允許爲null。
    2. Hahtable:數組+單鏈表實現。線程安全。兩倍+1擴容。無序,key不允許重複。key和value都不允許爲null。
    3. ConcurrentHashMap:存儲方式和HashMap一樣。通過把整個Map分爲N個Segment,通過用ReentrantLock重入鎖對寫操作的segmet加鎖,來保證線程安全,但是效率提升N倍,默認提升16倍。
    4. TreeMap:可以指定比較器(comparator)排序的Map集合,按集合的key排序,key不允許重複。
    5. WeakHashMap:弱引用的Map集合,清除集合中不再使用的內容,使用gc進行回收:WeakHashMap的鍵是“弱鍵”。在 WeakHashMap 中,當某個鍵不再正常使用時,會被從WeakHashMap中被自動移除,並把key值放入ReferenceQueue中。
    6. IndentityHashMap:key可以重複的Map集合:比較鍵(和值)時使用引用相等性代替對象相等性,也就是說使用 == 而不是使用 equals。
  • Queue
    1.PriorityQueue:數組實現。取出的順序並不是加入隊列的順序,而是按元素節點大小排序後的順序。
    2.ArrayDeque:數組實現的雙端隊列。
    3.BlockingQueue:阻塞隊列,注意它是在java.util.concurrent包下。
  • Stack:繼承Vector類的棧對象。

3. Jdk1.8中HashMap中鏈表長度超過多少個節點會轉成紅黑樹?爲什麼是這個節點數量,而不設置成更大或更小?
鏈表元素個數大於等於8會轉成紅黑樹,鏈表元素個數小於等於6時,樹結構還原成鏈表,以達到提高查詢效率。
原因:
1.我們知道鏈表的時間複雜度是O(n),紅黑樹的時間複雜度O(logn),很明顯紅黑樹複雜度比鏈表複雜度低,至於爲什麼不之間使用紅黑樹是因爲樹節點所佔空間是普通節點的兩倍,而且節點數量小於8時,它們之間的查找效率相差並不大。
2.至於爲什麼是8,源碼上說,爲了配合使用分佈良好的hashCode,樹節點很少使用。並且在理想狀態下,受隨機分佈的hashCode影響,鏈表中的節點遵循泊松分佈,而且根據統計,鏈表中節點數是8的概率已經接近千分之一,而且此時鏈表的性能已經很差了。所以在這種比較罕見和極端的情況下,纔會把鏈表轉變爲紅黑樹。
4. HashMap的實現原理?
1.首先從構造函數說起,HashMap有兩個主要的構造函數,當new 一個對象時傳入初始化容量的時候會進行初始化工作,如果使用無參構造函數則就把負載因子設爲初始值如下:

//定義初始化容量的,會進行數組和一些參數的初始化
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
//無參構造函數,則什麼也不會做
public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
 //真正的初始化構造函數
 public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
  1. 當put一個元素的時候,首先會調用hash(Object key),計算key的hash值。
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  1. 然後調用putVal()方法。首先判斷定義的數組是否爲null或者長度是否爲零,如果是就會調用resize()方法進行數據擴容,來完成數組的初始化工作。
  2. 通過(表長度-1)與hash值((length - 1) & hash)計算key在數組的下標,如果在該下標的數組值爲空,則直接new一個節點存入數組。
  3. 如果不爲空,判斷這個位置元素的key是否與加入的key相等,如果一樣只進行值更新。
  4. 如果不相等,判斷這個位置的元素是不是紅黑樹節點,如果是就調用putTreeVal()函數進行添加元素。
  5. 如果不是紅黑樹節點,說明是一個單向鏈表,則通過循環將節點加入到鏈表表尾,並判斷節點長度大於等於8時,把鏈表轉成紅黑樹。

5. HashMap中hash(Object key),爲什麼(hashcode >>> 16)
首先hashcode >>> 16是取hash值的高16位
由於和(length-1)運算,length 絕大多數情況小於2的16次方。所以始終是hashcode 的低16位(甚至更低)參與運算。要是高16位也參與運算,會讓得到的下標更加散列
所以這樣高16位是用不到的,如何讓高16也參與運算呢。所以纔有hash(Object key)方法。讓他的hashCode()和自己的高16位^運算。所以(h >>> 16)得到他的高16位與hashCode()進行^運算。

線程

1. 並行和併發有什麼區別?

並行是指兩個或者多個事件在同一時刻發生;而併發是指兩個或多個事件在同一時間間隔發生。
並行是在不同實體上的多個事件,併發是在同一實體上的多個事件。
在一臺處理器上“同時”處理多個任務,在多臺處理器上同時處理多個任務。如hadoop分佈式集羣。
所以併發編程的目標是充分的利用處理器的每一個核,以達到最高的處理性能。

2. 線程和進程的區別?

進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程中擁有獨立的內存單元,而多個線程共享內存資源,減少切換次數,從而效率更高。線程是進程的一個實體,是cpu調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間可以併發執行。

3. 線程池都有哪些狀態?這些狀態之間的關係?
在這裏插入圖片描述
詳細講解點擊

4. 創建線程有哪幾種方式?

①. 繼承Thread類創建線程類
定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱爲執行體。
創建Thread子類的實例,即創建了線程對象。
調用線程對象的start()方法來啓動該線程。
②. 通過Runnable接口創建線程類
定義runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
創建 Runnable實現類的實例,並依此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象。
調用線程對象的start()方法來啓動該線程。
③. 通過Callable和Future創建線程
創建Callable接口的實現類,並實現call()方法,該call()方法將作爲線程執行體,並且有返回值。
創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
使用FutureTask對象作爲Thread對象的target創建並啓動新線程。
調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值。

5. Runnable 和 Callable 有什麼區別?

1.Runnable的線程執行代碼在run()方法中,Callable的線程執行代碼在call()方法中。
2.run()方法不可以拋出異常,call()方法可以拋出異常。
3.Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的代碼而已;Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取異步執行的結果。

6. sleep() 和 wait() 有什麼區別?

sleep()和wait()都是線程暫停執行的方法。
1、這兩個方法來自不同的類分別是Thread和Object,sleep方法屬於Thread類中的靜態方法,wait屬於Object的成員方法。
2、sleep()是線程類(Thread)的方法,不涉及線程通信,調用時會暫停此線程指定的時間,但監控依然保持,不會釋放對象鎖,到時間自動恢復;wait()是Object的方法,用於線程間的通信,調用時會放棄對象鎖,進入等待隊列,待調用notify()/notifyAll()喚醒指定的線程或者所有線程,才進入對象鎖定池準備獲得對象鎖進入運行狀態。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用(使用範圍)。
4、sleep()方法必須捕獲異常InterruptedException,而wait()\notify()以及notifyAll()不需要捕獲異常.
**注意**
  sleep方法只讓出了CPU,而並不會釋放同步資源鎖。
  線程執行sleep()方法後會轉入阻塞狀態。
  sleep()方法指定的時間爲線程不會運行的最短時間。因此,sleep()方法不能保證該線程睡眠到期後就開始執行。
  notify的作用相當於叫醒睡着的人,而並不會給他分配任務,就是說notify只是讓之前調用wait的線程有權利重新參與線程的調度。

7. Thread的join()和yield()的區別?

join()方法使調用該方法的線程在此(或之前)執行完畢,也就是等待調用join()的線程執行完run()方法後再往下繼續執行。注意該方法也需要捕捉異常。就是說讓該線程在執行完RUN()方法以後再執行join方法後面的代碼,就是說可以讓兩個線程合併起來,用於實現同步功能。
yield()該方法與sleep() 類似 只不過不能夠由用戶指定暫停多長的時間,並且調用yield()方法的對象線程會直接進入就緒狀態,只能讓同優先級的線程有執行的機會。 前面提到了 sleep不會釋放鎖標識yield也不會釋放鎖標識。

8. 創建線程池有哪幾種方式?

Java通過Executors(jdk1.5併發包)提供四種線程池,分別爲:
newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。
newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。

詳解點擊查看
9. 如果線程池的任務隊列滿了,該怎麼處理提交的任務?

ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新提交被拒絕的任務
ThreadPoolExecutor.CallerRunsPolicy:由調用線程(提交任務的線程)處理該任務

需要注意的是:線程池默認的拒絕策略是AbortPolicy
10. 線程池中 submit()和 execute()方法有什麼區別?

1.接收的參數不一樣:submit(Callable task)、submit(Runnable task, T result)、submit(Runnable task)歸屬於ExecutorService接口。execute(Runnable command)歸屬於Executor接口。ExecutorService繼承了Executor。
2.submit有返回值,而execute沒有。
3.submit方便Exception處理。

11. ThreadLocal是什麼?

ThreadLocal提供了線程內存儲變量的能力,這些變量不同之處在於每一個線程讀取的變量是對應的互相獨立的。通過get和set方法就可以得到當前線程對應的值。
做個不恰當的比喻,從表面上看ThreadLocal相當於維護了一個map,key就是當前的線程,value就是需要存儲的對象。
這裏的這個比喻是不恰當的,實際上是ThreadLocal的靜態內部類ThreadLocalMap爲每個Thread都維護了一個數組table,ThreadLocal確定了一個數組下標,而這個下標就是value存儲的對應位置。

詳解點擊鏈接

同步

1. synchronized 底層實現原理?

我們都知道,對象被創建在堆中。並且對象在內存中的存儲佈局方式可以分爲3塊區域:對象頭實例數據對齊填充。其中對象頭,便是我們今天的主角。
對於對象頭來說,主要是包括倆部分信息:
在這裏插入圖片描述
1、對象標記也稱Mark Word字段,存儲當前對象的一些運行時數據,比如:鎖狀態標誌、線程持有的鎖…等等。
在這裏插入圖片描述
今天我們只聊:指向重量級鎖的指針
2、另一部分是類型指針:JVM通過這個指針來確定這個對象是哪個類的實例。
synchronized鎖的宏觀實現
synchronized的對象鎖,其指針指向的是一個monitor對象(由C++實現)的起始地址。每個對象實例都會有一個 monitor。其中monitor可以與對象一起創建、銷燬;亦或者當線程試圖獲取對象鎖時自動生成。
monitor是由ObjectMonitor實現(ObjectMonitor.hpp文件,C++實現的),對於我們來說主要關注的是如下代碼:
在這裏插入圖片描述
我們可以看到這裏定義了_WaitSet 和 _EntryList倆個隊列,其中_WaitSet 用來保存每個等待鎖的線程對象。
那_EntryList呢?
彆着急,讓我們先看一下_owner,它指向持有ObjectMonitor對象的線程。當多個線程同時訪問一段同步代碼時,會先存放到 _EntryList 集合中,接下來當線程獲取到對象的monitor時,就會把_owner變量設置爲當前線程。同時count變量+1。如果線程調用wait() 方法,就會釋放當前持有的monitor,那麼_owner變量就會被置爲null,同時_count減1,並且該線程進入 WaitSet集合中,等待下一次被喚醒。
當然,若當前線程順利執行完方法,也將釋放monitor,重走一遍剛纔的內容,也就是_owner變量就會被置爲null,同時_count減1,並且該線程進入 WaitSet集合中,等待下一次被喚醒。
因爲這個鎖對象存放在對象本身,也就是爲什麼Java中任意對象可以作爲鎖的原因

詳解點擊鏈接

2. synchronized 和 volatile 的區別是什麼?

1.volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取; synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。
2.volatile僅能使用在變量級別;synchronized則可以使用在變量、方法、和類級別的。
3.volatile僅能實現變量的修改可見性,不能保證原子性;而synchronized則可以保證變量的修改可見性和原子性。
4.volatile不會造成線程的阻塞;synchronized可能會造成線程的阻塞。
5.volatile標記的變量不會被編譯器優化;synchronized標記的變量可以被編譯器優化。

詳解點擊鏈接

3. synchronized 和 Lock 有什麼區別?

首先synchronized是java內置關鍵字,在jvm層面,Lock是個java類;
synchronized無法判斷是否獲取鎖的狀態,Lock可以判斷是否獲取到鎖;
synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;
用synchronized關鍵字的兩個線程1和線程2,如果當前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結束了;
synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可);
Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。

4. synchronized 和 ReentrantLock 區別是什麼?

synchronized是和if、else、for、while一樣的關鍵字,ReentrantLock是類,這是二者的本質區別。既然ReentrantLock是類,那麼它就提供了比synchronized更多更靈活的特性,可以被繼承、可以有方法、可以有各種各樣的類變量,ReentrantLock比synchronized的擴展性體現在幾點上:
ReentrantLock可以對獲取鎖的等待時間進行設置,這樣就避免了死鎖
ReentrantLock可以獲取各種鎖的信息
ReentrantLock可以靈活地實現多路通知
另外,二者的鎖機制其實也是不一樣的:ReentrantLock底層調用的是Unsafe的park方法加鎖,synchronized操作的應該是對象頭中mark word。

5. 鎖的類型都有哪些?
詳情點擊鏈接
6. 說一下CAS,及CAS有什麼問題?

CAS,是Java保證原子性的一種重要方法,也是一種樂觀鎖的實現方式。
它需要先提前一步獲取舊值,然後進入此方法比較當下的值是否與舊值相同,如果相同,則更新數據,否則退出方法,重複一遍剛纔的動作。由此可見,CAS方法是非堵塞的。CAS方法需要三個參數,變量內存值、舊的預期值、數據更新值;
問題:
1、ABA問題,比如剛開始讀取到備份是3,然後被其他線程連續修改兩次,最終結果還是3,那麼CAS很可能識別不到數據發生了改變,這種情況對程序造成了極大的安全隱患。可以通過添加版本號等標誌位來解決該問題。
2、循環時間長開銷大,如果長時間自旋不成功,會給CPU帶來很大開銷。可以使用自適應自旋鎖解決這個問題
3、只能保證一個共享變量的原子操作。比如AtomicInteger都是每次只能對一個變量進行原子性控制。

7. AQS是什麼?
AQS是一個抽象類AbstractQueuedSynchronizer(抽象隊列同步器)。它是基於等待隊列用來實現同步鎖(ReentrantLock,Semaphore,CountdownLatch,CyclicBarrier,Exchanger等等)核心組件的基礎框架,它本身沒有實現任何的同步接口,只是定義了獲取以及釋放同步狀態的方法來提供自定義的同步組件。
詳解點擊鏈接
8. 分佈式鎖實現的方式?

詳解點擊鏈接

9. 說一下 atomic 的原理?

Atomic包中的類基本的特性就是在多線程環境下,當有多個線程同時對單個(包括基本類型及引用類型)變量進行操作時,具有排他性,即當多個線程同時對該變量的值進行更新時,僅有一個線程能成功,而未成功的線程可以向自旋鎖一樣,繼續嘗試,一直等到執行成功。
Atomic系列的類中的核心方法都會調用unsafe類中的幾個本地方法。我們需要先知道一個東西就是Unsafe類,全名爲:sun.misc.Unsafe,這個類包含了大量的對C代碼的操作,包括很多直接內存分配以及原子操作的調用,而它之所以標記爲非安全的,是告訴你這個裏面大量的方法調用都會存在安全隱患,需要小心使用,否則會導致嚴重的後果,例如在通過unsafe分配內存的時候,如果自己指定某些區域可能會導致一些類似C++一樣的指針越界到其他進程的問題。


類加載

1. 講講類的實例化順序,比如父類靜態數據,構造函數,字段,子類靜態數據,構造函數,字段,當 new 的時候, 他們的執行順序。
此題考察的是類加載器實例化時進行的操作步驟(加載–>連接->初始化)。
父類靜態代變量、
父類靜態代碼塊、
子類靜態變量、
子類靜態代碼塊、
父類非靜態變量(父類實例成員變量)、
父類構造函數、
子類非靜態變量(子類實例成員變量)、
子類構造函數。

2. 類加載的流程,類加載器都有哪些?它們之間的關係和負責加載的內容?

詳解點擊鏈接

3. 類加載機制有哪些?

  • 全盤負責委託機制:當一個ClassLoader加載一個類時,除非顯示的使用另一個ClassLoader,否則該類所依賴和引用的類也都由這個ClassLoader加載。
  • 雙親委派機制:指先委託父類加載器尋找目標類,在找不到的情況下再在自己的路徑中查找並載入目標類。

JVM

JVM常問面試題


JavaWeb

1. jsp的內置對象。

  • pageContext:JSP的頁面容器
  • request : 獲取用戶的請求信息
  • response: 服務器向客戶端的迴應信息
  • session :用來保存每一個用戶的信息
  • application:表示所有用戶的共享信息
  • config : 服務器配置信息,可以取得初始化參數
  • out : 用於向客戶端、瀏覽器輸出數據。
  • page:指向了當前jsp程序本身。
  • exception:封裝了jsp程序執行過程中發生的異常和錯誤信息。

2. 四種屬性範圍:

  • page(pageContext):只在一個頁面中保存屬性。 跳轉之後無效;
  • request:只在一次請求中有效,服務器跳轉之後有效。 客戶端跳無效;
  • session:再一次會話中有效。服務器跳轉、客戶端跳轉都有效。 網頁關閉重新打開無效;
  • application:在整個服務器上保存,所有用戶都可使用。 重啓服務器後無效;

3. session 和 cookie 有什麼區別?

由於HTTP協議是無狀態的協議,所以服務端需要記錄用戶的狀態時,就需要用某種機制來識具體的用戶,這個機制就是Session.典型的場景比如購物車,當你點擊下單按鈕時,由於HTTP協議無狀態,所以並不知道是哪個用戶操作的,所以服務端要爲特定的用戶創建了特定的Session,用用於標識這個用戶,並且跟蹤用戶,這樣才知道購物車裏面有幾本書。這個Session是保存在服務端的,有一個唯一標識。在服務端保存Session的方法很多,內存、數據庫、文件都有。集羣的時候也要考慮Session的轉移,在大型的網站,一般會有專門的Session服務器集羣,用來保存用戶會話,這個時候 Session 信息都是放在內存的,使用一些緩存服務比如Memcached之類的來放 Session。
思考一下服務端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會發送相應的Cookie信息到服務端。實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次創建Session的時候,服務端會在HTTP協議中告訴客戶端,需要在 Cookie 裏面記錄一個Session ID,以後每次請求把這個會話ID發送到服務器,我就知道你是誰了。有人問,如果客戶端的瀏覽器禁用了 Cookie 怎麼辦?一般這種情況下,會使用一種叫做URL重寫的技術來進行會話跟蹤,即每次HTTP交互,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的參數,服務端據此來識別用戶。
Cookie其實還可以用在一些方便用戶的場景下,設想你某次登陸過一個網站,下次登錄的時候不想再次輸入賬號了,怎麼辦?這個信息可以寫到Cookie裏面,訪問網站的時候,網站頁面的腳本可以讀取這個信息,就自動幫你把用戶名給填了,能夠方便一下用戶。這也是Cookie名稱的由來,給用戶的一點甜頭。所以,總結一下:Session是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據可以保存在集羣、數據庫、文件中;Cookie是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式。

4. 說一下 session 的工作原理?

其實session是一個存在服務器上的類似於一個散列表格的文件。裏面存有我們需要的信息,在我們需要用的時候可以從裏面取出來。類似於一個大號的map吧,裏面的鍵存儲的是用戶的sessionid,用戶向服務器發送請求的時候會帶上這個sessionid。這時就可以從中取出對應的值了。

5. 如果客戶端禁止 cookie 能實現 session 還能用嗎?

Cookie與 Session,一般認爲是兩個獨立的東西,Session採用的是在服務器端保持狀態的方案,而Cookie採用的是在客戶端保持狀態的方案。但爲什麼禁用Cookie就不能得到Session呢?因爲Session是用Session ID來確定當前對話所對應的服務器Session,而Session ID是通過Cookie來傳遞的,禁用Cookie相當於失去了Session ID,也就得不到Session了。
假定用戶關閉Cookie的情況下使用Session,其實現途徑有以下幾種:
1.設置php.ini配置文件中的“session.use_trans_sid = 1”,或者編譯時打開打開了“–enable-trans-sid”選項,讓PHP自動跨頁傳遞Session ID。
2.手動通過URL傳值、隱藏表單傳遞Session ID。
3.用文件、數據庫等形式保存Session ID,在跨頁過程中手動調用。

6.forward 和 redirect 的區別?

Forward和Redirect代表了兩種請求轉發方式:直接轉發和間接轉發。
直接轉發方式(Forward),客戶端和瀏覽器只發出一次請求,Servlet、HTML、JSP或其它信息資源,由第二個信息資源響應該請求,在請求對象request中,保存的對象對於每個信息資源是共享的。
間接轉發方式(Redirect)實際是兩次HTTP請求,服務器端在響應第一次請求的時候,讓瀏覽器再向另外一個URL發出請求,從而達到轉發的目的。
舉個通俗的例子:
直接轉發就相當於:“A找B借錢,B說沒有,B去找C借,借到借不到都會把消息傳遞給A”;
間接轉發就相當於:“A找B借錢,B說沒有,讓A去找C借”。

7. 簡述 tcp 和 udp的區別?

TCP面向連接(如打電話要先撥號建立連接);UDP是無連接的,即發送數據之前不需要建立連接。
TCP提供可靠的服務。也就是說,通過TCP連接傳送的數據,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付。
Tcp通過校驗和,重傳控制,序號標識,滑動窗口、確認應答實現可靠傳輸。如丟包時的重發控制,還可以對次序亂掉的分包進行順序控制。
UDP具有較好的實時性,工作效率比TCP高,適用於對高速傳輸和實時性有較高的通信或廣播通信。
每一條TCP連接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通信。
TCP對系統資源要求較多,UDP對系統資源要求較少。

8. tcp 爲什麼要三次握手,兩次不行嗎?爲什麼?

爲了實現可靠數據傳輸, TCP 協議的通信雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程即是通信雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟。
如果只是兩次握手, 至多隻有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認。

9. 說一下 tcp 粘包是怎麼產生的?

①. 發送方產生粘包在這裏插入圖片描述
採用TCP協議傳輸數據的客戶端與服務器經常是保持一個長連接的狀態(一次連接發一次數據不存在粘包),雙方在連接不斷開的情況下,可以一直傳輸數據;但當發送的數據包過於的小時,那麼TCP協議默認的會啓用Nagle算法,將這些較小的數據包進行合併發送(緩衝區數據發送是一個堆壓的過程);這個合併過程就是在發送緩衝區中進行的,也就是說數據發送出來它已經是粘包的狀態了。
②. 接收方產生粘包在這裏插入圖片描述
接收方採用TCP協議接收數據時的過程是這樣的:數據到底接收方,從網絡模型的下方傳遞至傳輸層,傳輸層的TCP協議處理是將其放置接收緩衝區,然後由應用層來主動獲取(C語言用recv、read等函數);這時會出現一個問題,就是我們在程序中調用的讀取數據函數不能及時的把緩衝區中的數據拿出來,而下一個數據又到來並有一部分放入的緩衝區末尾,等我們讀取數據時就是一個粘包。(放數據的速度 > 應用層拿數據速度)

10. OSI 的七層模型都有哪些?

應用層:網絡服務與最終用戶的一個接口。
表示層:數據的表示、安全、壓縮。
會話層:建立、管理、終止會話。
傳輸層:定義傳輸數據的協議端口號,以及流控和差錯校驗。
網絡層:進行邏輯地址尋址,實現不同網絡之間的路徑選擇。
數據鏈路層:建立邏輯連接、進行硬件地址尋址、差錯校驗等功能。
物理層:建立、維護、斷開物理連接。

11. get 和 post 請求有哪些區別?

GET在瀏覽器回退時是無害的,而POST會再次提交請求。
GET產生的URL地址可以被Bookmark,而POST不可以。
GET請求會被瀏覽器主動cache,而POST不會,除非手動設置。
GET請求只能進行url編碼,而POST支持多種編碼方式。
GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留。
GET請求在URL中傳送的參數是有長度限制的,而POST麼有。
對參數的數據類型,GET只接受ASCII字符,而POST沒有限制。
GET比POST更不安全,因爲參數直接暴露在URL上,所以不能用來傳遞敏感信息。
GET參數通過URL傳遞,POST放在Request body中。

12. 如何實現跨域?
方式一:圖片ping或script標籤跨域
圖片ping常用於跟蹤用戶點擊頁面或動態廣告曝光次數。
script標籤可以得到從其他來源數據,這也是JSONP依賴的根據。

方式二:JSONP跨域
JSONP(JSON with Padding)是數據格式JSON的一種“使用模式”,可以讓網頁從別的網域要數據。根據 XmlHttpRequest 對象受到同源策略的影響,而利用 script元素的這個開放策略,網頁可以得到從其他來源動態產生的JSON數據,而這種使用模式就是所謂的 JSONP。用JSONP抓到的數據並不是JSON,而是任意的JavaScript,用 JavaScript解釋器運行而不是用JSON解析器解析。所有,通過Chrome查看所有JSONP發送的Get請求都是js類型,而非XHR。 在這裏插入圖片描述缺點:
只能使用Get請求
不能註冊success、error等事件監聽函數,不能很容易的確定JSONP請求是否失敗
JSONP是從其他域中加載代碼執行,容易受到跨站請求僞造的攻擊,其安全性無法確保

方式三:CORS
Cross-Origin Resource Sharing(CORS)跨域資源共享是一份瀏覽器技術的規範,提供了 Web 服務從不同域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,確保安全的跨域數據傳輸。現代瀏覽器使用CORS在API容器如XMLHttpRequest來減少HTTP請求的風險來源。與 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。服務器一般需要增加如下響應頭的一種或幾種:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

跨域請求默認不會攜帶Cookie信息,如果需要攜帶,請配置下述參數:

"Access-Control-Allow-Credentials": true
// Ajax設置
"withCredentials": true

方式四:window.name+iframe
window.name通過在iframe(一般動態創建i)中加載跨域HTML文件來起作用。然後,HTML文件將傳遞給請求者的字符串內容賦值給window.name。然後,請求者可以檢索window.name值作爲響應。

  • iframe標籤的跨域能力;
  • window.name屬性值在文檔刷新後依舊存在的能力(且最大允許2M左右)。

每個iframe都有包裹它的window,而這個window是top window的子窗口。contentWindow屬性返回iframe元素的Window對象。你可以使用這個Window對象來訪問iframe的文檔及其內部DOM。

<!-- 
 下述用端口 
 10000表示:domainA
 10001表示:domainB
-->
 
<!-- localhost:10000 -->
<script>
  var iframe = document.createElement('iframe');
  iframe.style.display = 'none'; // 隱藏
 
  var state = 0; // 防止頁面無限刷新
  iframe.onload = function() {
      if(state === 1) {
          console.log(JSON.parse(iframe.contentWindow.name));
          // 清除創建的iframe
          iframe.contentWindow.document.write('');
          iframe.contentWindow.close();
          document.body.removeChild(iframe);
      } else if(state === 0) {
          state = 1;
          // 加載完成,指向當前域,防止錯誤(proxy.html爲空白頁面)
          // Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame.
          iframe.contentWindow.location = 'http://localhost:10000/proxy.html';
      }
  };
 
  iframe.src = 'http://localhost:10001';
  document.body.appendChild(iframe);
</script>
 
<!-- localhost:10001 -->
<!DOCTYPE html>
...
<script>
  window.name = JSON.stringify({a: 1, b: 2});
</script>
</html>

方式五:window.postMessage()
HTML5新特性,可以用來向其他所有的 window 對象發送消息。需要注意的是我們必須要保證所有的腳本執行完才發送 MessageEvent,如果在函數執行的過程中調用了它,就會讓後面的函數超時無法執行。

下述代碼實現了跨域存儲localStorage

<!-- 
 下述用端口 
 10000表示:domainA
 10001表示:domainB
-->
 
<!-- localhost:10000 -->
<iframe src="http://localhost:10001/msg.html" name="myPostMessage" style="display:none;">
</iframe>
 
<script>
  function main() {
      LSsetItem('test', 'Test: ' + new Date());
      LSgetItem('test', function(value) {
          console.log('value: ' + value);
      });
      LSremoveItem('test');
  }
 
  var callbacks = {};
  window.addEventListener('message', function(event) {
      if (event.source === frames['myPostMessage']) {
          console.log(event)
          var data = /^#localStorage#(\d+)(null)?#([\S\s]*)/.exec(event.data);
          if (data) {
              if (callbacks[data[1]]) {
                  callbacks[data[1]](data[2] === 'null' ? null : data[3]);
              }
              delete callbacks[data[1]];
          }
      }
  }, false);
 
  var domain = '*';
  // 增加
  function LSsetItem(key, value) {
      var obj = {
          setItem: key,
          value: value
      };
      frames['myPostMessage'].postMessage(JSON.stringify(obj), domain);
  }
  // 獲取
  function LSgetItem(key, callback) {
      var identifier = new Date().getTime();
      var obj = {
          identifier: identifier,
          getItem: key
      };
      callbacks[identifier] = callback;
      frames['myPostMessage'].postMessage(JSON.stringify(obj), domain);
  }
  // 刪除
  function LSremoveItem(key) {
      var obj = {
          removeItem: key
      };
      frames['myPostMessage'].postMessage(JSON.stringify(obj), domain);
  }
</script>
 
<!-- localhost:10001 -->
<script>
  window.addEventListener('message', function(event) {
    console.log('Receiver debugging', event);
    if (event.origin == 'http://localhost:10000') {
      var data = JSON.parse(event.data);
      if ('setItem' in data) {
        localStorage.setItem(data.setItem, data.value);
      } else if ('getItem' in data) {
        var gotItem = localStorage.getItem(data.getItem);
        event.source.postMessage(
          '#localStorage#' + data.identifier +
          (gotItem === null ? 'null#' : '#' + gotItem),
          event.origin
        );
      } else if ('removeItem' in data) {
        localStorage.removeItem(data.removeItem);
      }
    }
  }, false);
</script>

注意Safari一下,會報錯:

Blocked a frame with origin “http://localhost:10001” from accessing a frame with origin “http://localhost:10000“. Protocols, domains, and ports must match.

避免該錯誤,可以在Safari瀏覽器中勾選開發菜單==>停用跨域限制。或者只能使用服務器端轉存的方式實現,因爲Safari瀏覽器默認只支持CORS跨域請求。

方式六:修改document.domain跨子域

前提條件:這兩個域名必須屬於同一個基礎域名!而且所用的協議,端口都要一致,否則無法利用document.domain進行跨域,所以只能跨子域

在根域範圍內,允許把domain屬性的值設置爲它的上一級域。例如,在”aaa.xxx.com”域內,可以把domain設置爲 “xxx.com” 但不能設置爲 “xxx.org” 或者”com”。

現在存在兩個域名aaa.xxx.com和bbb.xxx.com。在aaa下嵌入bbb的頁面,由於其document.name不一致,無法在aaa下操作bbb的js。可以在aaa和bbb下通過js將document.name = ‘xxx.com’;設置一致,來達到互相訪問的作用。

方式七:WebSocket

WebSocket protocol 是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通信,同時允許跨域通訊,是server push技術的一種很棒的實現。相關文章,請查看:WebSocket、WebSocket-SockJS

需要注意:WebSocket對象不支持DOM 2級事件偵聽器,必須使用DOM 0級語法分別定義各個事件。

方式八:代理

同源策略是針對瀏覽器端進行的限制,可以通過服務器端來解決該問題

DomainA客戶端(瀏覽器) ==> DomainA服務器 ==> DomainB服務器 ==> DomainA客戶端(瀏覽器)

來源:blog.csdn.net/ligang2585116/article/details/73072868

13.說一下 JSONP 實現原理?

jsonp 即 json+padding,動態創建script標籤,利用script標籤的src屬性可以獲取任何域下的js腳本,通過這個特性(也可以說漏洞),服務器端不在返貨json格式,而是返回一段調用某個函數的js代碼,在src中進行了調用,這樣實現了跨域。


設計模式

超詳細的設計模式

框架

1. springmvc的工作流程?

可以看這個博客:
詳情點擊鏈接
https://www.cnblogs.com/jiyukai/p/7629498.html

2. springmvc的常用註解

  • @Controller:用於控制層註解 , (特殊的@Component)。
  • @RestContoller:用戶控制器註解,等於@Controller+@ResponseBody.
  • @RequestMappint:用於方法上,來映射 Request 請求與處理器。參數:
    1. value:定義request請求的映射地址
    2. method:定義地request址請求的方式,包括【GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.】默認接受get請求,如果請求方式和定義的方式不一樣則請求無法成功。
    3. params:定義request請求中必須包含的參數值。
    4. headers:定義request請求中必須包含某些指定的請求頭,如:RequestMapping(value = “/something”, headers = “content-type=text/*”)說明請求中必須要包含"text/html", "text/plain"這中類型的Content-type頭,纔是一個匹配的請求。
    5. consumes:定義請求提交內容的類型。
    6. produces:指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回
  • @ResponseBody:將返回值內容通過springMVC提供的HttpMessageConverter接口轉換爲json、xml等格式的數據,再轉換爲java對象綁定到Controller類方法的參數上,即將返回值放在response體內。
  • @RequestBody:允許request的參數在request體中,而不是在直接鏈接在地址後面。此註解放在參數前。
  • @PathVariable:用來接收路徑參數,如/news/001,可接收001作爲參數,此註解放置在參數前。
  • @RequestParam:value:請求參數名,require:是否必須存在,默認爲true,defaultValue:默認值,表示如果請求中沒有同名參數時的默認值;處理簡單類型的綁定,作用在參數前。
  • @ModelAttribute:用於把參數保存到model中,可以註解方法或參數,註解在方法上的時候,該方法將在處理器方法執行之前執行,然後把返回的對象存放在 session或模型屬性中。
  • @ControllerAdvice 使一個Controller成爲全局的異常處理類, 類中用ExceptinHandler方法註解的方法可以處理所有Controller發生的異常。
  • @ExceptionHandler: 註解到方法上, 出現異常時會執行該方法。

3. spring的優點?

  1. 非侵入式設計:Spring是一種非侵入式(non-invasive)框架,它可以使應用程序代碼對框架的依賴最小化。
  2. 方便解耦、簡化開發:Spring就是一個大工廠,可以將所有對象的創建和依賴關係的維護工作都交給Spring容器的管理,大大的降低了組件之間的耦合性。
  3. 支持AOP:Spring提供了對AOP的支持,它允許將一些通用任務,如安全、事物、日誌等進行集中式處理,從而提高了程序的複用性。
  4. 支持聲明式事務處理:只需要通過配置就可以完成對事物的管理,而無須手動編程。
  5. 方便程序的測試:Spring提供了對Junit4的支持,可以通過註解方便的測試Spring程序。
  6. 方便集成各種優秀框架:Spring不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如Struts、Hibernate、MyBatis、Quartz等)的直接支持。
  7. 降低Jave EE API的使用難度:Spring對Java EE開發中非常難用的一些API(如JDBC、JavaMail等),都提供了封裝,使這些API應用難度大大降低。
  8. IOC反轉控制(也可以叫DI依賴注入)
    就是對象依賴關係不用你來維護,由IOC容器來維護(對象間依賴關係就是類與類之間的依賴關係,使用與被使用。之前這些要你自己去完成它們的依賴關係,有了IOC容器這工作就就交給IOC容器來完成。)
  9. AOP面向切面編程

4. 說下Spring的Aop
AOP即 Aspect Oriented Program 面向切面編程,是對OOP即面向對象的程序設計和POP面向過程的程序設計的補充。
首先,在面向切面編程的思想裏面,把功能分爲核心業務功能,和周邊功能。

所謂的核心業務,比如登陸,增加數據,刪除數據都叫核心業務
所謂的周邊功能,比如性能統計,日誌,事務管理等等

周邊功能在 Spring 的面向切面編程AOP思想裏,即被定義爲切面
在面向切面編程AOP的思想裏面,核心業務功能和切面功能分別獨立進行開發,然後把切面功能和核心業務功能 “編織” 在一起,這就叫AOP
詳情點擊鏈接
5. Spring 的IOC
Ioc—Inversion of Control,即“控制反轉”,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象的創建和銷燬等交給容器控制,而不是傳統的在你的對象內部直接控制。
DI—Dependency Injection,即“依賴注入”,是IOC的一種實現方式,即是一種技術:組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。
這樣設計的目的:把對象的創建和依賴交給Spring的IOC容器管理,實現對象之間的“解耦”。
Spring中Bean的生命週期?
Spring Bean的生命週期
6. Spring中使用了哪些設計模式?都在什麼地方使用的?
詳情點擊鏈接
7. Spring中bean的作用域有哪幾種?

  1. singleton:單例模式,IOC容器僅創建一個Bean實例,IOC容器每次返回的是同一個Bean實例。
  2. prototype : 原型模式,每次請求都會創建一個新的 bean 實例。
  3. request : 每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP request內有效。
  4. session : 每一次HTTP請求都會產生一個新的 bean,同一個Session共享一個Bean實例。不同Session使用不同的實例。
  5. global-session: 全局session作用域,僅僅在基於portlet的web應用中才有意義,Spring5已經沒有了。該屬性僅用於HTTP Session,同session作用域不同的是,所有的Session共享一個Bean實例。

8. spring中bean的線程安全問題
spring中bean的線程安全問題
9. Spring事務的傳播特性和隔離級別
Spring事務的傳播特性和隔離級別
10. Springboot常用的配置文件有哪些?
10. Mybatis和Hibernate的區別

  1. Mybatis和Hibernate都可以成爲ORM框架,但是Mybatis是半ORM的,HIbernate是全ORM,因爲Hibernate完全可以通過對象關係模型實現對數據庫的操作,擁有完整的JavaBean對象與數據庫的映射結構來自動生成sql。而mybatis僅有基本的字段映射,對象數據以及對象實際關係仍然需要通過手寫sql來實現和管理。
  2. MyBatis的二級緩存配置都是在每個具體的表-對象映射中進行詳細配置,這樣針對不同的表可以自定義不同的緩存機制。並且Mybatis可以在命名空間中共享相同的緩存配置和實例,通過Cache-ref來實現。Hibernate的二級緩存配置在SessionFactory生成的配置文件中進行詳細配置,然後再在具體的表-對象映射中配置是那種緩存。
  3. Hibernate是重量級的框架, 裏面封裝比較完整,功能比較豐富,比如sql優化、關係映射和日誌記錄等等都會消耗大量資源。MyBatis是輕量級的框架, 學習使用門檻低, 使用靈活方便,只做了字段方面的映射,手動優化sql等等。

11.Mybatis的緩存機制
mybatis的緩存機制


數據庫

1. sql的連接類型
1、自關聯(join或inner join)
2、左外關聯(left join或left outer join)
3、右外關聯(right join或right outer join)
4、全關聯(full join)
2. where和having的區別

where執行在返回結果之前,且先於聚合函數之前,用於過濾行,不能使用聚合函數;having執行在返回結果之後,可以使用聚合查詢,對返回的結果進行過濾。

4. sql的約束

  • NOT NULL:非空約束。
  • UNIQUE:唯一約束,此字段的每條記錄必須唯一,一般我們用來約束id,他和primary key一樣,都對字段保證了唯一性。
  • PRIMARY KEY:主鍵約束。設置此字段爲這張表的主鍵,每個表應該有一個主鍵,而且每個表都只能有一個主鍵,主鍵字段必須唯一且不能有null值
  • FOREIGN KEY:外建約束。
  • CHECK:約束用於限制字段中的值的範圍。
  • DEFAULT:缺省約束:默認值,如果定義了默認值,再插入數據時如果沒有插入數據,會根據默認值插入

5. 事務的四大特徵
ACID,原子性(Atomicity)、一致性(Correspondence)、隔離
性(Isolation)、持久性(Durability)。

(1)原子性:整個事務中的所有操作,要麼全部完成,要麼全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
(2)一致性:在事務開始之前和事務結束以後,數據庫的完整性約束沒有被破壞。一致性是指事務必須使數據庫從一個一致性狀態變換到另一個一致性狀態,也就是說一個事務執行之前和執行之後都必須處於一致性狀態。
(3)隔離性:隔離狀態執行事務,使它們好像是系統在給定時間內執行的唯一操作。如果有兩個事務,運行在相同的時間內,執行 相同的功能,事務的隔離性將確保每一事務在系統中認爲只有該事務在使用系統。這種屬性有時稱爲串行化,爲了防止事務操作間的混淆, 必須串行化或序列化請 求,使得在同一時間僅有一個請求用於同一數據。
(4)持久性:在事務完成以後,該事務所對數據庫所作的更改便持久的保存在數據庫之中,並不會被回滾。

6. sql調優
7. mysql的四種隔離級別?怎麼實現的?
Mysql的四個隔離級別是如何實現的
8. Oracle也是使用Limit進行分頁的嗎?
Oracle是使用的rownum和between來進行分頁的。rownum是虛擬列,是得出結果後,再進行計算的。所以,只能是小於的,無法大於,要使用的大於,就必須使用別名。rownum的效率比between高。原因點擊查看詳情
9. Mysql索引的類型?
點擊鏈接查看詳情
10. Mysql索引的實現算法?爲什麼使用該算法?
點擊查看詳情
11. 數據庫的鎖?表級鎖、頁級鎖、行級鎖
點擊查看詳情

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章