01、String 為什么是 final
??1枫攀、String 類是一個不可變類括饶,被 final 修改的類不能被繼承,這樣提高了 String 類使用的安全性来涨。
??2图焰、String 類的主要變量 value[]都被設計成private final 的,這樣在多線程時蹦掐,對 String對象的訪問是可以保證安全技羔。
??3、JVM 對 final 修飾的類進行了編譯優(yōu)化卧抗,設計成 final藤滥,JVM 不用對相關(guān)方法在虛函數(shù)表中查詢,直接定位到 String 類的相關(guān)方法調(diào)用社裆,提高了執(zhí)行效率拙绊。
02、Class.forName 和 和 ClassLoader 的區(qū)別
??Class.forName 和 ClassLoader 都是用來裝載類的,對于類的裝載一般為分三個階段加載标沪、鏈接榄攀、編譯,它們裝載類的方式是有區(qū)別金句。
??首先看一下 Class.forName(..)檩赢,forName(..)方法有一個重載方法 forName(className,boolean,ClassLoader)。 它有三個參數(shù)违寞,第一個參數(shù)是類的包路徑贞瞒,第二個參數(shù)是 boolean類型,為 true 地表示 Loading 時會進行初始化趁曼,第三個就是指定一個加載器憔狞;當你調(diào)用class.forName(..)時,默認調(diào)用的是有三個參數(shù)的重載方法彰阴,第二個參數(shù)默認傳入 true,第三個參數(shù)默認使用的是當前類加載時用的加載器瘾敢。
??ClassLoader.loadClass()也有一個重載方法,從源碼中可以看出它默認調(diào)的是它的重載方法 loadClass(name, false)尿这,當?shù)诙?shù)為 false 時簇抵,說明類加載時不會被鏈接。 這也是兩者之間最大區(qū)別射众,前者在加載的時候已經(jīng)初始化碟摆,后者在加載的時候還沒有鏈接。如果你需要在加載時初始化一些東西叨橱,就要用 Class.forName 了典蜕,比如我們常用的驅(qū)動加載,實際上它的注冊動作就是在加載時的一個靜態(tài)塊中完成的。所以它不能被 ClassLoader 加載代替。
03笨使、進程和線程的區(qū)別
??定義:進程是具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位嗜憔。線程是進程的一個實體,是 CPU 調(diào)度和分派的基本單位,它是比進程更小的能獨立運行的基本單位悄泥。
??特點:
??1、一個進程可以擁有很多個線程,但每個線程只屬于一個進程。
??2火的、線程相對進程而言,劃分尺度更小淑倾,并發(fā)性能更高馏鹤。
??3、進程在執(zhí)行過程中擁有獨立的內(nèi)存單元娇哆,而多個線程共享內(nèi)存湃累,從而極大地提高了程序的運行效率勃救。
??4、線程必須依賴應用脱茉,在應用中調(diào)度,每個線程必須執(zhí)行的入口垄开、出口琴许、執(zhí)行序列,線程是不能夠獨立存在運行的溉躲。
??5榜田、進程是資源分配的基本單位,線程是處理機調(diào)度的基本單位锻梳,所有的線程共享其所屬進程的所有資源與代碼箭券。
??6、多線程的意義在于一個應用程序中疑枯,有多個執(zhí)行部分可以同時執(zhí)行辩块。但操作系統(tǒng)并沒有將多個線程看做多個獨立的應用,來實現(xiàn)進程的調(diào)度和管理以及資源分配荆永。
04废亭、Java 的引用類型有哪幾種
??Java 自從 JDK1.2 版本開始,引入四種引用的類型具钥,它們由強到弱依次是:強引用(StongReference) 豆村、軟引用(SoftReference)、弱引用(WeakReference )骂删、虛引用(PhantomReference) 掌动,它們的各自特點及使用領域。
??強引用: 代碼中最常用看到的 Object o = new Object();這里就是強引用宁玫,只要引用在粗恢,GC 就不回收它,如果 JVM 內(nèi)存空間不足會拋出 OutOfMemoryError欧瘪。
??軟引用:通常用描述一些有用但不是必需的對象适滓,如果 JVM 內(nèi)存不足時,會被 GC恋追,通常被用來作為一些緩存模塊的設計凭迹,而且不容易 OOM。
??弱引用:比軟引用還低級別的引用苦囱,軟引用一般是內(nèi)存不足時回收嗅绸,而弱引用只要被 GC 掃描線程發(fā)現(xiàn)就會回收掉,即便是 JVM 內(nèi)存還充足的情況下撕彤。
??虛引用:如其名鱼鸠,虛無般的存在猛拴,完全不會影響對象的生命周期,如果一個對象僅持有虛引用蚀狰,就如同沒有引用一樣愉昆,可能隨時被回收掉,一般會與強用隊列關(guān)聯(lián)使用麻蹋,一般只用于對象回收的事件傳遞跛溉。
05、Http報文結(jié)構(gòu)
??上圖為請求報文扮授,包括“請求行①②③”芳室、“請求頭部④”、“請求包體⑤”刹勃,請求行①②③之間是用空格隔開的堪侯,面試回答時撿主要的說,沒必要把所有的參數(shù)都介紹一下荔仁。
??請求報文包括 “請求行” 伍宦、“ 請 求頭部” 、“ 請求包體”乏梁;請求行中主要包括:請求方式雹拄、請求地址、Http 版本掌呜,它們之間用空格分開滓玖。請求頭部主要包括 : Accept: 告 訴 服 務 端 客 戶 端 接 受 什 么 類 型 的 響 應 ;Accept-Language:客戶端可接受的自然語言;User-Agent:請求端的瀏覽器以及服務器類型;Accept-Encoding:客戶端可接受的編碼壓縮格式; Accept-Charset:可接受的應答的字符集;Host:請求的主名质蕉,允許多個域名同處一個 IP 地址势篡,即虛擬主機;connection:連接方式(close 或 keep-alive);Cookie:存儲于客戶端擴展字段,向同一域名的服務端發(fā)送屬于該域的 cookie;空行:最后一個請求頭之后是一個空行模暗,發(fā)送回車符和換行符禁悠,通知服務器以下不再有請求頭;
??請求包體:也是請求正文,業(yè)務報文兑宇。
??響應報文包括“狀態(tài)行” 碍侦、“響應頭部” 、“響應包體”隶糕;狀態(tài)行中包括:Http 協(xié)議版本瓷产、狀態(tài)碼以及狀態(tài)碼描述(常用狀態(tài)碼及描述,500:服務器內(nèi)部錯誤枚驻,404:頁面找不到濒旦,200OK:表示請求成功返回,403:服務器收到請求但拒絕服務再登;其它的 1xx,2xx,3xx,4xx,5xx系尔邓,大家可以網(wǎng)上查找一下晾剖,蠻重要,能說出來就行)梯嗽。
??響應頭部齿尽,Server:響應服務器類型;Content-Type:響應數(shù)據(jù)的文檔類型灯节;Cache-Control:響應輸出到客戶端后循头,服務端通過該報文頭屬告訴客戶端如何控制響應內(nèi)容的緩存。
??響應包體显晶,真正的業(yè)務報文贷岸,也就是請求期望的返回數(shù)據(jù)壹士。
06磷雇、Http 如何 處理長連接
??Http目前有兩個版本分別是 Http1.0 和 Http1.1,Http1.0 默認是短連接躏救,如果需要長連接支持唯笙,需要加上Connection: Keep-alive;Http1.1 的版本默認是支持長連接請求的方式盒使,可以在抓取的請求中看到 Connection: Keep-alive崩掘,如果不想用長連接,需要在報文首部加上 Connection:close;對于默認的長連接可以通過 Keep-Alive:timeout=N 進行超時時間設置少办。
??[追問]對長連接數(shù)據(jù)傳輸完成的識別:第一種:通過 Content-Length 指示的大小苞慢,如果傳的報文長度達到了 Content-Length,則認為傳輸完成英妓。
??第二種:動態(tài)請求生成的文件中往往不包含 Content-Length挽放,往往是通過分塊傳輸入,服務器不能預先判斷文件大小蔓纠,這里要通過 Transfer-Encoding:chunked 模式來傳輸數(shù)據(jù)辑畦。Chunked 是按塊進行數(shù)據(jù)傳輸?shù)模@時候就要根據(jù) chunked 編碼來判斷腿倚,chunked 編碼的數(shù)據(jù)在最后有一個空 chunked 塊纯出,表明本次傳輸數(shù)據(jù)結(jié)束。
07敷燎、TCP三次握手和四次揮手
??圖來源于網(wǎng)上暂筝,如果要看懂這個圖,先來了解一下幾個簡單的概念:SYN 表示建立連接硬贯,F(xiàn)IN 表示關(guān)閉連接乖杠,ACK 表示響應,序號是隨機產(chǎn)生的但作用很大澄成,這里不詳細說了胧洒,這幾個關(guān)鍵字在面試的時候有必要先解釋一下畏吓。
??TCP 建立連接和斷開連接的操作有幾個很重要的關(guān)鍵字,分別:SYN 表示請求建立連接卫漫、ACK 表示響應菲饼、FIN 表示關(guān)閉連接請求、隨機序列 會 隨傳送報文 的 字節(jié)數(shù)增加 (SYN 列赎、FIN 都) 算位的宏悦,即便沒有字節(jié)傳送,序列也會增加)包吝。
??TCP 建立連接的三次握手饼煞, 第一次握手:主機 A 發(fā)送位碼為 syn=1,隨機產(chǎn)生 seq =200的數(shù)據(jù)包到服務器,主機 B 由 SYN=1 知道诗越,A 要求建立聯(lián)機砖瞧;
??第二次握手:主機 B 收到請求后要確認聯(lián)機信息,向 A 發(fā)送 ack 確認序列=(主機 A的 seq+1),syn=1,ack=1,隨機產(chǎn)生 seq=500 的包嚷狞;
??第三次握手:主機 A 收到后檢查 ack 確認序列是否正確块促,即第一次發(fā)送的 seq number+1,以及位碼 ack 是否為 1,若正確床未,主機 A 會再發(fā)送 ack number=(主機 B 的seq+1),ack=1竭翠,主機B收到后確認 seq 值與 ack=1 則連接建立成功。
??【三次握手總結(jié)】 主機A發(fā) syn 給主機B薇搁,主機B回 ack,syn 斋扰,主機 A 回 ack ,三次握手啃洋,連接成功传货。
??四次揮手, 第一次揮手:主機A發(fā)送一個FIN裂允,用來關(guān)閉客戶A到服務器B的數(shù)據(jù)傳送损离。
??第二 次揮手:主機B收到這個FIN,它發(fā)回一個ACK绝编,確認序號為收到的序號加 1僻澎。和SYN一樣,一個FIN將占用一個序號十饥。
??第三 次揮手:主機B關(guān)閉與主機A的連接窟勃,發(fā)送一個 FIN 給主機 A。
08逗堵、線程啟動用 start 方法還是run
??下面是 JDK 中 start()方法的源碼秉氧,可以看到 start()調(diào)用的是 start0(),而start0()是一個 native 方法蜒秤,通過注釋可以知道汁咏,它的作用主要是為線程分配系統(tǒng)資源的亚斋;而 run 只是一個普通的方法,所以線程的啟動是通過 start 方法實現(xiàn)的攘滩。
public synchronized void start() {
if (threadStatus != 0 || this != me)
throw new IllegalThreadStateException();
group.add(this);
start0();
if (stopBeforeStart) {
stop0(throwableFromStop);
}
}
private native void start0();
09帅刊、ThreadLocal 的基本原理
??下面兩個問題是一個同學面試的時候遇到的,網(wǎng)上也能看到漂问,問題不難但平時不留意也不太容易回答赖瞒。1、每個線程的變量副本是存儲在哪里的蚤假?2栏饮、threadlocal 是何時初始化的?變量副本是如何為共享的那個變量賦值的磷仰?回答這樣的問題袍嬉,建議大家看一下 JDK相關(guān) threadlocal 部分的源碼,下面只引用部分源碼來解釋說明芒划。
??ThreadLocal 并非是線程的本地實現(xiàn)冬竟,而是線程的本地變量欧穴,它歸附于具體的線程民逼,為每個使用該變量的線程提供一個副本,每個線程都可以獨立的操作這個副本涮帘,在外面看來拼苍,貌似每個線程都有一份變量。線程的變量存在哪里调缨,這里可以結(jié)果 ThreadLocal 的源碼說明疮鲫,這里看一下 get 實現(xiàn)
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中聲明實現(xiàn)的燕侠,所以 每個線程的變量副本就保 存 在相應線程的 ThreadLocalMap 對象中。
??第二個問題立莉,可以理解 ThreadLocal 如何把變量的副本復制并且初始化的(聲明和初始化)绢彤,這里看一下源碼中的 set 方法實現(xiàn)
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
??當?shù)谝淮握{(diào)用 set 方法時,獲取的 ThreadLocalMap 對象為空蜓耻,這里會調(diào)用 createMap方法創(chuàng)建一個 ThreadLocalMap 對象茫舶,并且完成相應的初始,將 value 值存放進去刹淌。后面再次調(diào)用將會直接從線程中獲取ThreadLocalMap 對象饶氏,然后將副本保存進去讥耗。
10、JVM內(nèi)存泄露的原因有哪些
??這個問題看似簡單疹启,卻用一個問題考察了 JVM 很多個相關(guān)的知識點葛账,回答這個問題你首先要了解 JVM 的結(jié)構(gòu)、對象的分配與存儲皮仁、GC 的原理等籍琳,但你看到此的時候如果對上面的知識點還不是很熟悉的話,先翻開其相關(guān)知識點贷祈,之前都已經(jīng)談到過趋急,然后這個問題就容易回答了。
??JVM 結(jié)構(gòu)上一般分為堆內(nèi)存势誊、棧內(nèi)存呜达、方法區(qū),內(nèi)存泄露可能會發(fā)生在任何一個位置粟耻;在 JVM 劃分上方法區(qū)通常劃給堆查近,所以這兩塊可以一起。而棧內(nèi)存通常是用來存放普通變量和對象引用挤忙,回收速度速度快霜威,一般不會造成內(nèi)存泄露,一旦溢出通常是棧內(nèi)存大小分配不合理册烈,或者可能顯示的將對象空間分配到棧內(nèi)存來追求效率造成的戈泼。下面我們重點探討堆內(nèi)存的溢出(根據(jù)面試的場景來判斷有沒有談棧內(nèi)存這塊)。
??參考: 首先造成 Java JVM 泄露的主要原因:JVM 未及時的對垃圾進行回收造成的赏僧;當對象失去引用且持續(xù)占有內(nèi)存或無用對象的內(nèi)存得不到及時釋放大猛,從而造成內(nèi)存空間的浪費稱為內(nèi)存泄漏。造成這種對象無法及時釋放導致內(nèi)存泄露的原因淀零,可以簡單的為歸分兩類挽绩。
??一是基 于設計方面 :1、對應用加載數(shù)據(jù)級別判斷失誤驾中,從而導致 JVM 內(nèi)存分配不合理(企業(yè)單機部署應用常見到)唉堪。2、應用請求的常連接設計哀卫,常連接會一直占用后臺資源巨坊,不能及時釋放。3此改、數(shù)據(jù)庫操作時趾撵,存在很多耗時連接,導致大量資源不能釋放。4占调、大量的監(jiān)聽設計等暂题。
??二 是基于開發(fā)方面:1、大量靜態(tài)變量的使用(靜態(tài)變量的生成周期與應用一致)究珊,如果靜態(tài)引用指向的是集合或者數(shù)據(jù)薪者,會一直占用資源。2剿涮、不合理的方法使用言津,比如 jdk6之前的 substring 就可能導致內(nèi)存泄露。3取试、數(shù)據(jù)庫連接未能及時關(guān)閉悬槽,剛工作不久的同學容易忽略。4瞬浓、單例模式使用初婆,單例通常用來加載資源信息,但如果加載信息里有大量的集合猿棉、數(shù)組等對象磅叛,這些資源會一直駐留內(nèi)存中,不易釋放萨赁。5弊琴、在循環(huán)中創(chuàng)建復雜對象、一次性讀取加載大量信息到內(nèi)存中位迂,都有可能造成內(nèi)存泄露访雪。
11详瑞、OO的設計原則
??面向?qū)ο笤O計原則通常歸結(jié)為五大類掂林,
??第 一 “單 一職責原則” (SRP):一個設計元素只做一件事,不要隨意耦合坝橡,多管閑事泻帮;
??第二 “開 放 封閉 原則” (OCP):對變更關(guān)閉、對擴展開放计寇,提倡基于接口設計锣杂,新的需要最好不要去變更已經(jīng)完成的功能,可以靈活通過接口擴展新功能番宁;
??第三 “里氏 替換原則” (LSP):子類可以替換父類并且出現(xiàn)在父類能夠出現(xiàn)的任何地方元莫,這個也是提倡面向接口編程思想的;
??第四 “依賴 倒置原則” (DIP):要依賴于抽象蝶押,不要依賴于具體踱蠢,簡單的說就是面對抽象編程,不要過于依賴于細節(jié)棋电;
??第 五 “接口 隔離原則” (ISP):使用多個專門的接口比使用單個接口要好茎截,在設計中不要把各種業(yè)務類型的東西放到一個接口中造成臃腫苇侵。