Java面試指南

0一、String 爲何是 final


  一、String 類是一個不可變類,被 final 修改的類不能被繼承,這樣提升了 String 類使用的安全性。
  二、String 類的主要變量 value[]都被設計成private final 的,這樣在多線程時,對 String對象的訪問是能夠保證安全。
  三、JVM 對 final 修飾的類進行了編譯優化,設計成 final,JVM 不用對相關方法在虛函數表中查詢,直接定位到 String 類的相關方法調用,提升了執行效率。面試


0二、Class.forName 和 和 ClassLoader 的區別


  Class.forNameClassLoader 都是用來裝載類的,對於類的裝載通常爲分三個階段加載、連接、編譯,它們裝載類的方式是有區別。
  首先看一下 Class.forName(..),forName(..)方法有一個重載方法 forName(className,boolean,ClassLoader)。 它有三個參數,第一個參數是類的包路徑,第二個參數是 boolean類型,爲 true 地表示 Loading 時會進行初始化,第三個就是指定一個加載器;當你調用class.forName(..)時,默認調用的是有三個參數的重載方法,第二個參數默認傳入 true,第三個參數默認使用的是當前類加載時用的加載器。
  ClassLoader.loadClass()也有一個重載方法,從源碼中能夠看出它默認調的是它的重載方法 loadClass(name, false),當第二參數爲 false 時,說明類加載時不會被連接。 這也是二者之間最大區別,前者在加載的時候已經初始化,後者在加載的時候尚未連接。若是你須要在加載時初始化一些東西,就要用 Class.forName 了,好比咱們經常使用的驅動加載,實際上它的註冊動做就是在加載時的一個靜態塊中完成的。因此它不能被 ClassLoader 加載代替。數據庫


0三、進程和線程的區別


image.png

  定義:進程是具備必定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。線程是進程的一個實體,是 CPU 調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
  特色:
  一、一個進程能夠擁有不少個線程,但每一個線程只屬於一個進程。
  二、線程相對進程而言,劃分尺度更小,併發性能更高。
  三、進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。
  四、線程必須依賴應用,在應用中調度,每一個線程必須執行的入口、出口、執行序列,線程是不可以獨立存在運行的。
  五、進程是資源分配的基本單位,線程是處理機調度的基本單位,全部的線程共享其所屬進程的全部資源與代碼。
  六、多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行。但操做系統並無將多個線程看作多個獨立的應用,來實現進程的調度和管理以及資源分配。
編程


0四、Java 的引用類型有哪幾種


  Java 自從 JDK1.2 版本開始,引入四種引用的類型,它們由強到弱依次是:強引用(StongReference) 、軟引用(SoftReference)、弱引用(**WeakReference **)、虛引用(PhantomReference) ,它們的各自特色及使用領域。
  強引用: 代碼中最經常使用看到的 Object o = new Object();這裏就是強引用,只要引用在,GC 就不回收它,若是 JVM 內存空間不足會拋出 OutOfMemoryError。
  軟引用:一般用描述一些有用但不是必需的對象,若是 JVM 內存不足時,會被 GC,一般被用來做爲一些緩存模塊的設計,並且不容易 OOM。
  弱引用:比軟引用還低級別的引用,軟引用通常是內存不足時回收,而弱引用只要被 GC 掃描線程發現就會回收掉,即使是 JVM 內存還充足的狀況下。
  虛引用:如其名,虛無般的存在,徹底不會影響對象的生命週期,若是一個對象僅持有虛引用,就如同沒有引用同樣,可能隨時被回收掉,通常會與強用隊列關聯使用,通常只用於對象回收的事件傳遞。數組


0五、Http報文結構


image.png

image.png
  上圖爲請求報文,包括「請求行①②③」、「請求頭部④」、「請求包體⑤」,請求行①②③之間是用空格隔開的,面試回答時撿主要的說,不必把全部的參數都介紹一下。
  請求報文包括 「請求行」「 請 求頭部」「 請求包體」;請求行中主要包括:請求方式、請求地址、Http 版本,它們之間用空格分開。請求頭部主要包括 : Accept: 告 訴 服 務 端 客 戶 端 接 受 什 麼 類 型 的 響 應 ; Accept-Language:客戶端可接受的天然語言; User-Agent:請求端的瀏覽器以及服務器類型; Accept-Encoding:客戶端可接受的編碼壓縮格式; Accept-Charset:可接受的應答的字符集; Host:請求的主名,容許多個域名同處一個 IP 地址,即虛擬主機; connection:鏈接方式(close 或 keep-alive); Cookie:存儲於客戶端擴展字段,向同一域名的服務端發送屬於該域的 cookie;空行:最後一個請求頭以後是一個空行,發送回車符和換行符,通知服務器如下再也不有請求頭;
  請求包體:也是請求正文,業務報文。
  響應報文包括**「狀態行」** 、 「響應頭部」「響應包體」;狀態行中包括:Http 協議版本、狀態碼以及狀態碼描述(經常使用狀態碼及描述, 500:服務器內部錯誤, 404:頁面找不到, 200OK:表示請求成功返回, 403:服務器收到請求但拒絕服務;其它的 1xx,2xx,3xx,4xx,5xx系,你們能夠網上查找一下,蠻重要,能說出來就行)。
  響應頭部, Server:響應服務器類型; Content-Type:響應數據的文檔類型; Cache-Control:響應輸出到客戶端後,服務端經過該報文頭屬告訴客戶端如何控制響應內容的緩存。
  響應包體,真正的業務報文,也就是請求指望的返回數據。


