1.private 修飾的方法可以通過反射通路,那麼private修飾的意義在哪裡?
因為private本職上是一種程式設計思想的展現,即封裝的思想,用private修飾的變量和方法本質上都是不對外提供的,那麼對于調用者來說,這些就是不可見的,是以它并不對這個事情負責;尤其是在一個大型工程裡面來說,各個子產品的業務範疇的定義是需要非常明确的,不歸自己管的絕對不想管,是以如果我定義了一個private,那就意味着你盡量别用,如果你非得用,那麼好,出了問題你負責,我不管的。
2.Java的類初始化順序
總的原則就是先基類後自己,先靜态後普通代碼塊後構造函數:
是以,基類的靜态字段和靜态代碼塊-->>派生類的靜态代碼塊-->>基類普通代碼塊和基類普通成員變量-->>基類構造函數-->>派生類普通代碼塊-->>派生類構造函數
至于為什麼先基類後自己,這個是很容易想明白的,因為加入我派生類依賴基類的某個變量,那肯定是要先基類準備好了,自己再去是更好的。而為什麼先靜态後普通代碼塊呢?我個人了解,是因為靜态變量是屬于某個類的,隻要這個類被使用了,靜态的就要先加載,至于後面執行個體化的哪個,它不管的。
3.對方法區和永久區的了解以及它們之間的關系
方法區是jvm規範裡要求的,永久區是Hotspot虛拟機對方法區的具體實作,前者是規範,後者是實作方式。而Java8其實又做了修改,已經不再有永久區了,而是metaspace;
4.一個java檔案有3個類,編譯後有幾個class檔案
這個應該是有三個檔案才是。
5.局部變量使用前需要顯式地指派,否則編譯通過不了,為什麼這麼設計
因為編譯器識别不出成員變量究竟會在啥時候使用和指派,是以可以給個預設值,但是局部變量是很明顯的,是以這種時候編譯器給出這種限制可以極大的避免一些問題的産生。
6.ReadWriteLock讀寫之間互斥嗎
讀鎖和讀鎖之間不互斥,其他都是互斥的。
讀寫鎖其實是有兩個鎖,一個讀,一個寫,他們共享同一個sync,但是分别用的是共享模式和非共享模式,這個是用state的頭16位和尾16位去做的。
7.Semaphore拿到執行權的線程之間是否互斥
不是互斥的。
8.寫一個單例模式public class Singleton{
private static class SingltonHandler{
private staticSingleton singleton=new Singleton();
}
publicstaticSingletongetInstance() {
return SingltonHandler.singleton;
}
}
最簡單的寫法就是靜态内部類,靜态内部類維護一個外部的變量,而不用的時候也不會去加載它。
9.B樹和B+樹是解決什麼樣的問題的,怎樣演化過來,之間差別
B+樹和B樹之間的核心差別就在于,mysql的每個節點是存儲在一個頁上面的,而對于B樹來說每個頁所存儲的資料包含三個部分(key,子節點指針和data),是以我們希望每個頁上存儲更多的子節點這樣能夠保證B樹更大的度,也就帶來了更小的樹深度,是以這就是為什麼B+樹把真實的data都放在葉子節點上的原因;
10.寫一個生産者消費者模式
可以用lock和condition或者wait+notify的方式來寫;
11.寫一個死鎖
這個很簡單,兩個線程互斥的取兩份資源即可;
12.cpu的100%定位
先找到top程序,然後jstack,然後找這個程序中的top線程,最後轉換下16進制就好了;
13.int a=1是原子操作嗎?
是的,a++不是,long 不是,long被volatile修飾就是了
14.for循環可以删除arraylist嗎?
不可以,因為删除元素時涉及到數組元素的移動。
15.新的任務送出到線程池,線程池是怎樣處理
這個其實是個考察線程池原理的問題,線程池包含核心線程數,最大線程數,和隊列,一般如果沒有到核心線程數,會擴大核心線程數,如果是超過核心線程數,不到隊列數,會加到隊列裡,如果是超過了隊列+核心線程數,會擴容到最大線程數,最後,都不行,會執行拒絕政策;
16.AQS和CAS原理
CAS待梳理;
17.synchronized底層實作原理
是在Java對象的頭上mark word裡面存放的有:
鎖狀态 對象的hashcode 對象分代年齡 是否是偏向鎖 鎖标志位
鎖一共有四種狀态,從低到高分别是:無鎖、偏向鎖、輕量級鎖和重量級鎖,會随着競争情況逐漸更新但隻能更新不能降級。
偏向鎖是在棧幀中記錄擁有偏向鎖的線程id,如果是同個線程則直接擷取,如果不是同個線程,那此線程會失敗,并且通知這個偏向鎖撤銷;
重點看下鎖更新:
偏向鎖:如果有其他線程競争鎖,而且此時鎖的擁有者還無法釋放的時候,就會更新為輕量級鎖;
輕量級自選鎖:如果自旋次數超過了某個門檻值,或者線程1在執行,線程2在自旋等待,線程3又過來競争的時候,就膨脹成重量級鎖;
18.volatile的作用
防止指令重排,可見性;
提到volatile就不得不說作業系統的解決各個cpu的高速緩存之間的緩存一緻性的問題的思路,
1.在總線上加lock,但是這種的話各個cpu都阻塞了;
2.緩存一緻性協定,如果發現操作的是共享變量,那就通知其他的cpu讓這個緩存失效,這也就是會引發所謂的一緻性協定風暴和緩存行失效的問題,也就是為什麼要用clh隊列做aqs的問題。
19.AOP和IOC原理
待梳理;
20.Spring怎樣解決循環依賴的問題
利用緩存,先提前把各個bean暴露出去;
22.dispatchServlet是怎樣分發任務的?
mvc待整理;
23.jvm gc複制算法
24.注解的原理
25.程序間通信方式;
26.Reentrantlock是可重入鎖,啥是可重入?
就是可以同一個線程進入兩次,這個取決于它的實作,tryacquire的時候,發現如果目前線程和占領它的鎖的線程是同個線程,就會直接擷取鎖;
27.線程如果異常會怎樣?
線程的異常必須要線程自身去捕獲并處理,如果不處理,這個線程會死掉,而且,這個線程的異常并不會被主線程所捕獲;
28.hashMap原理;
1.8以後版本:
1.7以前的版本就不看了,因為目前核心重點在關注的就是1.8了,首先還是一個連結清單或者叫數組吧,然後每個節點上下面不一定是連結清單了,還有可能是紅黑樹,這個門檻值是8;
先看put,如果不存在,直接插入就好,然後如果存在,那就進入到裡面去,先取出對應位置的值放着;
首先如果是紅黑樹,直接插進去,如果是連結清單,先插進去,再轉換成紅黑樹。
最後判斷這取出的node是不是空,如果不空,覆寫掉它的value;
最後,如果整個的值超過了門檻值,就擴容;
關于擴容:
第一步:把數組擴大到兩倍,把門檻值擴大到兩倍
第二步:把原有的資料放到新的數組裡,需要重新做hash,注意,這裡的hash用的還是很巧妙的,它用的是hashcode&&table.size()-1 ,這樣其實就把這個值散列到表的每個位置上面去,是以就也要求這個表的size必須要是powerof 2;說起來如何判斷一個數字是powerof2也有個巧妙的方法,就是直接n&&(n-1)==0;
至于concurrentHashmap,最難的地方在于擴容,擴容的過程是這樣的:先把這個擴容的過程分成多個子任務,然後每個子任務去做各自的擴容;
29.jvm虛拟機;
30.Java類加載和雙親委派
其實整個類加載有個非常核心的關鍵之處,就在于Java把擷取class資訊轉換成byte數組這一步驟外包出去了,就是它不管你從哪裡擷取這個byte數組,你完全可以自行擷取,這樣才引出了各種各樣的類加載器和雙親委派的類加載機制。
Java有兩類加載器,一類是系統提供的,另外一類是自定義的:
系統提供的有三個:
bootstrap :負責加載核心類庫的
extension:擴充庫的ext
app:classPath下的;
所謂SPI,其實就是自己定義好了interface,但是不實作,讓别人來實作,但是這裡有幾個問題:
1.别人的實作你怎麼知道在哪兒呢?
那就放在固定的地方去META/INF底下,讀取某個約定好的位置的資訊,然後取出來看看叫啥名字
2.自己的類可能是引導類加載器加載的,而别人的實作無法用引導類加載器加載?
Java 應用運作的初始線程的上下文類加載器是系統類加載器。線上程中運作的代碼可以通過此類加載器來加載類和資源。