天天看點

各大公司Java面試題超詳細總結(一)

Java記憶體模型:

Java虛拟機規範中将Java運作時資料分為六種。

1.程式計數器:是一個資料結構,用于儲存目前正常執行的程式的記憶體位址。Java虛拟機的多線程就是通過線程輪流切換并配置設定處理器時間來實作的,為了線程切換後能恢複到正确的位置,每條線程都需要一個獨立的程式計數器,互不影響,該區域為“線程私有”。

2.Java虛拟機棧:線程私有的,與線程生命周期相同,用于存儲局部變量表,操作棧,方法傳回值。局部變量表放着基本資料類型,還有對象的引用。

3.本地方法棧:跟虛拟機棧很像,不過它是為虛拟機使用到的Native方法服務。

4.Java堆:所有線程共享的一塊記憶體區域,對象執行個體幾乎都在這配置設定記憶體。

5.方法區:各個線程共享的區域,儲存虛拟機加載的類資訊,常量,靜态變量,編譯後的代碼。

6.運作時常量池:代表運作時每個class檔案中的常量表。包括幾種常量:編譯時的數字常量、方法或者域的引用。

“你能不能談談,java GC是在什麼時候,對什麼東西,做了什麼事情?”

在什麼時候:

1.新生代有一個Eden區和兩個survivor區,首先将對象放入Eden區,如果空間不足就向其中的一個survivor區上放,如果仍然放不下就會引發一次發生在新生代的minor GC,将存活的對象放入另一個survivor區中,然後清空Eden和之前的那個survivor區的記憶體。在某次GC過程中,如果發現仍然又放不下的對象,就将這些對象放入老年代記憶體裡去。

2.大對象以及長期存活的對象直接進入老年區。

3.當每次執行minor GC的時候應該對要晉升到老年代的對象進行分析,如果這些馬上要到老年區的老年對象的大小超過了老年區的剩餘大小,那麼執行一次Full GC以盡可能地獲得老年區的空間。

對什麼東西:從GC Roots搜尋不到,而且經過一次标記清理之後仍沒有複活的對象。

做什麼: 新生代:複制清理; 老年代:标記-清除和标記-壓縮算法; 永久代:存放Java中的類和加載類的類加載器本身。

GC Roots都有哪些: 1. 虛拟機棧中的引用的對象 2. 方法區中靜态屬性引用的對象,常量引用的對象 3. 本地方法棧中JNI(即一般說的Native方法)引用的對象。

Volatile和Synchronized四個不同點:

1 粒度不同,前者針對變量 ,後者鎖對象和類

2 syn阻塞,volatile線程不阻塞

3 syn保證三大特性,volatile不保證原子性

4 syn編譯器優化,volatile不優化 volatile具備兩種特性:

1.保證此變量對所有線程的可見性,指一條線程修改了這個變量的值,新值對于其他線程來說是可見的,但并不是多線程安全的。

2.禁止指令重排序優化。

Volatile如何保證記憶體可見性:

1.當寫一個volatile變量時,JMM會把該線程對應的本地記憶體中的共享變量重新整理到主記憶體。

2.當讀一個volatile變量時,JMM會把該線程對應的本地記憶體置為無效。線程接下來将從主記憶體中讀取共享變量。

同步:就是一個任務的完成需要依賴另外一個任務,隻有等待被依賴的任務完成後,依賴任務才能完成。

異步:不需要等待被依賴的任務完成,隻是通知被依賴的任務要完成什麼工作,隻要自己任務完成了就算完成了,被依賴的任務是否完成會通知回來。(異步的特點就是通知)。 打電話和發短信來比喻同步和異步操作。

阻塞:CPU停下來等一個慢的操作完成以後,才會接着完成其他的工作。

非阻塞:非阻塞就是在這個慢的執行時,CPU去做其他工作,等這個慢的完成後,CPU才會接着完成後續的操作。

非阻塞會造成線程切換增加,增加CPU的使用時間能不能補償系統的切換成本需要考慮。

