點贊關注,不會迷路! 
本文大多是各大廠topN題目,針對中高級java開發,會持續更新,如果有優質面試題,歡迎大家一起交流 點擊一起學習 暗號:csdn 第一時間領取最新學習資料+簡歷優化資源
1、String類能不能被繼承?為什么?
不能
因為string類是被final修飾的類,final修飾過的類不能被繼承、final修飾過的變數不能被修改
2、實作單例設計模式(懶漢、餓漢)
//懶漢,顧名思義比較懶,在用的時候才實體化
public class Singleton {
//創建實體,注意,此時沒有new
private static Singleton instance = null;
//構造方法私有化
private Singleton() {}
//公有的靜態方法,回傳實體物件
public static Singleton getInstance() {
if (instance == null) {
//這里才new
instance = new Singleton();
}
return instance;
}
}
//餓漢,顧名思義很饑餓,創建物件的時候就直接new
public class Singleton {
//創建實體的時候就new
private static Singleton instance = new Singleton();
// 私有化構造方法,外部不能new
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
3、簡述Java的反射機制和使用場景
反射是Java的一種機制,可以讓我們在運行時獲取類的資訊
通過反射我們可以獲取到類的所有資訊,比如它的屬性、構造器、方法、注解等
適用于需要動態創建物件的場景
4、什么是記憶體泄漏,怎么確定記憶體泄漏?
概念:記憶體泄漏就是指jvm記憶體沒有及時釋放,用人話說就是使用完的物件沒有被回收,一般造成原因都是編碼不規范,new了很多值為null的物件,然后又不呼叫
5、簡述動態代理和靜態代理
靜態代理:
由程式員創建或由特定工具自動生成源代碼,再對其編譯,在程式運行前,代理類的.class檔案就已經存在了
靜態代理通常只代理一個類
靜態代理事先知道要代理的是什么
動態代理:
在程式運行時,運用反射機制動態創建而成
動態代理是代理一個介面下的多個實作類
動態代理不知道要代理什么東西,只有在運行時才知道
6、手寫生產者消費者模型
倉庫:Warehouse
public class Warehouse {
private volatile int apple = 0;
public synchronized void increace() {
while (apple == 5) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
apple++;
System.out.println("蘋果生產成功!");
notify();
}
public synchronized void decreace() {
while (apple == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
apple--;
System.out.println("蘋果消費成功!");
notify();
}
public static void main(String[] args) {
Warehouse warehouse = new Warehouse();
Consumer con = new Consumer(warehouse);
Producer pro = new Producer(warehouse);
Thread t1 = new Thread(con);
Thread t2 = new Thread(pro);
t1.start();
t2.start();
}
}
生產者:Producer
public class Producer implements Runnable {
private Warehouse warehouse;
public Producer(Warehouse warehouse) {
this.warehouse = warehouse;
}
@Override
public void run() {
for( int i=0;i<10;i++)
{
try {
System. out .println("pro i:" +i);
Thread. sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
warehouse.increace();
}
}
}
消費者:Consumer
public class Consumer implements Runnable {
private Warehouse warehouse;
public Consumer(Warehouse warehouse) {
this.warehouse = warehouse;
}
@Override
public void run() {
for( int i=0;i<10;i++)
{
try {
System. out .println("Con: i " +i);
// 這里設定跟上面30不同是為了 倉庫中的蘋果能夠增加,不會生產一個馬上被消費
Thread. sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
warehouse.decreace();
}
}
}
最新2021整理收集的一些Java學習資料(都整理成檔案),有很多干貨,包含mysql,netty,spring,執行緒,spring cloud等詳細講解,也有詳細的學習規劃圖,面試題整理等,
點擊 : 一線大廠核心技術分享 分享最新技術,走在知識前端
點擊一起學習 暗號:csdn 最新學習資料+簡歷優化資源

7、Java中介面和抽象類的異同?
先單獨說一點,讓你可以在朋友面前裝逼:介面也能被繼承,只不過是被介面繼承
1、都能包含抽象的方法,這些抽象的方法用于描述類具備的功能,不提供具體的實作(jdk1.8允許介面有一個default的實作方法)
2、介面是對事物行為的抽象,而抽象類是對事務本質的抽象;
3、介面中的變數必須給出初始值,抽象類可以不給;
4、一個類只能繼承一個抽象類,但可以實作多個介面;
5、抽象類中可以寫非抽象的方法,從而避免在子類中重復書寫它們,這樣可以提高代碼的復用性,這是抽象類的優勢;介面中只能有抽象的方法;
針對第二點和第四點舉個例幫助理解:
對于抽象類:比方說有公雞、母雞、公狗、母狗,我們可以抽象出兩個更高級的類,雞類和狗類,因為你不能又是雞又是狗,所以你只能繼承其中一個,這就是為什么抽象類只能單繼承;
對于介面:眾所周知,雞都會唱、跳、rap,這時候就可以把雞的基本操作抽象成一個介面,而有的雞通過后天練習可能會學會打籃球,那么就存在一種高端雞,又會唱跳rap又會打籃球,這就是為什么介面可以多實作;
8、Java中sleep和wait的區別?
1、sleep是Thread的方法,wait是Object的方法
2、sleep方法沒有釋放鎖,而wait方法釋放了鎖
3、wait,notify和notifyAll只能在同步控制方法或者同步控制塊里面使用,而sleep可以在任何地方使用
4、sleep必須捕獲例外,而wait,notify和notifyAll不需要捕獲例外
9、Java如何進行高效的陣列拷貝?
Arrays.copyOf或 System.arraycopy,是自己new陣列, 然后for回圈復制效率的兩倍左右
為什么快,因為它們是native方法;
10、工廠模式使用場景
設計模式共23種,按功能可以分為創建型、結構型、行為型,工廠模式屬于創建型模式,主要用于創建物件;
比方說造一輛車
如果不使用工廠模式,我需要造寶馬的時候,就寫一個造寶馬的方法,需要造奔馳的時候就寫一個造奔馳的方法,缺陷很明顯,后期不易維護、代碼冗余、不符合面向物件的思想;
使用工廠模式時,我只需要寫一個造汽車的方法,在內部做一些判斷,如果引數是寶馬我就造寶馬,如果是奔馳我就造奔馳,這樣后期如果造汽車,只需要呼叫它的方法傳入引數就行;
工廠模式能細分為簡單工廠,工廠方法和抽象工廠三種
常見的有簡單工廠和工廠方法兩種, 簡單工廠中包含工廠、產品和具體產品三個角色,其中工廠是整個模式的核心,這個類當中包含有必要的判斷邏輯,可以決定在什么時候創建哪一個產品類的物件,而客戶端則可以免除直接創建產品物件的責任,從這個角度上說簡單工廠實作了對責任的分割,另外,由于客戶端所使用的物件都由工廠生成,并統一轉型為產品型別,所以客戶端無需關心自己得到的是哪一個具體產品,這樣在添加新的具體產品時,就不需要修改客戶端的代碼,從某種程度上也實作了開放-封閉法則,但是每當需要添加新的具體產品時,就需要修改工廠類, 工廠方法使用了面向物件的多型性,保留了簡單工廠的優點,而且克服了它的缺點,首先,在工廠模式當中,核心的工廠類不再負責所有產品的創建,而是將具體的產品創建交給子類去做,這個核心類成了抽象工廠角色,僅僅負責給出具體工廠子類必須實作的介面,這種進一步抽象的結果是,可以允許系統在不修改具體工廠角色的情況下引進新的產品, 在現實的開發當中,典型的例子就是在servcie層中需要得到DAO物件,通常就會抽象出DAO介面作為產品,介面的實作類作為具體產品,然后提供工廠供service使用,這樣就可以分離service和dao的耦合,當DAO添加新的實作類是,service不需要修改,提升系統的擴展性和維護性,
11、成員變數和方法的區別?
成員變數有兩種:實體變數和類變數(也稱靜態變數,靜態域),
成員方法有三種:實體方法,類方法(也稱靜態方法),構造方法(無回傳值,方法名和類名一致),
public class Person {
public static final int defaultAge = 18;//常量,類編譯時放到常量池
public static int age = 18;//類變數,在類加載的準備階段,分配到方法區
private String like;//實體變數,在類被實體化時,分配到堆中
//靜態模塊
static {
System.out.println("唱 跳 rap 籃球");
}
//類方法,分配到方法區
static int getAge() {
return age;
}
public Person() {
//類的構造方法
}
//實體方法,分配到方法區
public String getLike() {
return like;
}
//實體方法
public void setLike(String like) {
this.like = like;
}
}
類變數的特點:
它是該類所有實體共享的屬性,在記憶體中只有一個地方存盤這個變數(在方法區),在類加載的準備階段,分配到方法區,初始化階段正式賦值,
所有實體都可以修改這個類變數的值,(前提是沒有被final修飾)
訪問類變數不用實體化物件,直接通過類可以使用,
生命周期取決于類的生命周期,
類方法的特點:
直接通過類就可以呼叫,
類方法可以直接呼叫類變數和類方法,
類方法不可以直接呼叫實體變數和實體方法,
類方法沒有this,因為沒有實體,
實體變數和實體方法的特點:
都必須通過實體物件才可以訪問,(實體變數位于堆中,生命周期取決于實體的生命周期)
12、Java編譯后的.class檔案包含了哪些內容?
編譯后的.class檔案是一組以8位位元組為基礎單位的二進制流,各個資料專案嚴格按照順序緊湊地排列在.class檔案之中,中間沒有添加任何分隔符;
根據Java虛擬機規范的規定,.class檔案格式采用一種類似于C語言的偽結構來存盤資料,包含無符號數和表:
無符號數:以u1、u2、u4、u8來分別代表1個位元組、2個位元組、4個位元組和8個位元組的無符號數,無符號數可以用來描述數字、索引參考、數量值或者按照UTF-8編碼構成的字串值;
表:由多個無符號數或者其他表作為資料項構成的復合資料型別,所有表都習慣性的以”_info“結尾,
Class檔案的結構沒有分隔符,無論你是數量還是順序,都是嚴格規定的,哪個位元組代表什么含義、長度多少、先后順序如何,都不允許改變,
13、簡述zset實作原理
一兩句話說不清楚,就是一種資料結構,打個比方
1、2、3、4、5、6、7、8、9
要找到8,我得遍歷8次
如果我把1、3、5、7、9拎出來作為一個類似索引的東西
找8的時候,先在索引里面找,8在7和9中間,找到7以后再遍歷1次就找到了8,總共遍歷5次
而這個1、3、5、7、9的索引還可以拎個1、5、9出來,以此類推
回到zset,它維護了兩個元素,一個是 dict,用來維護資料到分數的關系,一個是zskiplist,用來維護分數所在鏈表的關系
14、簡述協程的優缺點
協程是一種輕量級執行緒
優點:
跨平臺
跨體系架構
無需執行緒背景關系切換的開銷
無需原子操作鎖定及同步的開銷
方便切換控制流,簡化編程模型
高并發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題,所以很適合用于高并發處理,
缺點:
無法利用多核資源,協程的本質是個單執行緒,它不能同時將 單個CPU 的多個核用上,協程需要和行程配合才能運行在多CPU上.當然我們日常所撰寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用,
進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程式,這一點和事件驅動一樣,可以使用異步IO操作來解決
15、如何判斷一個hash函式好不好?
1、計算性能
2、離散性
hash演算法一定要把資料哈希均勻,不能全部擠在一坨,對hash演算法不太清楚的必須看看這篇文章,我以前花了整整一天時間寫的,舉了很多例,真正搞懂hashCode和hash演算法
16、http中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中,
(本標準答案參考自w3schools)
然而實際上,它們的本質都是 TCP 連接,并無區別,上面的答案純粹是為了應付面試官,真正導致產生區別的原因是 HTTP 的規定以及瀏覽器/服務器的限制,這才導致它們在應用程序中可能會有所不同,
17、解決hash沖突的方式?
拉鏈法
代表作:hashMap
原理:把所有hash值相同的元素放到鏈表中
再哈希法
原理:產生沖突時,對結果再進行一次hash,直到沒有hash沖突,一般沒人用,太魯莽了,而且治標不治本,理論上永遠不能徹底解決hash沖突
開放地址法
1、線性探測法
2、線性補償探測法
3、偽隨機探測
18、簡述裝飾者模式和配接器模式
裝飾者模式
概括:在不改變現有物件結構的情況下,動態地給該物件增加一些職責(即增加其額外功能),它屬于結構型模式,mybatis-plus的QueryWrapper就是裝飾者模式的體現
配接器模式
概況:將一個介面轉換成用戶需要的另一個介面,使介面不兼容的那些類可以一起作業,也屬于結構型模式,SpringMvc中的HandlerAdapter就是配接器模式的體現
19、hashCode和equals方法的聯系
java規定:
如果兩個物件的hashCode()相等,那么他們的equals()不一定相等,
如果兩個物件的equals()相等,那么他們的hashCode()必定相等,
20、什么是重寫和多載?
1、重寫是在子類存在方法與父類的方法的名字相同,而且引數的個數與型別一樣,回傳值也一樣的方法,就稱為重寫(Override)
2、多載是一個類中定義了多個方法名相同,而他們的引數的數量不同或數量相同而型別和次序不同,則稱為方法的多載(Overload)
3、多載是一個類的多型性表現,而重寫是子類與父類的一種多型性表現
21、Java有幾種基本資料型別?分別占用多少位元組?
| 資料型別 | 占用位元組 |
|---|---|
| byte | 1 |
| short | 2 |
| int | 4 |
| long | 8 |
| char | 2(c語言是1位元組,可以存盤一個漢字) |
| float | 4 |
| double | 8 |
| boolean | 1/8 |
22、Java例外有哪些型別?
Java所有的例外都繼承至Throwable,分為Error和Exception兩大類,其中:
Error是系統級錯誤,在代碼層無法處理,常見的有StackOverflowError、OutOfMemoryError等;
Exception是例外,通常可以在代碼層處理,常見的有NullpointerException、ClassCaseException、IndexOutOfBoundsException等
23、簡述jvm記憶體模型?
程式計數器:執行緒私有,各執行緒之間獨立儲存,互不影響,若當前執行的是Java方法,則記錄的就是當前執行指令的地址,若是native方法,則為空;
java虛擬機堆疊:執行緒私有,每個方法在執行時都會創建一個堆疊幀,方法執行程序就是堆疊幀在虛擬機堆疊中從入堆疊到出堆疊的程序,入堆疊表示方法開始被呼叫,出堆疊表示方法執行完畢,堆疊幀用于保存方法內部區域變數、運算元、方法回傳值、動態鏈接;我們平時說的堆疊其實一般就是指區域變數區:用于存放方法引數、方法內定義的區域變數,還有已知的八大基本資料型別、物件參考、回傳值地址;
本地方法堆疊:執行緒私有,和虛擬機堆疊相似,區別在于虛擬機堆疊的服務物件是java方法,而本地方法堆疊是本地方法;
堆:執行緒共享,在虛擬機啟動的時候創建,用于存放物件實體,堆是GC管理的主要區域;
方法區:執行緒共享,其實方法區也是堆的物理組成部分,用于存放常量、靜態變數 、 類資訊(構造方法/介面定義) 、運行時常量池;注意, 實體變數在堆記憶體中,和方法區無關,
(jdk1.8之前,方法區的實作是永久代,從1.8開始,用元空間代替了永久代,注意一點,方法區還是那個方法區)
這一坨內容請酌情觀看,我怕把你繞暈~
再細分的話還有運行時常量池和字串常量池:jdk1.6的時候,兩者都是屬于方法區,1.7開始,字串常量池被移到了堆記憶體;運行時常量池用于存放編譯期生成的各種常量(“abc”,123等)和符號參考;而字串常量池是為了提高jvm效率單獨用來存放字串的,因為字串不同于其他資料型別,它可以很長很長很長很長很長很長很長很長很長很長很長很長~;
24、簡述GC機制,新生代和老年代的區別?
垃圾回收是java管理記憶體的一種機制,作用是清理無用物件避免記憶體泄漏,gc主要發生在java堆上,而堆可以細分為新生代和老年代(分代是為了提高gc效率,其實不分代也可以完成gc,只不過gc機制會對堆的所有區域進行掃描,浪費資源),新生代還可以細分為三個虛擬的區,Eden區、FromSurvivor區、ToSurvivor區,一開始物件都在Eden區,Eden區的物件經過一次新生代gc(復制演算法)后若還能存活,就會移動到survivor區(ToSurvivor區),在此次新生代gc時,在survivor發生的改變就是,From區中的物件會根據年齡來決定去留,達到閾值,會移動到老年代,沒達到就移動到To區,經過此次新生代gc,Eden和From區都已被清空,From區和To區會互換;
新生代gc(Minor GC)觸發時機:當Eden區沒有足夠空間進行分配時;
老年代gc(Major GC/Full GC)觸發時機:當老年代中沒有足夠的記憶體空間來存放物件時,虛擬機會發起一次Major GC/Full GC,只要老年代的連續空間大于新生代物件總大小或者歷次晉升的平均大小就會進行Minor GC,否則將進行Full GC,
25、如何優化JVM頻繁Minor GC?
適當擴大堆記憶體太小
26、簡述類加載機制,什么是雙親委派?
1、加載:將源檔案(代碼)編譯成.class檔案,傳遞給類加載器
2、驗證:類加載器拿到這些位元組碼檔案后開始執行檢查(通過本地類別庫),確保加載進來的位元組流格式符合java虛擬機要求
3、準備:對類變數(不是實體變數)分配記憶體,并且給一個默認初始化的值(0或者null)
4、決議:將常量池中的符號參考加載成直接參考(如string str = new string(“123”),str就是符號參考,123是直接參考)
5、初始化:對類的靜態變數初始化為指定的值,執行靜態代碼塊
雙親委派機制:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,
因此所有的加載請求最終都應該傳送到頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它的搜索范圍中沒有找到所需的類)時,子加載器才會嘗試自己去加載,
27、synchronized底層實作原理?它與lock相比有什么優缺點?
首先那些說看過synchronized原始碼的基本都是大聰明,synchronized根本點不進去,想弄懂它的實作原理,我們只能通過看編譯好的位元組碼檔案
原理:
基于物件的監視器(ObjectMonitor),我們在位元組碼檔案里面可以看到,在同步方法執行前后,有兩個指令,方法前monitorenter,方法后monitorexit;
與lock對比:
1、synchronized不需要手動釋放鎖,lock需要在鎖用完后進行unlock;
2、synchronized只能是默認的非公平鎖,lock可以指定使用公平鎖或者非公平鎖;
3、lock提供的Condition(條件)可以指定喚醒哪些執行緒,而synchronized只能隨機喚醒一個或者全部喚醒;
28、jvm指令重排原因?怎么避免?
原因:計算機記憶體操作速度遠慢于CPU運行速度,所以就造成CPU空置,為了將提高CPU利用率,虛擬機會按照自己的一些規則會跳過執行慢的代碼,去執行快的代碼(即對代碼重新排序),從而提升jvm的整體性能,
怎么避免:給關鍵的代碼加上volatile關鍵字,所謂關鍵,就是會被執行順序影響結果,
volatile關鍵字的三個特征是:執行緒可見、不具備原子性、禁止指令重排,volatile 的讀性能消耗與普通變數幾乎相同,但是寫操作稍慢,因為它需要在本地代碼中插入許多記憶體屏障指令來保證處理器不發生亂序執行,
29、ReentrantLock是什么?有什么用?怎么用?和synchronized的區別?
ReentrantLock是Lock的一個子類
作用:
用來給資源加鎖,避免高并發造成的資料例外問題;
使用:
public void lockTest() {
//創建lock實體,可傳引數true或者false,表示是否是公平鎖,默認是非公平鎖
ReentrantLock lock = new ReentrantLock();
//在需要保證同步的代碼前lock
lock.lock();
int i = 0;
i++;
//代碼后unlock
lock.unlock();
}
與synchronized主要區別,lock需要手動釋放鎖,可以指定公平鎖或者非公平鎖
30、volatile關鍵字解決了什么問題?實作原理是什么?
解決了什么問題:
1、保證了變數的可見性
2、禁止指令重排
比如i=i+1,單執行緒操作沒問題,如果使用多執行緒,比如兩個執行緒,執行這段代碼后(i初始值為0),i應該等于2,但是如果不用volatile修飾變數i,結果會等于1,初始時,兩個執行緒分別讀取i的值存入各自所在的CPU的高速快取當中,然后執行緒1進行加1操作,然后把i的最新值1寫入到記憶體,此時執行緒2的高速快取當中i的值還是0,進行加1操作之后,i的值為1,然后執行緒2把i的值寫入記憶體,
實作原理
基于記憶體屏障,關于記憶體屏障,搞java開發的同學在開發中不可能接觸到,所以不用關心太多,知道記憶體屏障有什么作用,面試官問到你能唬住他就行了,因為面試官自己也不懂
(1)它確保指令重排序時不會把其后面的指令排到記憶體屏障前面,也不會把前面的指令排到記憶體屏障后面,總之一句話,他能保證指令按照我們希望的順序執行;
(2)它會強制將對快取的修改操作立即寫入主存,使得其它執行緒能立馬發現;
31、ThreadLocal實作原理?
ThreadLocal中文名叫執行緒變數,它底層維護了一個map,key就是當前的ThreadLocal物件(可以理解為當前執行該段代碼的執行緒),value就是你set的值,這個map保證了各個執行緒的資料互不干擾;
32、執行緒池實作原理?
要說實作原理,必須得把執行緒池的幾個引數徹底搞懂,不要死記硬背
1、corePoolSize(必填):核心執行緒數, 2、maximumPoolSize(必填):最大執行緒數,
3、keepAliveTime(必填):執行緒空閑時長,如果超過該時長,非核心執行緒就會被回收,
4、unit(必填):指定keepAliveTime的時間單位,常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分),
5、workQueue(必填):任務佇列,通過執行緒池的execute()方法提交的Runnable物件將存盤在該佇列中,
6、threadFactory(可選):執行緒工廠,一般就用默認的,
7、handler(可選):拒絕策略,當執行緒數達到最大執行緒數時就要執行飽和策略,
33、java是如何實作執行緒安全的?哪些資料結構是執行緒安全的?
1、鎖機制:用synchronize、lock給共享資源加鎖;
2、使用java提供的安全類:java.util.concurrent包下的類自身就是執行緒安全的,在保證安全的同時還能保證性能;
執行緒安全的資料結構:
常見的有Atomicinteger、ConcurrentHashMap,太多了,截個圖吧,大家可以自己去java.util.concurrent包下看
34、java執行緒間通信方式
1、基于synchronized+wait() 和 notify()
2、基于reentrantLock的Condition
3、基于volatile
4、基于CountDownLatch
35、什么是公平鎖?什么是非公平鎖?
公平鎖:當前獲得鎖的執行緒釋放鎖后,其它所有等待中的執行緒會按照來的順序執行,不會造成鎖競爭;
非公平鎖:當前獲得鎖的執行緒釋放鎖后,其它所有等待中的執行緒會全部參與鎖競爭;
36、什么是redis快取穿透、快取擊穿、快取雪崩?如何解決?
從事態嚴重性來講:穿透 > 雪崩 > 擊穿
快取穿透:請求資料庫中根本就不存在的資料,既然資料庫中都沒有,快取中更沒有,導致每次請求直接懟到資料庫;
快取雪崩:快取大面積失效;
快取擊穿:請求了很多快取中沒有但是資料庫中真實存在的資料,一般是快取過期導致,也導致請求直接懟到資料庫;
解決辦法:
快取穿透:最簡單的就是利用布隆過濾器過濾非法key;
快取雪崩:設定key過期時間的時候加上一個亂數,關鍵點就在于讓key錯開時間失效;
快取擊穿:延長熱點資料過期時間,或者直接設定永遠不過期;
37、說說資料庫設計三范式?
第一范式
屬性(欄位)的原子性約束,要求屬性具有原子性,不可再分割; 比如個人資訊,個人資訊不能作為一個欄位,它可以再分為姓名、name、age等;
第二范式
記錄的惟一性約束,要求記錄有惟一標識,每條記錄需要有一個屬性來做為物體的唯一標識;
第三范式
欄位冗余性的約束,即任何欄位不能由其他欄位派生出來;主鍵沒有直接關系的資料列必須消除,消除的辦法就是再創建一個表來存放他們,當然外鍵除外;
誤區:
并不是非得嚴格按照三范式來設計,好的資料庫設計一定不是這樣的,而是根據實際情況柔性處理;
38、簡單闡述Java中的io、nio、bio
i/o即input/output,就是指讀寫操作
面試官經常問io和nio的區別,如果把io和nio放一起比較的話,那這里的io其實可以理解為bio,即blocking-io:
bio:同步阻塞
bio是java傳統的io模型,他是同步阻塞io,一個執行緒觸發io操作后,必須等待這個io操作執行完成,期間不能去做其他事情;
nio:同步非阻塞
nio(non-blocking-io)是同步非阻塞io,一個執行緒觸發io操作后它可以立即回傳,但是他需要不斷地輪詢去獲取回傳結果;
aio:異步非阻塞
aio(Asynchronous-io)是異步非阻塞io,一個執行緒觸發io操作后她可以立馬回傳去做其他事情,內核系統將io操作執行完成后會通知執行緒;
多路復用io:異步阻塞
io多路復用:可以理解為異步阻塞io,但官方沒這么叫,一個執行緒可以管理多個連接,不用來回切換;
39、String str = new String(“abc“)到底new了幾個物件?
兩個或者一個
1、兩個:如果常量池里面沒有“abc”這個字串,那虛擬機就會在堆記憶體中new出一個String物件,還會在常量池中new一個abc字串物件;
2、一個:如果常量池中已經有"abc"這個字串,也就是說你在前面已經new過一個值為“abc”的字串,那虛擬機就只會在堆記憶體中new一個String物件,并將常量池中“abc”的地址指向你剛剛new的String物件
40、spring如何解決回圈依賴?
spring只能通過提前暴露bean來解決setter注入的回圈依賴,無法解決構造器注入的回圈依賴;
這個屬于大廠高端面試題了,一般只要如上答出就ok了
41、mysql深度分頁
分頁大家都懂,如果某天資料庫量達到100萬條,而你需要的資料恰好在最后10條,常規的分頁就會變得特別慢
此時就要用些技巧,思路就是先查id后查記錄,直接看代碼吧
//常規分頁
SELECT * FROM table_name limit 7000,10 //0.868s
//先查id ,寫法很多,看個人習慣
SELECT * FROM table_name a,(SELECT id FROM table_name limit 7000,10) b WHERE a.id = b.id //0.468
//如果你的表有自增id,就這么寫,效率直接起飛,真的是專案經理看了感動,架構師看了落淚
SELECT * FROM table_name WHERE id>7000 LIMIT 10 //0.372
很生氣,我現在資料庫就只有這么多資料,體現不出太大的差別,資料量越大優勢越明顯
42、int和Integer有什么區別?
int是基本資料型別,默認值為0,integer是其包裝型別,默認值為null
- 原始型別: boolean,char,byte,short,int,long,float,double
- 包裝型別:Boolean,Character,Byte,Short,Integer,Long,Float,Double
java物體類盡量都用integer,假設一個學生類,有個考試成績屬性,如果學生缺考,該屬性不應該有值,應該為null,你用int的話默認值為0,考試成績為0和缺考是兩碼事
43、String和StringBuilder、StringBuffer的區別?
Java平臺提供了兩種型別的字串:String和StringBuffer/StringBuilder,它們可以儲存和操作字串,其中String是只讀字串,也就意味著String參考的字串內容是不能被改變的,而StringBuffer/StringBuilder類表示的字串物件可以直接進行修改,StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,區別在于它是在單執行緒環境下使用的,因為它的所有方面都沒有被synchronized修飾,因此它的效率也比StringBuffer要高,
44、常見的運行時例外
- ArithmeticException(算術例外)
- ClassCastException (類轉換例外)
- IllegalArgumentException (非法引數例外)
- IndexOutOfBoundsException (下標越界例外)
- NullPointerException (空指標例外)
- SecurityException (安全例外)
45、事務的ACID是指什么?
- 原子性(Atomic):事務中各項操作,要么全做要么全不做,任何一項操作的失敗都會導致整個事務的失敗;
- 一致性(Consistent):事務結束后系統狀態是一致的;
- 隔離性(Isolated):并發執行的事務彼此無法看到對方的中間狀態;
- 持久性(Durable):事務完成后所做的改動都會被持久化,即使發生災難性的失敗,通過日志和同步備份可以在故障發生后重建資料,
46、事務隔離級別
1、READ_UNCOMMITTED
讀未提交,即能夠讀取到沒有被提交的資料,無法解決臟讀、不可重復讀、幻讀
2、READ_COMMITED
讀已提交,即能夠讀到那些已經提交的資料,能夠防止臟讀,但是無法限制不可重復讀和幻讀
3、REPEATABLE_READ
可重復讀,即在資料讀出來之后加鎖,類似"select * from XXX for update",明確資料讀取出來就是為了更新用的,所以要加一把鎖,防止別人修改它,REPEATABLE_READ的意思也類似,讀取了一條資料,這個事務不結束,別的事務就不可以改這條記錄,這樣就解決了臟讀、不可重復讀的問題,但是幻讀的問題還是無法解決
4、SERLALIZABLE
串行化,最高的事務隔離級別,不管多少事務,挨個運行完一個事務的所有子事務之后才可以執行另外一個事務里面的所有子事務,解決了臟讀、不可重復讀和幻讀
表格總結
47、悲觀鎖和樂觀鎖的原理以及應用場景
悲觀鎖:
顧名思義,比較悲觀,每次去拿資料都認為別人會修改,所有每次在操作前都會加鎖,如:讀寫鎖、行鎖、表鎖等,synchronized的原理也是悲觀鎖;適用于多寫操作
樂觀鎖:
每次拿資料都認為別人不會修改,所以不會加鎖,但是在更新的時候,會先判斷在此期間有沒有人更新該資料,如果有,回傳沖突報錯資訊,讓用戶決定怎么操作;適用于多讀操作
48、對spring IOC和AOP的理解
IOC(控制反轉)
也叫DI(依賴注入),是一種思想,不是一種技術,IOC主張把物件的控制權交由spring,底層實作是反射+工廠方法模式,IOC容器實際上就是個Map,存放各種物件;
AOP
面向切面編程,把一些能共用、冗余、繁瑣的功能提取出來,AOP能在不改變原有業務邏輯的情況下,增強橫切邏輯代碼,根本上解耦合,避免橫切邏輯代碼重復;常見使用場景有事務管理、日志、全域例外處理、用戶鑒權;
49、簡述java記憶體模型
注意和本文23點的jvm記憶體模型區分開,這是兩種截然不同的記憶體模型
以下是本人總結的概念,別看字多,都是精華,我想過刪些,一句都沒找到,閱讀一遍應該理解的差不多了
java記憶體模型的由來:這和cpu和記憶體技術發展有關,當代cpu運算速度太快,以至于被記憶體拉低了程式運行的整體效率
應對措施:為了不被記憶體影響性能,cpu廠商給cpu加了一級二級甚至三級高速快取,cpu讀取資料時先從一級快取中找,沒有的話找二級,再沒有就找三級或者主存;java記憶體模型規定了變數都是存盤在主存中,程式運行時操作的是高速快取中的資料,操作完之后再同步到主存;
后遺癥及規范在cpu和主存之間增加了一個高速快取固然提升了程式運行效率,但是在多執行緒并發操作同一個變數時,就可能造成資料不一致的問題,所以就有了JMM(java記憶體模型,也可叫JMM規范),它保證了并發編程中資料的正確性(正確性可細分為原子性、可見性、有序性),底層具體實作方式比較復雜,好在JMM為我們日常編程提供了一些用于保證資料正確性的關鍵字,如synchronized(原子性、有序性)、volatile(可見性、有序性);
50、什么是泛型擦除?
泛型只是為了在編碼程序中,我的理解是泛型存在的意義有兩個:一是為了讓我們更快地發現錯誤,比如你把User放進了ArrayList< Dog >中,編譯器立馬會報錯;二是避免型別檢查,從而避免在運行時拋出 classCastException;泛型擦除就是指泛型會在編譯時被消除
ArrayList<User> users = new ArrayList<>();
ArrayList<Dog> dogs = new ArrayList<>();
System.out.println(users.getClass() == dogs.getClass());
//true
如上,運行結果表面兩個list是相等的,因為經過編譯后,泛型被擦除,兩個list當然也就相等;
51、你能想到幾種方法實作兩數交換?
1、第三變數
public void swapOne(){
int a = 4;
int b = 5;
int c = a;
a = b;
b = c;
System.out.println("a:"+a); //a:5
System.out.println("b:"+b); //a:4
}
2、數學計算
public void swapTwo(){
int a = 4;
int b = 5;
a=a+b;
b=a-b;
a=a-b;
System.out.println("a:"+a); //a:5
System.out.println("b:"+b); //a:4
}
3、異或運算
public void swapThree(){
int a = 4;
int b = 5;
a = a ^ b; // 0101 ^ 0100 ===> 0001,此時a的值為1
b = a ^ b; // 0001 ^ 0100 ===> 0101,此時b的值為5
a = a ^ b; // 0001 ^ 0101 ===> 0100,此時a的值為4
System.out.println("a:"+a); //a:5
System.out.println("b:"+b); //a:4
}
52、什么是CAS操作?什么ABA問題?如何解決?
CAS全稱compare and swap(比較并交換),作用是保證原子性
CAS操作包含三個運算元 —— 記憶體位置、預期原值、新值, 如果記憶體位置的值和預期原值相等,就把該值更新為新值,如果不相等,則什么都不做;
ABA問題:CAS操作存在的一個并發問題,打個比方,有兩個執行緒A、B同時操作變數x,A讀取到的預期原值是1,此時執行緒B先將x設定為2,再設定為1,等執行緒A再來操作的時候,x變數的預期原值和當前值相等,但是x在整個程序中的值是發生過變化的,這在某些業務場景下是不允許的;
解決:利用版本號,給變數x增加版本號,每次操作增加對本版好的判斷和修改;
53、什么是回表查詢?如何避免?
對于mysql資料庫的InnoDB引擎,它的非主鍵索引是非聚簇索引,索引檔案和資料檔案分開存盤,索引檔案B+樹的葉節點只保存主鍵,在進行查詢時,需要先去索引檔案找到id,再根據id去資料檔案查找具體資料,這種現象就叫回表查詢
如何避免:索引覆寫
將查詢sql中的欄位添加到聯合索引里面,只要保證查詢陳述句里面的欄位都在索引檔案中,就無需進行回表查詢;
54、什么是CAP理論?
CAP理論是分布式系統架構設計中的一種猜想,或者說是一種理論
- 一致性(Consistence) : 所有節點訪問的資料都是一致的;
- 可用性(Availability): 非故障的節點在合理的時間內回傳合理的回應(不是錯誤或者超時的回應);
- 磁區容錯性(Partition tolerance) : 分布式系統出現網路磁區的時候,仍然能夠對外提供服務,網路磁區指的是網路設備出現的丟包、阻塞、超時等問題,
誤區:我發現很多人說到CAP時,都知道分布式系統中這三者不能同時兼顧,只能滿足其二,這種說法不嚴謹,并不是簡單的三選二,而是在一致性和可用性二者中只能選其一,磁區容錯性是我們必須保證的,不能因為出現網路磁區導致系統癱瘓,而一致性和可用性可以根據我們的業務來舍棄其一,即我們只能實作AP方案或者CP方案;
55、什么是BASE理論?
BASE理論由CAP理論演化而來,因為CAP對系統設計的要求過高,實作CAP的代價太大;
BASE實質上是對CAP 中 AP 方案的一個補充,
BA:Basically Available(基本可用)
S:Soft-state(軟狀態) E:Eventually
Consistent(最終一致性)
BASE理論核心思想
犧牲強一致性,通過某些邏輯來達到資料的最終一致性
因為大多數系統對強一致性的依賴沒那么強,拋開磁區容錯性不談,一致性和可用性這兩者,可用性才是我們應該保證的,而對于一致性,我們往往只需要保證最終的資料一致即可;
最新2021整理收集的一些Java學習資料(都整理成檔案),有很多干貨,包含mysql,netty,spring,執行緒,spring cloud等詳細講解,也有詳細的學習規劃圖,面試題整理等,
點擊 : 一線大廠核心技術分享 分享最新技術,走在知識前端
點擊一起學習 暗號:csdn 最新學習資料+簡歷優化資源


后續會分享更多純干貨文章,希望能真正幫到你們,你們的支持就是我最大的動力!歡迎關注點贊啊!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/263438.html
標籤:java
上一篇:多執行緒的創建