0六、Http 如何 處理長鏈接


  Http目前有兩個版本分別是 Http1.0 和 Http1.1,Http1.0 默認是短鏈接,若是須要長鏈接支持,須要加上Connection: Keep-alive;Http1.1 的版本默認是支持長鏈接請求的方式,能夠在抓取的請求中看到 Connection: Keep-alive,若是不想用長鏈接,須要在報文首部加上 Connection:close;對於默認的長鏈接能夠經過 Keep-Alive:timeout=N 進行超時時間設置。
  [追問]對長鏈接數據傳輸完成的識別:
  第一種:經過 Content-Length 指示的大小,若是傳的報文長度達到了 Content-Length,則認爲傳輸完成。
  第二種:動態請求生成的文件中每每不包含 Content-Length,每每是經過分塊傳輸入,服務器不能預先判斷文件大小,這裏要經過 Transfer-Encoding:chunked 模式來傳輸數據。Chunked 是按塊進行數據傳輸的,這時候就要根據 chunked 編碼來判斷,chunked 編碼的數據在最後有一個空 chunked 塊,代表本次傳輸數據結束。瀏覽器


0七、TCP三次握手和四次揮手


image.png
image.png
  圖來源於網上,若是要看懂這個圖,先來了解一下幾個簡單的概念:SYN 表示創建鏈接,FIN 表示關閉鏈接,ACK 表示響應,序號是隨機產生的但做用很大,這裏不詳細說了,這幾個關鍵字在面試的時候有必要先解釋一下。
  TCP 創建鏈接和斷開鏈接的操做有幾個很重要的關鍵字,分別: SYN 表示請求創建鏈接、ACK 表示響應、FIN 表示關閉鏈接請求、隨機序列 會 隨傳送報文 的 字節數增長 (SYN 、FIN 都) 算位的,即使沒有字節傳送,序列也會增長)
  TCP 創建鏈接的三次握手, 第一次握手:主機 A 發送位碼爲 syn=1,隨機產生 seq =200的數據包到服務器,主機 B 由 SYN=1 知道,A 要求創建聯機;
   第二次握手:主機 B 收到請求後要確認聯機信息,向 A 發送 ack 確認序列=(主機 A的 seq+1),syn=1,ack=1,隨機產生 seq=500 的包;
   第三次握手:主機 A 收到後檢查 ack 確認序列是否正確,即第一次發送的 seq number+1,以及位碼 ack 是否爲 1,若正確,主機 A 會再發送 ack number=(主機 B 的seq+1),ack=1,主機B收到後確認 seq 值與 ack=1 則鏈接創建成功。
   【三次握手總結】 主機A發 syn 給主機B,主機B回 ack,syn ,主機 A 回 ack ,三次握手,鏈接成功。
  四次揮手, 第一次揮手:主機A發送一個FIN,用來關閉客戶A到服務器B的數據傳送。
   第二 次揮手:主機B收到這個FIN,它發回一個ACK,確認序號爲收到的序號加 1。和SYN同樣,一個FIN將佔用一個序號。
   第三 次揮手:主機B關閉與主機A的鏈接,發送一個 FIN 給主機 A。


0八、線程啓動用 start 方法仍是run


  下面是 JDK 中 start()方法的源碼,能夠看到 start()調用的是 start0(),而start0()是一個 native 方法,經過註釋能夠知道,它的做用主要是爲線程分配系統資源的;而 run 只是一個普通的方法,因此線程的啓動是經過 start 方法實現的。緩存

public synchronized void start() {
		if (threadStatus != 0 || this != me)
			throw new IllegalThreadStateException();
		group.add(this);
		start0();
		if (stopBeforeStart) {
			stop0(throwableFromStop);
		}
	}
	private native void start0();
複製代碼