Servlet和Filter的差別:

整的流程是:Filter對使用者請求進行預處理,接着将請求交給Servlet進行處理并生成響應,最後Filter再對伺服器響應進行後處理。

Filter有如下幾個用處:

Filter可以進行對特定的url請求和相應做預處理和後處理。

在HttpServletRequest到達Servlet之前,攔截客戶的HttpServletRequest。

根據需要檢查HttpServletRequest,也可以修改HttpServletRequest頭和資料。

在HttpServletResponse到達用戶端之前,攔截HttpServletResponse。

根據需要檢查HttpServletResponse,也可以修改HttpServletResponse頭和資料。

實際上Filter和Servlet極其相似,差別隻是Filter不能直接對使用者生成響應。實際上Filter裡doFilter()方法裡的代碼就是從多個Servlet的service()方法裡抽取的通用代碼,通過使用Filter可以實作更好的複用。

Filter和Servlet的生命周期:

1.Filter在web伺服器啟動時初始化

2.如果某個Servlet配置了 1 ,該Servlet也是在Tomcat(Servlet容器)啟動時初始化。

3.如果Servlet沒有配置1 ,該Servlet不會在Tomcat啟動時初始化,而是在請求到來時初始化。

4.每次請求, Request都會被初始化,響應請求後,請求被銷毀。

5.Servlet初始化後,将不會随着請求的結束而登出。

6.關閉Tomcat時,Servlet、Filter依次被登出。

HashMap與HashTable的差別。

1、HashMap是非線程安全的,HashTable是線程安全的。

2、HashMap的鍵和值都允許有null值存在,而HashTable則不行。

3、因為線程安全的問題,HashMap效率比HashTable的要高。

HashMap的實作機制:

維護一個每個元素是一個連結清單的數組,而且連結清單中的每個節點是一個Entry[]鍵值對的資料結構。

實作了數組+連結清單的特性,查找快,插入删除也快。

對于每個key,他對應的數組索引下标是 int i = hash(key.hashcode)&(len-1);

每個新加入的節點放在連結清單首,然後該新加入的節點指向原連結清單首

程序間的通信方式

管道( pipe ):管道是一種半雙工的通信方式,資料隻能單向流動,而且隻能在具有親緣關系的程序間使用。程序的親緣關系通常是指父子程序關系。

有名管道 (named pipe) : 有名管道也是半雙工的通信方式,但是它允許無親緣關系程序間的通信。

信号量( semophore ) : 信号量是一個計數器,可以用來控制多個程序對共享資源的通路。它常作為一種鎖機制,防止某程序正在通路共享資源時,其他程序也通路該資源。是以,主要作為程序間以及同一程序内不同線程之間的同步手段。

消息隊列( message queue ) : 消息隊列是由消息的連結清單,存放在核心中并由消息隊列辨別符辨別。消息隊列克服了信号傳遞資訊少、管道隻能承載無格式位元組流以及緩沖區大小受限等缺點。

信号 ( sinal ) : 信号是一種比較複雜的通信方式,用于通知接收程序某個事件已經發生。

共享記憶體( shared memory ) :共享記憶體就是映射一段能被其他程序所通路的記憶體,這段共享記憶體由一個程序建立,但多個程序都可以通路。共享記憶體是最快的 IPC 方式,它是針對其他程序間通信方式運作效率低而專門設計的。它往往與其他通信機制,如信号量,配合使用,來實作程序間的同步和通信。

套接字( socket ) : 套解口也是一種程序間通信機制,與其他通信機制不同的是,它可用于不同機器間的程序通信。

JAVA 中堆和棧的差別,說下java 的記憶體機制

a.基本資料類型比變量和對象的引用都是在棧配置設定的

b.堆記憶體用來存放由new建立的對象和數組

c.類變量(static修飾的變量),程式在一加載的時候就在堆中為類變量配置設定記憶體,堆中的記憶體位址存放在棧中

d.執行個體變量:當你使用java關鍵字new的時候,系統在堆中開辟并不一定是連續的空間配置設定給變量,是根據零散的堆記憶體位址,通過雜湊演算法換算為一長串數字以表征這個變量在堆中的”實體位置”,執行個體變量的生命周期–當執行個體變量的引用丢失後,将被GC(垃圾回收器)列入可回收“名單”中,但并不是馬上就釋放堆中記憶體

e.局部變量: 由聲明在某方法,或某代碼段裡(比如for循環),執行到它的時候在棧中開辟記憶體,當局部變量一但脫離作用域,記憶體立即釋放

分布式Session架構

配置伺服器,Zookeeper叢集管理伺服器可以統一管理所有伺服器的配置檔案

共享這些Session存儲在一個分布式緩存中,可以随時寫入和讀取,而且性能要很好,如Memcache,Tair。

封裝一個類繼承自HttpSession,将Session存入到這個類中然後再存入分布式緩存中

由于Cookie不能跨域通路,要實作Session同步,要同步SessionID寫到不同域名下。

代理的共有優點:業務類隻需要關注業務邏輯本身,保證了業務類的重用性。

Java靜态代理:

代理對象和目标對象實作了相同的接口,目标對象作為代理對象的一個屬性,具體接口實作中,代理對象可以在調用目标對象相應方法前後加上其他業務處理邏輯。

缺點:一個代理類隻能代理一個業務類。如果業務類增加方法時,相應的代理類也要增加方法。

Java動态代理:

Java動态代理是寫一個類實作InvocationHandler接口,重寫Invoke方法,在Invoke方法可以進行增強處理的邏輯的編寫,這個公共代理類在運作的時候才能明确自己要代理的對象,同時可以實作該被代理類的方法的實作,然後在實作類方法的時候可以進行增強處理。

實際上:代理對象的方法 = 增強處理 + 被代理對象的方法

JDK和CGLIB生成動态代理類的差別:

JDK動态代理隻能針對實作了接口的類生成代理(執行個體化一個類)。此時代理對象和目标對象實作了相同的接口,目标對象作為代理對象的一個屬性,具體接口實作中,可以在調用目标對象相應方法前後加上其他業務處理邏輯

CGLIB是針對類實作代理,主要是對指定的類生成一個子類(沒有執行個體化一個類),覆寫其中的方法 。

Spring AOP應用場景

性能檢測,通路控制,日志管理,事務等。

預設的政策是如果目标類實作接口,則使用JDK動态代理技術,如果目标對象沒有實作接口,則預設會采用CGLIB代理

Spring IOC (控制反轉,依賴注入)

Spring支援三種依賴注入方式,分别是屬性(Setter方法)注入,構造注入和接口注入。

在Spring中,那些組成應用的主體及由Spring IOC容器所管理的對象被稱之為Bean。

Spring的IOC容器通過反射的機制執行個體化Bean并建立Bean之間的依賴關系。

簡單地講,Bean就是由Spring IOC容器初始化、裝配及被管理的對象。

擷取Bean對象的過程,首先通過Resource加載配置檔案并啟動IOC容器,然後通過getBean方法擷取bean對象,就可以調用他的方法。

Spring Bean的作用域:

Singleton:Spring IOC容器中隻有一個共享的Bean執行個體,一般都是Singleton作用域。

Prototype:每一個請求,會産生一個新的Bean執行個體。

Request:每一次http請求會産生一個新的Bean執行個體。

類加載器工作機制:

1.裝載:将Java二進制代碼導入jvm中,生成Class檔案。

2.連接配接:a)校驗:檢查載入Class檔案資料的正确性 b)準備:給類的靜态變量配置設定存儲空間 c)解析:将符号引用轉成直接引用

3:初始化:對類的靜态變量,靜态方法和靜态代碼塊執行初始化工作。

雙親委派模型:類加載器收到類加載請求,首先将請求委派給父類加載器完成 使用者自定義加載器->應用程式加載器->擴充類加載器->啟動類加載器。