0九、ThreadLocal 的基本原理


  下面兩個問題是一個同窗面試的時候遇到的,網上也能看到,問題不難但平時不留意也不太容易回答。一、每一個線程的變量副本是存儲在哪裏的?二、threadlocal 是什麼時候初始化的?變量副本是如何爲共享的那個變量賦值的?回答這樣的問題,建議你們看一下 JDK相關 threadlocal 部分的源碼,下面只引用部分源碼來解釋說明。
  ThreadLocal 並不是是線程的本地實現,而是線程的本地變量,它歸附於具體的線程,爲每一個使用該變量的線程提供一個副本,每一個線程均可以獨立的操做這個副本,在外面看來,貌似每一個線程都有一份變量。線程的變量存在哪裏,這裏能夠結果 ThreadLocal 的源碼說明,這裏看一下 get 實現安全

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
複製代碼

  在 get 方法中,會先得到當前線程對象,而後傳到 getMap()中獲取 ThreadLocalMap對象,咱們要的變量副本是從 ThreadLocalMap 對象中取出來的,可見每一個線程的變量副本是保存在 ThreadLocalMap 對象裏,而跟一下代碼能夠看到 ThreadLocalMap 是在 Thread中聲明實現的,因此 每一個線程的變量副本就保 存 在相應線程的 ThreadLocalMap 對象中。
  第二個問題,能夠理解 ThreadLocal 如何把變量的副本複製而且初始化的(聲明和初始化),這裏看一下源碼中的 set 方法實現bash

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
複製代碼

  當第一次調用 set 方法時,獲取的 ThreadLocalMap 對象爲空,這裏會調用 createMap方法建立一個 ThreadLocalMap 對象,而且完成相應的初始,將 value 值存放進去。後面再次調用將會直接從線程中獲取ThreadLocalMap 對象,而後將副本保存進去。服務器


十、JVM內存泄露的緣由有哪些


  這個問題看似簡單,卻用一個問題考察了 JVM 不少個相關的知識點,回答這個問題你首先要了解 JVM 的結構、對象的分配與存儲、GC 的原理等,但你看到此的時候若是對上面的知識點還不是很熟悉的話,先翻開其相關知識點,以前都已經談到過,而後這個問題就容易回答了。
  JVM 結構上通常分爲堆內存、棧內存、方法區,內存泄露可能會發生在任何一個位置;在 JVM 劃分上方法區一般劃給堆,因此這兩塊能夠一塊兒。而棧內存一般是用來存放普通變量和對象引用,回收速度速度快,通常不會形成內存泄露,一旦溢出一般是棧內存大小分配不合理,或者可能顯示的將對象空間分配到棧內存來追求效率形成的。下面咱們重點探討堆內存的溢出(根據面試的場景來判斷有沒有談棧內存這塊)。
  參考: 首先形成 Java JVM 泄露的主要緣由:JVM 未及時的對垃圾進行回收形成的;當對象失去引用且持續佔有內存或無用對象的內存得不到及時釋放,從而形成內存空間的浪費稱爲內存泄漏。形成這種對象沒法及時釋放致使內存泄露的緣由,能夠簡單的爲歸分兩類。
  一是基 於設計方面 :一、對應用加載數據級別判斷失誤,從而致使 JVM 內存分配不合理(企業單機部署應用常見到)。二、應用請求的常鏈接設計,常鏈接會一直佔用後臺資源,不能及時釋放。三、數據庫操做時,存在不少耗時鏈接,致使大量資源不能釋放。四、大量的監聽設計等。
  二 是基於開發方面:一、大量靜態變量的使用(靜態變量的生成周期與應用一致),若是靜態引用指向的是集合或者數據,會一直佔用資源。二、不合理的方法使用,好比 jdk6以前的 substring 就可能致使內存泄露。三、數據庫鏈接未能及時關閉,剛工做不久的同窗容易忽略。四、單例模式使用,單例一般用來加載資源信息,但若是加載信息裏有大量的集合、數組等對象,這些資源會一直駐留內存中,不易釋放。五、在循環中建立複雜對象、一次性讀取加載大量信息到內存中,都有可能形成內存泄露。cookie


十一、OO的設計原則


  面向對象設計原則一般歸結爲五大類,
  第 一 「單 一職責原則」 (SRP):一個設計元素只作一件事,不要隨意耦合,多管閒事;
  第二 「開 放 封閉 原則」 (OCP):對變動關閉、對擴展開放,提倡基於接口設計,新的須要最好不要去變動已經完成的功能,能夠靈活經過接口擴展新功能;
  第三 「里氏 替換原則」 (LSP):子類能夠替換父類而且出如今父類可以出現的任何地方,這個也是提倡面向接口編程思想的;
  第四 「依賴 倒置原則」 (DIP):要依賴於抽象,不要依賴於具體,簡單的說就是面對抽象編程,不要過於依賴於細節;
  第 五 「接口 隔離原則」 (ISP):使用多個專門的接口比使用單個接口要好,在設計中不要把各類業務類型的東西放到一個接口中形成臃腫。


更多更新請關注........

相關文章
相關標籤/搜索