Java面試300題(2020年版,3-5年面試題重點突破)

目錄

Java知識點彙總

1 JDK、JRE、JVM關係是什麼?

JDK(Java Development Kit)即爲Java開發工具包,包含編寫Java程序所必須的編譯、
運行等開發工具以及JRE。開發工具如:用於編譯java程序的javac命令、用於啓動JVM運
行java程序的java命令、用於生成文檔的javadoc命令以及用於打包的jar命令等等。
JRE(Java Runtime Environment)即爲Java運行環境,提供了運行Java應用程序所
必須的軟件環境,包含有Java虛擬機(JVM)和豐富的系統類庫。系統類庫即爲java提前
封裝好的功能類,只需拿來直接使用即可,可以大大的提高開發效率。 
JVM(Java Virtual Machines)即爲Java虛擬機,提供了字節碼文件
(.class)的運行環境支持。簡單說,就是JDK包含JRE包含JVM。

2 繼承的的特點和好處,弊端?

  • 特點:可以從以下兩方面來講:
  • 類與類之間的繼承:只能單繼承不能多繼承,但是可以多層繼承。
  • 接口與接口之間的繼承:既可以單繼承也可以多繼承。
  • 好處:(1)提高了代碼的複用性、維護性、可擴展性。(2)讓類與類產生了關係,
    是多態的前提。
  • 弊端:增強了類與類的耦合性。
  • 補充:面向對象的三大、四大
  • 面向對象的三大思想特徵:封裝、繼承、多態
  • 面向對象的四大特性:封裝、繼承、多態、抽象

3 局部變量和成員變量的區別

  • 在類中的位置:成員變量定義在類中方法外,局部變量定義在方法中或者方法的聲明上。
  • 在內存中的位置:成員變量存放在堆內存中,局部變量存放在棧內存中。
  • 初始化值不同:局部變量沒有默認值,必須先賦值才能使用;成員變量有默認初始化值。
  • 生命週期不同:成員變量隨着對象的存在而存在,隨着對象的消失而消失;局部變量
    隨着方法的調用而存在,方法調用完畢而消失。

4 Java支持的數據類型有哪些?什麼是自動拆裝箱?

基本數據類型:整數值型:byte,short,int,long, 字符型:char 浮點類型:float
,double 布爾型:boolean 整數默認int型,小數默認是double型。Float和long類
型的必須加後綴。
首先知道String是引用類型不是基本類型,引用類型聲明的變量是指該變量在內存中實
際存儲的是一個引用地址,實體在堆中。引用類型包括類、接口、數組等。String類還
是final修飾的。 而包裝類就屬於引用類型,自動裝箱和拆箱就是基本類型和引用類型
之間的轉換,至於爲什麼要轉換,因爲基本類型轉換爲引用類型後,就可以new對象,從
而調用包裝類中封裝好的方法進行基本類型之間的轉換或者toString(當然用類名直接
調用也可以,便於一眼看出該方法是靜態的),還有就是如果集合中想存放基本類型,
泛型的限定類型只能是對應的包裝類型。

5 類變量(靜態變量)和實例變量(對象變量,成員變量)的區別?

  • 所屬不同:類變量屬於類,是對象的共性內容;實例變量屬於對象,是對象的特性內容。
  • 在內存中位置不同:類變量存在方法區的靜態區;實例變量存在堆內存中。
  • 生命週期不同:類變量隨着類的加載而存在,隨着類的消失而消失;實例變量隨着對
    象的存在而存在,隨着對象的消失而消失。
  • 調用方式不同:類變量既能被類名點的形式調用,也能通過對象點的形式調用;而實
    例變量只能通過對象點的形式調用。

6 static關鍵字的特點?注意事項是什麼?有什麼好處和弊端?

  • 靜態成員隨着類的加載而加載,優於對象存在
  • 存在於方法區的字節碼文件中的靜態區
  • 被類中所有的對象所共享
  • 能夠通過“類名點”的形式調用
  • 靜態只能訪問靜態
  • 注意事項:
    • 靜態方法中不可以有this、super關鍵字(因爲靜態優先於對象存在;靜態只
      能覆蓋靜態。)
    • 好處:能夠被類名點的形式調用,簡化書寫。被該類下所有的對象所共享。
    • 弊端:生命週期過長。

7 如何實現對象克隆?

  • 實現Cloneable接口並重寫Object類中的clone()方法;
  • 實現Serializable接口,通過對象的序列化和反序列化實現克隆,可以實現真正的
    深度克隆
  • 基於序列化和反序列化實現的克隆不僅僅是深度克隆,更重要的是通過泛型限定,可
    以檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出
    異常,這種是方案明顯優於使用Object類的clone方法克隆對象。讓問題在編譯的時候
    暴露出來總是優於把問題留到運行時。

8 訪問修飾符public,private,protected,以及不寫(默認)時的區別

  • public:當前類、同包、子類、其他包
  • protected:當前類、同包、子類
  • private:當前類
  • default:當前類、同包

9 Java中如何實現序列化,有什麼意義?

序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。可
以對流化後的對象進行讀寫操作,也可將流化後的對象傳輸於網絡之間。序列化是爲了
解決對象流讀寫操作時可能引發的問題(如果不進行序列化可能會存在數據亂序的問題)
。要實現序列化,需要讓一個類實現Serializable接口,該接口是一個標識性接口,
標註該類對象是可被序列化的,然後使用一個輸出流來構造一個對象輸出流並通過
writeObject(Object)方法就可以將實現對象寫出(即保存其狀態);如果需要反
序列化則可以用一個輸入流建立對象輸入流,然後通過readObject方法從流中讀取對
象。序列化除了能夠實現對象的持久化之外,還能夠用於對象的深度克隆

10 抽象類和接口的區別

  • 抽象類只能被繼承,而且只能單繼承。接口需要被實現,而且可以多實現。
  • 抽象類中可以定義非抽象方法,子類可以直接繼承使用。接口中都有抽象方法,需要
    子類去實現。
  • 抽象類使用的是  is a 關係。接口使用的 like a 關係。
  • 抽象類的成員修飾符可以自定義。接口中的成員修飾符是固定的。全都是public的。

11 兩個對象值相同(x.equals(y) == true),但卻可有不同的 hash code,這句 話對不對?

不對,如果兩個對象 x 和 y 滿足 x.equals(y) == true,它們的哈希碼
(hash code)應當相同。
Java 對於 eqauls 方法和hashCode方法是這樣規定的:

  • 如果兩個對象相同(equals 方法返回 true ),那麼它們的 h ashCode 值
    一定要相同;
  • 如果兩個對象的 hashCode相同,它們並不一定相同。當然,你未必要按照
    要求 去做,但是如果你違背了上述原則就會發現在使用容器時,相同的對象可
    以出現在 Set 集合中,同時增加新元素 的效率會大大下降(對於使用哈希存
    儲的系統,如果哈希碼頻繁的衝突將會造成存取性能急劇下降)。

12 類和對象的關係?

類是用來描述事物的屬性(事物的外在的特徵,成員變量)和行爲(事物具有
的特性,成員方法)的一個抽象的概念。
對象是類的實例化。
比如:學生是一個類,具體某一個人就是對象。

13 封裝就是私有,對嗎?爲什麼?

不對,private(私有)僅僅是封裝的一種體現形式。我們常用的類,方法,函數
也是封裝。只要是對外不可見,就能達到封裝的效果,比如:包與包之間的訪問。
補充:get/set訪問方式必須成對出現嗎?
答:不是,具體看需求,需要設置值就提供set方法,如果需要訪問值,就提供get方法

14 什麼是封裝?封裝的原則?好處

封裝就是隱藏對象的屬性和具體實現細節,僅對外提供公共的訪問方式。
原則:(1)將不需要對外暴露的信息隱藏;(2)對外提供公共的訪問方式。
好處:將變化隔離;提高了安全性;提高了代碼代碼重用性(是對的),便於使用。

15 Java中參數傳遞的問題

Java中只有值傳遞。
如果是基本數據類型,傳遞的是值,
如果是引用數據類型,傳遞的地址值(但是String類型作爲方法的形參時,傳
遞的是值),地址值也是值。

16 構造方法,set方法都可以給成員變量賦值,這兩種賦值方式有什麼區別?

構造方法主要作用是用來給對象初始化,賦值只是它的捎帶工作,也可以不用賦值。
Set方法只能用來賦值,在原有對象的基礎上賦值,可以用來修改值。
構造方法重新賦值,相對於重新創建對象。

17 靜態代碼塊和構造代碼塊的區別?

  • 靜態代碼塊隨着類的加載而加載,一般是用來加載驅動的。只在類加載的時候
    執行一次,優先於構造方法執行
  • 構造代碼塊裏邊放的是所有構造方法的共性內容,爲了簡化書寫,調高效率。
    每創建一次對象,就執行一次,它是優先於構造方法執行的。

18 java 中實現多態的機制是什麼

靠的是父類或接口定義的引用變量可以指向子類或具體實現類的實例對象,而程
序調用的方法在運行期才動態綁定,就是引用變量所指向的具體實例對象的方法,
也就是內存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法。

19 String 是最基本的數據類型嗎?

不是。Java中的基本數據類型只有8個:byte、short、int、long、float、
double、char、boolean;除了基本類型(primitive type)和枚舉類型
(enumeration type),剩下的都是引用類型(reference type)。

20 float f=3.4;是否正確

不正確。3.4是雙精度數,將雙精度型(double)賦值給浮點型(float)屬於
下轉型(down-casting,也稱爲窄化)會造成精度損失,因此需要強制類型轉
換float f =(float)3.4; 或者寫成float f =3.4F;。

21 int和Integer有什麼區別?

Java是一個近乎純潔的面向對象編程語言,但是爲了編程的方便還是引入了基本
數據類型,但是爲了能夠將這些基本數據類型當成對象操作,Java爲每一個基本
數據類型都引入了對應的包裝類型(wrapper class),int的包裝類就是Integer,
從Java 5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。Java 爲每個
原始類型提供了包裝類型:- 原始類型: boolean,char,byte,short,int,
long,float,double- 包裝類型:Boolean,Character,Byte,Short,
Integer,Long,Float,Double

22 switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?

在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。
從Java 5開始,Java中引入了枚舉類型,expr也可以是enum類型,從Java 7開始,
expr還可以是字符串(String),但是長整型(long)在目前所有的版本中都是不可以的。

23 數組有沒有length()方法?String有沒有length()方法?

數組沒有length()方法,有length 的屬性。String 有length()方法。
JavaScript中,獲得字符串的長度是通過length屬性得到的,這一點容易和Java混淆。

24 在Java中,如何跳出當前的多重嵌套循環?

在最外層循環前加一個標記如A,然後用break A;可以跳出多重循環。
(Java中支持帶標籤的break和continue語句,作用有點類似於C和C++中的goto語句,
但是就像要避免使用goto一樣,應該避免使用帶標籤的break和continue,因爲它不
會讓你的程序變得更優雅,很多時候甚至有相反的作用,所以這種語法其實不知道更好)

25 構造器(constructor)是否可被重寫(override)

構造器不能被繼承,因此不能被重寫,但可以被重載。

26 是否可以繼承String類?

String 類是final類,不可以被繼承。

27 String和StringBuilder、StringBuffer的區別?

Java平臺提供了兩種類型的字符串:String和StringBuffer/StringBuilder,
它們可以儲存和操作字符串。其中String是隻讀字符串,也就意味着String引用的
字符串內容是不能被改變的。而StringBuffer/StringBuilder類表示的字符串對
象可以直接進行修改。StringBuilder是Java 5中引入的,它和StringBuffer的
方法完全相同,區別在於它是在單線程環境下使用的,因爲它的所有方面都沒有
被synchronized修飾,因此它的效率也比StringBuffer要高。

28 重載(Overload)和重寫(Override)的區別。重載的方法能否根據返回類型進行區分?

方法的重載和重寫都是實現多態的方式,區別在於前者實現的是編譯時的多態性,
而後者實現的是運行時的多態性。重載發生在一個類中,同名的方法如果有不同的
參數列表(參數類型不同、參數個數不同或者二者都不同)則視爲重載;重寫發生
在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回類型,
比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)
。重載對返回類型沒有特殊的要求。

29 char 型變量中能不能存貯一箇中文漢字,爲什麼?

char類型可以存儲一箇中文漢字,因爲Java中使用的編碼是Unicode(不選擇任何
特定的編碼,直接使用字符在字符集中的編號,這是統一的唯一方法),
一個char類型佔2個字節(16比特),所以放一箇中文是沒問題的。

30 抽象的(abstract)方法是否可同時是靜態的(static),是否可同時是本

地方法(native),是否可同時被synchronized修飾?
都不能。抽象方法需要子類重寫,而靜態的方法是無法被重寫的,因此二者是矛盾的。
本地方法是由本地代碼(如C代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。
synchronized和方法的實現細節有關,抽象方法不涉及實現細節,因此也是相互矛盾的。

31 是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的調用?

不可以,靜態方法只能訪問靜態成員,因爲非靜態方法的調用要先創建對象,
在調用靜態方法時可能對象並沒有被初始化。

32 String s = new String(“xyz”);創建了幾個字符串對象?

兩個對象,一個是靜態區的”xyz”,一個是用new創建在堆上的對象。

33 接口是否可繼承(extends)接口?抽象類是否可實現(implements)接口?

抽象類是否可繼承具體類(concrete class)?
接口可以繼承接口,而且支持多重繼承。抽象類可以實現(implements)接口,
抽象類可繼承具體類也可以繼承抽象類。

34 一個”.java”源文件中是否可以包含多個類(不是內部類)?有什麼限制?

可以,但一個源文件中最多隻能有一個公開類(public class)而且文件
名必須和公開類的類名完全保持一致。

35 Java 中的final關鍵字有哪些用法?

  • 修飾類:表示該類不能被繼承;
  • 修飾方法:表示方法不能被重寫;
  • 修飾變量:表示變量只能一次賦值以後值不能被修改(常量)。

36 比較一下Java和JavaSciprt。

JavaScript 與Java是兩個公司開發的不同的兩個產品。Java 是
原Sun Microsystems公司推出的面向對象的程序設計語言,特別
適合於互聯網應用程序開發;而JavaScript是Netscape公司的產品
,爲了擴展Netscape瀏覽器的功能而開發的一種可以嵌入Web頁面中
運行的基於對象和事件驅動的解釋性語言。下面對兩種語言間的異同作
如下比較:- 基於對象和麪向對象:Java是一種真正的面向對象的語言
,即使是開發簡單的程序,必須設計對象;JavaScript是種腳本語言,
它可以用來製作與網絡無關的,與用戶交互作用的複雜軟件。它是一種基
於對象(Object-Based)和事件驅動(Event-Driven)的編程語言,因
而它本身提供了非常豐富的內部對象供設計人員使用。- 解釋和編譯:
Java的源代碼在執行之前,必須經過編譯。JavaScript是一種解釋性編
程語言,其源代碼不需經過編譯,由瀏覽器解釋執行。(目前的瀏覽器幾
乎都使用了JIT(即時編譯)技術來提升JavaScript的運行效率)- 強類
型變量和類型弱變量:Java採用強類型變量檢查,即所有變量在編譯之前
必須作聲明;JavaScript中變量是弱類型的,甚至在使用變量前可以不作
聲明,JavaScript的解釋器在運行時檢查推斷其數據類型。- 代碼格式不一樣。

37 什麼時候用斷言(assert)?

斷言在軟件開發中是一種常用的調試方式,很多開發語言中都支持這種機制。
一般來說,斷言用於保證程序最基本、關鍵的正確性。斷言檢查通常在開發和
測試時開啓。爲了保證程序的執行效率,在軟件發佈後斷言檢查通常是關閉的。
斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式爲true;
如果表達式的值爲false,那麼系統會報告一個AssertionError。

38 闡述final、finally、finalize的區別。

final:修飾符(關鍵字)有三種用法:如果一個類被聲明爲final,意味着它
不能再派生出新的子類,即不能被繼承,因此它和abstract是反義詞。將變量
聲明爲final,可以保證它們在使用中不被改變,被聲明爲final的變量必須在
聲明時給定初值,而在以後的引用中只能讀取不可修改。被聲明爲final的方法
也同樣只能使用,不能在子類中被重寫。- finally:通常放在try…catch…的
後面構造總是執行代碼塊,這就意味着程序無論正常執行還是發生異常,這裏的
代碼只要JVM不關閉都能執行,可以將釋放外部資源的代碼寫在finally塊中。
finalize:Object類中定義的方法,Java中允許使用finalize()方法在垃圾
收集器將對象從內存中清除出去之前做必要的清理工作。這個方法是由垃圾收集
器在銷燬對象時調用的,通過重寫finalize()方法可以整理系統資源或者執行其
他清理工作。

39 Java中是如何支持正則表達式操作的?

Java中的String類提供了支持正則表達式操作的方法,包括:matches()、
replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern
類表示正則表達式對象,它提供了豐富的API進行各種正則表達式操作,
請參考下面面試題的代碼。

40 獲得一個類的類對象有哪些方式?

方法1:類型.class,例如:String.class- 方法2:對象.getClass(),
例如:”hello”.getClass()- 方法3:Class.forName(),例如:
Class.forName(“java.lang.String”)

41 什麼是值傳遞和引用傳遞?

值傳遞是對基本型變量而言的,傳遞的是該變量的一個副本,改變副本不影響
原變量. 引用傳遞一般是對於對象型變量而言的,傳遞的是該對象地址的一個
副本, 並不是原對象本身 。 一般認爲,java內的傳遞都是值傳遞. java中實
例對象的傳遞是引用傳遞 。

42 Thread類的sleep()方法和對象的wait()方法都可以讓線程暫停執行,它們有什麼區別?

sleep()方法(休眠)是線程類(Thread)的靜態方法,調用此方法會讓當前
線程暫停執行指定的時間,將執行機會(CPU)讓給其他線程,但是對象的鎖
依然保持,因此休眠時間結束後會自動恢復。wait()是Object類的方法,調
用對象的wait()方法導致當前線程放棄對象的鎖(線程暫停執行),進入對
象的等待池(wait pool),只有調用對象的notify()方法(或notifyAll()方法)
時才能喚醒等待池中的線程進入等鎖池(lock pool),如果線程重新獲得對象的
鎖就可以進入就緒狀態。

43 線程的sleep()方法和yield()方法有什麼區別?

  • sleep()方法給其他線程運行機會時不考慮線程的優先級,因此會給低優先級的線
    程以運行的機會;yield()方法只會給相同優先級或更高優先級的線程以運行的機會;
  • 線程執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就
    緒(ready)狀態;
  • sleep()方法聲明拋出InterruptedException,而yield()方
    法沒有聲明任何異常;
  • sleep()方法比yield()方法(跟操作系統CPU調度相關)具有更好的可移植性。

44 當一個線程進入一個對象的synchronized方法A之後,其它線程是否可進入此對象的synchronized方法B?

不能。其它線程只能訪問該對象的非同步方法,同步方法則不能進入。
因爲非靜態方法上的synchronized修飾符要求執行方法時要獲得對象的鎖,
如果已經進入A方法說明對象鎖已經被取走,那麼試圖進入B方法的線程就只能在
等鎖池(注意不是等待池哦)中等待對象的鎖。

45 請說出與線程同步以及線程調度相關的方法。

wait():使一個線程處於等待(阻塞)狀態,並且釋放所持有的對象的鎖;
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方
法要處理InterruptedException異常;- notify():喚醒一個處於等待狀態
的線程,當然在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,
而是由JVM確定喚醒哪個線程,而且與優先級無關;- notityAll():喚醒所有
處於等待狀態的線程,該方法並不是將對象的鎖給所有線程,而是讓它們競爭,
只有獲得鎖的線程才能進入就緒狀態;

46 編寫多線程程序有幾種實現方式?

Java 5以前實現多線程有兩種實現方法:一種是繼承Thread類;
另一種是實現Runnable接口。兩種方式都要通過重寫run()方法來定義線程的行爲,
推薦使用後者,因爲Java中的繼承是單繼承,一個類有一個父類,
如果繼承了Thread類就無法再繼承其他類了,顯然使用Runnable接口更爲靈活。

47 synchronized關鍵字的用法?

synchronized關鍵字可以將對象或者方法標記爲同步,以實現對對象和方法的互斥訪問,
可以用synchronized(對象) { … }定義同步代碼塊,或者在聲明方法時將synchronized
作爲方法的修飾符。

48 舉例說明同步和異步。

如果系統中存在臨界資源(資源數量少於競爭資源的線程數量的資源),例如正在寫的數據以後
可能被另一個線程讀到,或者正在讀的數據可能已經被另一個線程寫過了,那麼這些數據就必須
進行同步存取(數據庫操作中的排他鎖就是最好的例子)。當應用程序在對象上調用了一個需
要花費很長時間來執行的方法,並且不希望讓程序等待方法的返回時,就應該使用異步編程,
在很多情況下采用異步途徑往往更有效率。事實上,所謂的同步就是指阻塞式操作,而異步就
是非阻塞式操作。

49 啓動一個線程是調用run()還是start()方法?

啓動一個線程是調用start()方法,使線程所代表的虛擬處理機處於可運行狀態,這意味着它
可以由JVM 調度並執行,這並不意味着線程就會立即運行。run()方法是線程啓動後要進行回
調(callback)的方法。

50 什麼是線程池(thread pool)?

在面向對象編程中,創建和銷燬對象是很費時間的,因爲創建一個對象要獲取內存資源或者其
它更多資源。在Java中更是如此,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷燬後進行
垃圾回收。所以提高服務程序效率的一個手段就是儘可能減少創建和銷燬對象的次數,特別是
一些很耗資源的對象創建和銷燬,這就是”池化資源”技術產生的原因。線程池顧名思義就是事
先創建若干個可執行的線程放入一個池(容器)中,需要的時候從池中獲取線程不用自行創建,
使用完畢不需要銷燬線程而是放回池中,從而減少創建和銷燬線程對象的開銷。Java 5+中的
Executor接口定義一個執行線程的工具。它的子類型即線程池接口是ExecutorService。要配
置一個線程池是比較複雜的,尤其是對於線程池的原理不是很清楚的情況下,因此在工具類
Executors面提供了一些靜態工廠方法,生成一些常用的線程池,
如下所示:- newSingleThreadExecutor:創建一個單線程的線程池。
這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。
如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它。此線程池保證所有任務
的執行順序按照任務的提交順序執行。- newFixedThreadPool:創建固定大小的線程池。
每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達
到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。
newCachedThreadPool:創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需
要的線程,那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增加時,此線程池又
可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴
於操作系統(或者說JVM)能夠創建的最大線程大小。- newScheduledThreadPool:創建一個
大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。
newSingleThreadExecutor:創建一個單線程的線程池。此線程池支持定時以及週期性執行任務的需求。

51 線程的基本狀態以及狀態之間的關係?

其中Running表示運行狀態,Runnable表示就緒狀態(萬事俱備,只欠CPU),Blocked表
示阻塞狀態,阻塞狀態又有多種情況,可能是因爲調用wait()方法進入等待池,也可能是執
行同步方法或同步代碼塊進入等鎖池,或者是調用了sleep()方法或join()方法等待休眠或
其他線程結束,或是因爲發生了I/O中斷。

52 簡述synchronized 和java.util.concurrent.locks.Lock的異同?

Lock是Java 5以後引入的新的API,和關鍵字synchronized相比主要相同點:Lock 能完
成synchronized所實現的所有功能;主要不同點:Lock有比synchronized更精確的線程
語義和更好的性能,而且不強制性的要求一定要獲得鎖。synchronized會自動釋放鎖,而
Lock一定要求程序員手工釋放,並且最好在finally 塊中釋放(這是釋放外部資源的最好
的地方)。

53 線程和進程有什麼區別?

線程是進程的子集,一個進程可以有很多線程,每條線程並行執行不同的任務。不同的進程
使用不同的內存空間,而所有的線程共享一片相同的內存空間。別把它和棧內存搞混,每個
線程都擁有單獨的棧內存用來存儲本地數據。

54 用Runnable還是Thread?

這個問題是上題的後續,大家都知道我們可以通過繼承Thread類或者調用Runnable接口來
實現線程,問題是,那個方法更好呢?什麼情況下使 用它?這個問題很容易回答,如果你
知道Java不支持類的多重繼承,但允許你調用多個接口。所以如果你要繼承其他類,當然是
調用Runnable接口好 了。

55 Thread 類中的start() 和 run() 方法有什麼區別?

這個問題經常被問到,但還是能從此區分出面試者對Java線程模型的理解程度。start()方
法被用來啓動新創建的線程,而且start()內部 調用了run()方法,這和直接調用run()方
法的效果不一樣。當你調用run()方法的時候,只會是在原來的線程中調用,沒有新的線程
啓動,start()方法纔會啓動新線程。

56  Java中Runnable和Callable有什麼不同?

Runnable和Callable都代表那些要在不同的線程中執行的任務。Runnable從JDK1.0開始就
有了,Callable是在 JDK1.5增加的。它們的主要區別是Callable的 call() 方法可以返
回值和拋出異常,而Runnable的run()方法沒有這些功能。Callable可以返回裝載有計算結
果的Future對象。

57 Java中的volatile 變量是什麼?

volatile是一個特殊的修飾符,只有成員變量才能使用它。在Java併發程序缺少同步類的情
況下,多線程對成員變量的操作對其它線程是透明的。volatile變量可以保證下一個讀取操
作會在前一個寫操作之後發生,就是上一題的volatile變量規則。

58 什麼是線程安全?Vector是一個線程安全類嗎?

如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。
如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量 的值也和預期的是一樣的,
就是線程安全的。一個線程安全的計數器類的同一個實例對象在被多個線程使用的情況下也不
會出現計算失誤。很顯然你可以將集合類分 成兩組,線程安全和非線程安全的。Vector 是用
同步方法來實現線程安全的, 而和它相似的ArrayList不是線程安全的。

59 一個線程運行時發生異常會怎樣?

這是我在一次面試中遇到的一個很刁鑽的Java面試題, 簡單的說,如果異常沒有被捕獲該線
程將會停止執行。Thread.UncaughtExceptionHandler是用於處理未捕獲異常造成線程突
然中 斷情況的一個內嵌接口。當一個未捕獲異常將造成線程中斷的時候JVM會使用
Thread.getUncaughtExceptionHandler()來 查詢線程的UncaughtExceptionHandler
並將線程和異常作爲參數傳遞給handler的uncaughtException()方法 進行處理。

60 如何在兩個線程間共享數據?

你可以通過共享對象來實現這個目的,或者是使用像阻塞隊列這樣併發的數據結構。
(涉及到在兩個線程間共享對象)用wait和notify方法實現了生產者消費者模型。

61 Java中notify 和 notifyAll有什麼區別?

這又是一個刁鑽的問題,因爲多線程可以等待單監控鎖,Java API 的設計人員提供了
一些方法當等待條件改變的時候通知它們,但是這些方法沒有完全實現。notify()方法
不能喚醒某個具體的線程,所以只有一個線程在等 待的時候它纔有用武之地。而notifyAll()
喚醒所有線程並允許他們爭奪鎖確保了至少有一個線程能繼續運行。

62 什麼是ThreadLocal變量?

ThreadLocal是Java裏一種特殊的變量。每個線程都有一個ThreadLocal就是每個線程都
擁有了自己獨立的一個變量,競爭條件被 徹底消除了。它是爲創建代價高昂的對象獲取線
程安全的好方法,比如你可以用ThreadLocal讓SimpleDateFormat變成線程安全的,因
爲那個類創建代價高昂且每次調用都需要創建不同的實例所以不值得在局部範圍使用它,
如果爲每個線程提供一個自己獨有的變量拷貝,將大大提高效率。首先,通 過複用減少了
代價高昂的對象的創建個數。其次,你在沒有使用高代價的同步或者不變性的情況下獲得
了線程安全。線程局部變量的另一個不錯的例子是 ThreadLocalRandom類,它在多線程
環境中減少了創建代價高昂的Random對象的個數。

63 什麼是FutureTask?

在Java併發程序中FutureTask表示一個可以取消的異步運算。它有啓動和取消運算、查詢
運算是否完成和取回運算結果等方法。只有當運算完 成的時候結果才能取回,如果運算尚
未完成get方法將會阻塞。一個FutureTask對象可以對調用了Callable和Runnable的對象
進行包 裝,由於FutureTask也是調用了Runnable接口所以它可以提交給Executor來執行。

64 如何避免死鎖?

java多線程中的死鎖死鎖是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一
種互相等待的現象,若無外力作用,它們都將無法推進下去。這是一個嚴重的問題,因爲死
鎖會讓你的程序掛起無法完成任務,死鎖的發生必須滿足以下四個條件:
互斥條件:一個資源每次只能被一個進程使用。
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關係。
避免死鎖最簡單的方法就是阻止循環等待條件,將系統中所有的資源設置標誌位、排序,
規定所有的進程申請資源必須以一定的順序(升序或降序)做操作來避免死鎖。

65 Java中活鎖和死鎖有什麼區別

這是上題的擴展,活鎖和死鎖類似,不同之處在於處於活鎖的線程或進程的狀態是不斷改
變的,活鎖可以認爲是一種特殊的飢餓。一個現實的活鎖例子是兩個 人在狹小的走廊碰到,
兩個人都試着避讓對方好讓彼此通過,但是因爲避讓的方向都一樣導致最後誰都不能通過走
廊。簡單的說就是,活鎖和死鎖的主要區別是前者 進程的狀態可以改變但是卻不能繼續執行。

66 怎麼檢測一個線程是否擁有鎖?

在java.lang.Thread中有一個方法叫holdsLock(),它返回true如果當且僅當當前線程
擁有某個具體對象的鎖。

67 你如何在Java中獲取線程堆棧?

對於不同的操作系統,有多種方法來獲得Java進程的線程堆棧。當你獲取線程堆棧時,
JVM會把所有線程的狀態存到日誌文件或者輸出到控制檯。在 Windows你可以使用
Ctrl + Break組合鍵來獲取線程堆棧,Linux下用kill -3命令。你也可以用jstack這
個工具來獲取,它對線程id進行操作,你可以用jps這個工具找到id。

68 有三個線程T1,T2,T3,怎麼確保它們按順序執行?

在多線程中有多種方法讓線程按特定順序執行,你可以用線程類的join()方法在一個線
程中啓動另一個線程,另外一個線程完成該線程繼續執行。爲了確保三個線程的順序你應
該先啓動最後一個(T3調用T2,T2調用T1),這樣T1就會先完成而T3最後完成。

69 如果你提交任務時,線程池隊列已滿。會時發會生什麼?

這個問題問得很狡猾,許多程序員會認爲該任務會阻塞直到線程池隊列有空位。事實上如
果一個任務不能被調度執行那麼ThreadPoolExecutor’s submit()方法將會拋出一個
RejectedExecutionException異常。

70 Java線程池中submit() 和 execute()方法有什麼區別?

兩個方法都可以向線程池提交任務,execute()方法的返回類型是void,它定義在
Executor接口中, 而submit()方法可以返回持有計算結果的Future對象,它定義在
ExecutorService接口中,它擴展了Executor接口,其它線 程池類像ThreadPoolExecutor
和ScheduledThreadPoolExecutor都有這些方法。

71 什麼是阻塞式方法?

阻塞式方法是指程序會一直等待該方法完成期間不做其他事情,ServerSocket的accept()
方法就是一直等待客戶端連接。這裏的阻塞是 指調用結果返回之前,當前線程會被掛起,
直到得到結果之後纔會返回。此外,還有異步和非阻塞式方法在任務完成前就返回。

72 多線程中的忙循環是什麼?

忙循環就是程序員用循環讓一個線程等待,不像傳統方法wait(), sleep() 或 yield()
它們都放棄了CPU控制,而忙循環不會放棄CPU,它就是在運行一個空循環。這麼做的目的是
爲了保留CPU緩存,在多核系統中,一個等待線程醒來的時候可 能會在另一個內核運行,這
樣會重建緩存。爲了避免重建緩存和減少等待重建的時間就可以使用它

73 如何在Java中創建線程安全的Singleton?

這是上面那個問題的後續,如果你不喜歡雙檢鎖而面試官問了創建Singleton類的替代方法,
你可以利用JVM的類加載和靜態變量初始化特徵來創建Singleton實例,或者是利用枚舉類型
來創建Singleton,我很喜歡用這種方法

74 寫出3條你遵循的多線程最佳實踐

給你的線程起個有意義的名字。這樣可以方便找bug或追蹤。OrderProcessor, QuoteProcessor
or TradeProcessor 這種名字比 Thread-1. Thread-2 and Thread-3 好多了,給線程起一
個和它要完成的任務相關的名字,所有的主要框架甚至JDK都遵循這個最佳實踐。
避免鎖定和縮小同步的範圍鎖花費的代價高昂且上下文切換更耗費時間空間,試試最低限度的使
用同步和鎖,縮小臨界區。因此相對於同步方法我更喜歡同步塊,它給我擁有對鎖的絕對控制權。
多用同步類少用wait 和 notify首先,CountDownLatch, Semaphore, CyclicBarrier
和 Exchanger 這些同步類簡化了編碼操作,而用wait和notify很難實現對複雜控制流的控制。
其次,這些類是由最好的企業編寫和維護在後續的JDK中它們還會不斷 優化和完善,使用這些更高
等級的同步工具你的程序可以不費吹灰之力獲得優化。
多用併發集合少用同步集合這是另外一個容易遵循且受益巨大的最佳實踐,併發集合比同步
集合的可擴展性更好,所以在併發編程時使用併發集合效果更好。如果下一次你需要用到map,
你應該首先想到用ConcurrentHashMap。

75 如何強制啓動一個線程?

這個問題就像是如何強制進行Java垃圾回收,目前還沒有覺得方法,雖然你可以使用
System.gc()來進行垃圾回收,但是不保證能成功。在Java裏面沒有辦法強制啓動一個
線程,它是被線程調度器控制着且Java沒有公佈相關的API。

76 Java中用到的線程調度算法是什麼

搶佔式。一個線程用完CPU之後,操作系統會根據線程優先級、線程飢餓情況等數據
算出一個總的優先級並分配下一個時間片給某個線程執行。

77 List、Set、Map是否繼承自Collection接口?

List、Set 是,Map 不是。Map是鍵值對映射容器,與List和Set有明顯的區別,而Set
存儲的零散的元素且不允許有重複元素(數學中的集合也是如此),List是線性結構的容器,
適用於按數值索引訪問元素的情形。

78 闡述ArrayList、Vector、LinkedList的存儲性能和特性。

ArrayList 和Vector都是使用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增
加和插入元素,它們都允許直接按序號索引元素,但是插入元素要涉及數組元素移動等內存
操作,所以索引數據快而插入數據慢,Vector中的方法由於添加了synchronized修飾,因
此Vector是線程安全的容器,但性能上較ArrayList差,因此已經是Java中的遺留容器。
LinkedList使用雙向鏈表實現存儲(將內存中零散的內存單元通過附加的引用關聯起來,形
成一個可以按序號索引的線性結構,這種鏈式存儲方式與數組的連續存儲方式相比,內存的利
用率更高),按序號索引數據需要進行前向或後向遍歷,但是插入數據時只需要記錄本項的前後
項即可,所以插入速度較快。Vector屬於遺留容器(Java早期的版本中提供的容器,除此之外
,Hashtable、Dictionary、BitSet、Stack、Properties都是遺留容器),已經不推薦使用,
但是由於ArrayList和LinkedListed都是非線程安全的,如果遇到多個線程操作同一個容器的場景
,則可以通過工具類Collections中的synchronizedList方法將其轉換成線程安全的容器後再使用
(這是對裝潢模式的應用,將已有對象傳入另一個類的構造器中創建新的對象來增強實現)。

79 Collection和Collections的區別?

Collection是一個接口,它是Set、List等容器的父接口;Collections是個一個工具類,
提供了一系列的靜態方法來輔助容器操作,這些方法包括對容器的搜索、排序、線程安全化等等。

80 List、Map、Set三個接口存取元素時,各有什麼特點?

List以特定索引來存取元素,可以有重複元素。Set不能存放重複元素(用對象的equals()
方法來區分元素是否重複)。Map保存鍵值對(key-value pair)映射,映射關係可以是一
對一或多對一。Set和Map容器都有基於哈希存儲和排序樹的兩種實現版本,基於哈希存儲的
版本理論存取時間複雜度爲O(1),而基於排序樹版本的實現在插入或刪除元素時會按照元素
或元素的鍵(key)構成排序樹從而達到排序和去重的效果。

81 TreeMap和TreeSet在排序時如何比較元素?Collections工具類中的sort()方法如何比較元素?

TreeSet要求存放的對象所屬的類必須實現Comparable接口,該接口提供了比較元素的compareTo()方
法,當插入元素時會回調該方法比較元素的大小。TreeMap要求存放的鍵值對映射的鍵必須實現Comparable
接口從而根據鍵對元素進行排序。Collections工具類的sort方法有兩種重載的形式,第一種要求傳入的
待排序容器中存放的對象比較實現Comparable接口以實現元素的比較;第二種不強制性的要求容器中
的元素必須可比較,但是要求傳入第二個參數,參數是Comparator接口的子類型(需要重寫
compare方法實現元素的比較),相當於一個臨時定義的排序規則,其實就是通過接口注入
比較元素大小的算法,也是對回調模式的應用(Java中對函數式編程的支持)。

82 Java集合框架是什麼?說出一些集合框架的長處?

  • 每種編程語言中都有集合。最初的Java版本號包括幾種集合類:Vector、
    Stack、HashTable和Array。
  • 隨着集合的廣泛使用。Java1.2提出了囊括全部集合接口、實現和算法的集合框架。
  • 在保證線程安全的情況下使用泛型和併發集合類,Java已經經歷了非常久。
    它還包括在Java併發包中,堵塞接口以及它們的實現。
  • 集合框架的部分長處例如以下:
    • 使用核心集合類減少開發成本,而非實現我們自己的集合類。
    • 隨着使用經過嚴格測試的集合框架類。代碼質量會得到提高。
    • 通過使用JDK附帶的集合類,能夠減少代碼維護成本。
    • 複用性和可操作性。

83 集合框架中的泛型有什麼長處?

Java1.5引入了泛型。全部的集合接口和實現都大量地使用它。泛型同意我們爲集合提供
一個能夠容納的對象類型,因此。假設你加入其他類型的不論什麼元素,它會在編譯時報錯
。這避免了在執行時出現ClassCastException。由於你將會在編譯時得到報錯信息。
泛型也使得代碼整潔,我們不須要使用顯式轉換和instanceOf操作符。
它也給執行時帶來長處。由於不會產生類型檢查的字節碼指令。

84 Java集合框架的基礎接口有哪些?

Collection爲集合層級的根接口。
一個集合代表一組對象。這些對象即爲它的元素。
Java平臺不提供這個接口不論什麼直接的實現。
Set是一個不能包括反覆元素的集合。
這個接口對數學集合抽象進行建模。被用來代表集合,就如一副牌。
List是一個有序集合。能夠包括反覆元素。
你能夠通過它的索引來訪問不論什麼元素。List更像長度動態變換的數組。
Map是一個將key映射到value的對象.一個Map不能包括反覆的key:每一個key最多僅
僅能映射一個value。

85 爲何Collection不從Cloneable和Serializable接口繼承?

Collection接口指定一組對象,對象即爲它的元素。怎樣維護這些元素由Collection
的詳細實現決定。
比如。一些如List的Collection實現同意反覆的元素。而其他的如Set就不同意。
非常多Collection實現有一個公有的clone方法。
然而。把它放到集合的全部實現中也是沒有意義的。這是由於Collection是一個
抽象表現。重要的是實現。
當與詳細實現打交道的時候,克隆或序列化的語義和含義才發揮作用。所以,詳
細實現應該決定怎樣對它進行克隆或序列化,或它能否夠被克隆或序列化。
在全部的實現中授權克隆和序列化,終於導致更少的靈活性和很多其他的限制。
特定的實現應該決定它能否夠被克隆和序列化。

86 爲何Map接口不繼承Collection接口

雖然Map接口和它的實現也是集合框架的一部分。但Map不是集合。集合也不是Map。
因此,Map繼承Collection毫無意義,反之亦然。
假設Map繼承Collection接口,那麼元素去哪兒?Map包括key-value對,
它提供抽取key或value列表集合的方法,可是它不適合“一組對象”規範。

87 Iterator是什麼?

Iterator接口提供遍歷不論什麼Collection的接口。
我們能夠從一個Collection中使用迭代器方法來獲取迭代器實例。迭代器代
替了Java集合框架中的Enumeration。迭代器同意調用者在迭代過程中移除元素。

88 Enumeration和Iterator接口的差別?

Enumeration的速度是Iterator的兩倍,也使用更少的內存。
Enumeration是非常基礎的,也滿足了基礎的須要。可是,與Enumeration相比,
Iterator更加安全,由於當一個集合正在被遍歷的時候。它會阻止其他線程去改動集合。
迭代器代替了Java集合框架中的Enumeration。
迭代器同意調用者從集合中移除元素,而Enumeration不能做到。
爲了使它的功能更加清晰,迭代器方法名已經經過改善。

89 爲何沒有像Iterator.add()這種方法。向集合中加入元素?

語義不明。已知的是,Iterator的協議不能確保迭代的次序。然而要注意。
ListIterator沒有提供一個add操作,它要確保迭代的順序。

90 Iterater和ListIterator之間有什麼差別?

  • 我們能夠使用Iterator來遍歷Set和List集合,而ListIterator僅僅能遍歷List。
  • Iterator僅僅能夠向前遍歷。而LIstIterator能夠雙向遍歷。
  • ListIterator從Iterator接口繼承,然後加入了一些額外的功能,
    比方加入一個元素、替換一個元素、獲取前面或後面元素的索引位置。

91 通過迭代器fail-fast屬性,你明確了什麼?

每次我們嘗試獲取下一個元素的時候,Iterator fail-fast屬性檢查當
前集合結構裏的不論什麼改動。假設發現不論什麼改動。它拋出ConcurrentModificationException。
Collection中全部Iterator的實現都是按fail-fast來設計的(ConcurrentHashMap
和CopyOnWriteArrayList這類併發集合類除外)。

92 UnsupportedOperationException是什麼?

UnsupportedOperationException是用於表明操作不支持的異常。
在JDK類中已被大量運用,在集合框架java.util.Collections.UnmodifiableCollection
將會在全部add和remove操作中拋出這個異常。

93 在Java中,HashMap是怎樣工作的?

HashMap在Map.Entry靜態內部類實現中存儲key-value對。
HashMap使用哈希算法。在put和get方法中。它使用hashCode()和equals()方法。
當我們通過傳遞key-value對調用put方法的時候。HashMap使用Key hashCode()和
哈希算法來找出存儲key-value對的索引。
Entry存儲在LinkedList中,所以假設存在entry。它使用equals()方法來檢查傳
遞的key是否已經存在。假設存在,它會覆蓋value。假設不存在。它會創建一個
新的entry然後保存。
當我們通過傳遞key調用get方法時,它再次使用hashCode()來找到數組中的索引,
然後使用equals()方法找出正確的Entry,然後返回它的值。下面的圖片解釋了詳細內容。
其他關於HashMap比較重要的問題是容量、負荷係數和閥值調整。HashMap默認的
初始容量是32,負荷係數是0.75。
閥值是爲負荷係數乘以容量,不管何時我們嘗試加入一個entry,假設map的大小
比閥值大的時候,HashMap會對map的內容進行又一次哈希。且使用更大的容量。
容量總是2的冪。所以假設你知道你須要存儲大量的key-value對,比方緩存從數據
庫裏面拉取的數據,使用正確的容量和負荷係數對HashMap進行初始化是個不錯的做法

94 hashCode()和equals()方法有何重要性?

HashMap使用Key對象的hashCode()和equals()方法去決定key-value對的索引。
當我們試着從HashMap中獲取值的時候,這些方法也會被用到。
假設這些方法沒有被正確地實現,在這種情況下,兩個不同Key或許會產生同樣的
hashCode()和equals()輸出,HashMap將會覺得它們是同樣的,然後覆蓋它們。
而非把它們存儲到不同的地方。

95 HashMap和HashTable有何不同?

  • HashMap同意key和value爲null。而HashTable不同意。
  • HashTable是同步的,而HashMap不是。所以HashMap適合單線程環境,
    HashTable適合多線程環境。
  • 在Java1.4中引入了LinkedHashMap,HashMap的一個子類,假如你想要遍歷順序,
    你非常easy從HashMap轉向LinkedHashMap,可是HashTable不是這種。
    它的順序是不可預知的。
  • HashMap提供對key的Set進行遍歷。因此它是fail-fast的。但HashTable
    提供對key的Enumeration進行遍歷,它不支持fail-fast。
  • HashTable被覺得是個遺留的類。假設你尋求在迭代的時候改動Map,
    你應該使用CocurrentHashMap。

96 怎樣決定選用HashMap還是TreeMap?

對於在Map中插入、刪除和定位元素這類操作,HashMap是最好的選擇。
然而。假如你須要對一個有序的key集合進行遍歷,TreeMap是更好的選擇。
基於你的collection的大小,或許向HashMap中加入元素會更快。將map
換爲TreeMap進行有序key的遍歷。

97 ArrayList和Vector有何異同點?

  • 兩者都是基於索引的,內部由一個數組支持。
  • 兩者維護插入的順序,我們能夠依據插入順序來獲取元素。
  • ArrayList和Vector的迭代器實現都是fail-fast的。
  • ArrayList和Vector兩者同意null值。也能夠使用索引值對元素進行隨機訪問。
  • Vector是同步的,而ArrayList不是。然而。假設你尋求在迭代的時候對
    列表進行改變。你應該使用CopyOnWriteArrayList。
  • ArrayList比Vector快。它由於有同步。不會過載。
  • ArrayList更加通用,由於我們能夠使用Collections工具類輕易地獲取
    同步列表和僅僅讀列表。

98 Array和ArrayList有何差別?什麼時候更適合用Array?

Array能夠容納基本類型和對象,而ArrayList僅僅能容納對象。
Array是指定大小的,而ArrayList大小是固定的。
Array沒有提供ArrayList那麼多功能,比方addAll、removeAll和iterator等。
雖然ArrayList明顯是更好的選擇。但也有些時候Array比較好用。

99 ArrayList和LinkedList有何差別?

  • ArrayList是由Array所支持的基於一個索引的數據結構,所以它提供對元
    素的隨機訪問。複雜度爲O(1),但LinkedList存儲一系列的節點數據。每一
    個節點都與前一個和下一個節點相連接。所以。雖然有使用索引獲取元素的方
    法,內部實現是從起始點開始遍歷,遍歷到索引的節點然後返回元素。時間復
    雜度爲O(n)。比ArrayList要慢。
  • 與ArrayList相比,在LinkedList中插入、加入和刪除一個元素會更快。
    由於在一個元素被插入到中間的時候,不會涉及改變數組的大小,或更新索引。
  • LinkedList比ArrayList消耗很多其他的內存,由於LinkedList中的每一
    個節點存儲了前後節點的引用。

100 哪些集合類提供對元素的隨機訪問?

ArrayList、HashMap、TreeMap和HashTable類提供對元素的隨機訪問。

101 哪些集合類是線程安全的?

Vector、HashTable、Properties和Stack是同步類,所以它們是線程安全
的,能夠在多線程環境下使用。
Java1.5併發API包括一些集合類。同意迭代時改動,由於它們都工作在集合
的克隆上。所以它們在多線程環境中是安全的。

102 併發集合類是什麼?

Java1.5併發包(java.util.concurrent)包括線程安全集合類,同意在
迭代時改動集合。
迭代器被設計爲fail-fast的,會拋出ConcurrentModificationException。
一部分類爲:CopyOnWriteArrayList、 ConcurrentHashMap、
CopyOnWriteArraySet。

103 BlockingQueue是什麼?

Java.util.concurrent.BlockingQueue是一個隊列,在進行檢索或移除一個
元素的時候,它會等待隊列變爲非空;當在加入一個元素時,它會等待隊列中的可用空間。
BlockingQueue接口是Java集合框架的一部分,主要用於實現生產者-消費者模式
。我們不須要操心等待生產者有可用的空間。或消費者有可用的對象。由於它都
在BlockingQueue的實現類中被處理了。
Java提供了集中BlockingQueue的實現,比方ArrayBlockingQueue、
LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

104 隊列和棧是什麼,列出它們的差別?

棧和隊列兩者都被用來預存儲數據。java.util.Queue是一個接口,
它的實現類在Java併發包中。隊列同意先進先出(FIFO)檢索元素,
但並不是總是這樣。Deque接口同意從兩端檢索元素。
棧與隊列非常類似,但它同意對元素進行後進先出(LIFO)進行檢索。
Stack是一個擴展自Vector的類,而Queue是一個接口。

105 Collections類是什麼?

Java.util.Collections是一個工具類僅包括靜態方法。它們操作或返
回集合。它包括操作集合的多態算法,返回一個由指定集合支持的新集合
和其他一些內容。這個類包括集合框架算法的方法,比方折半搜索、排序、
混編和逆序等。

106 Comparable和Comparator接口是什麼?

假設我們想使用Array或Collection的排序方法時。須要在自己定義類裏
實現Java提供Comparable接口。
Comparable接口有compareTo(T OBJ)方法,它被排序方法所使用。我們
應該重寫這種方法,假設“this”對象比傳遞的對象參數更小、相等或更大
時,它返回一個負整數、0或正整數
可是。在大多數實際情況下,我們想依據不同參數進行排序。
比方。作爲一個CEO。我想對僱員基於薪資進行排序。一個HR想基於年齡對
他們進行排序。這就是我們須要使用Comparator接口的情景。由於
Comparable.compareTo(Object o)方法實現僅僅能基於一個字段進行
排序,我們不能依據對象排序的須要選擇字段。
Comparator接口的compare(Object o1, Object o2)方法的實現須要
傳遞兩個對象參數,若第一個參數比第二個小,返回負整數;若第一個
等於第二個。返回0;若第一個比第二個大。返回正整數。

107 HashMap和HashSet區別

a. HashMap實現了Map接口,HashSet實現了Set接口
b.HashMap存儲鍵值對,HashSet存儲對象
c.HashMap調用put()向map中添加元素,HashSet調用add()像set中添加元素
d.HashMap使用Key計算hasncode,HashMap使用成員計算Hashcode

108 fail-fast 與 fail-safe 之間的區別?

Fail fast快速地報告任何的failure。無論何時任何一個問題都會
引發 fail fast系統fails· 在Java Fail fast 迭代器中,迭代objects集
合有時會出現併發修改異常,出現這種情況有2個原因· 如果一個線程正在迭代
一個集合,而另一個線程同時試圖修改這個集合· 在調用remove()方法後,如
何我們還試圖去修改集合object

109 是否可以往 TreeSet 或者 HashSet 中添加 null 元素?

可以往 hashset 中添加一個 null· TreeSet 也允許一個 null值

110 ArrayList集合加入1萬條數據,應該怎麼提高效率

ArrayList的默認初始容量爲10,要插入大量數據的時候需要不斷擴容,而擴
容是非常影響性能的。因此,現在明確了10萬條數據了,我們可以直接在初始
化的時候就設置ArrayList的容量!
這樣就可以提高效率了~

111 HashMap中的get操作是什麼原理?

先根據key的hashcode值找到對應的鏈表,再循環鏈表,根據key的hash是否
相同且key的==或者equals比較操作找到對應的值

112 Java中什麼是Exception?

異常是Java傳達給你的系統和程序錯誤的方
式。在java中,異常功能是通過實現比如Throwable,Exception,
RuntimeException之類的類,然後還有一
些處理異常時候的關鍵字,比如throw,throws,try,catch,finally之
類的。 所有的異常都是通過Throwable
衍生出來的。Throwable把錯誤進一步劃分爲 java.lang.Exception
和 java.lang.Error.  java.lang.Error 用
來處理系統錯誤,例如java.lang.StackOverFlowError 或者
Java.lang.OutOfMemoryError 之類的。然後
 Exception用來處理程序錯誤,請求的資源不可用等等。

113 Java中的檢查型異常和非檢查型異常有什麼區別?

這又是一個非常流行的Java異常面試題,會出現在各種層次的Java面試中。
檢查型異常和非檢查型異常的主要區別在於其處理方式。檢查型異常需要使
用try, catch和finally關鍵字在編譯期進行處理,否則會出現編譯
器會報錯。對於非檢查型異常則不需要這樣做。Java中所有繼承自
java.lang.Exception類的異常都是檢查型異常,所有繼承自
RuntimeException的異常都被稱爲非檢查型異常。

114 Java中的NullPointerException和ArrayIndexOutOfBoundException之間有什麼相同之處?

在Java異常面試中這並不是一個很流行的問題,但會出現在不同層次的初
學者面試中,用來測試應聘者對檢查型異常和非檢查型異常的概念是否熟悉。
順便說一下,該題的答案是,這兩個異常都是非檢查型異常,都繼承自
RuntimeException。該問題可能會引出另一個問題,即Java和C的數組有
什麼不同之處,因爲C裏面的數組是沒有大小限制的,絕對不會拋出ArrayIndexOutOfBoundException。

115 在Java異常處理的過程中,你遵循的那些最好的實踐是什麼?

這個問題在面試技術經理是非常常見的一個問題。因爲異常處理在項目設
計中是非常關鍵的,所以精通異常處理是十分必要的。異常處理有很多最
佳實踐,下面列舉集中,它們提高你代碼的健壯性和靈活性:

  1. 調用方法的時候返回布爾值來代替返回null,這樣可以 NullPointerException。
    由於空指針是java異常裏最噁心的異常。
  2. catch塊裏別不寫代碼。空catch塊是異常處理裏的錯誤事件,因爲它只是捕獲了
    異常,卻沒有任何處理或者提示。通常你起碼要打印出異常信息,當然你最好根據需求
    對異常信息進行處理。
    3)能拋受控異常(checked Exception)就儘量不拋受非控異常(checked Exception)。
    通過去掉重複的異常處理代碼,可以提高代碼的可讀性。
  3. 絕對不要讓你的數據庫相關異常顯示到客戶端。由於絕大多數數據庫和SQLException異
    常都是受控異常,在Java中,你應該在DAO層把異常信息處理,然後返回處理過的能讓用戶看懂
    並根據異常提示信息改正操作的異常信息。5) 在Java中,一定要在數據庫連接,數據庫查詢,
    流處理後,在finally塊中調用close()方法。

116 error和exception的區別?

Error類和Exception類的父類都是Throwable類
Error類一般是指與虛擬機相關的問題,如系統奔潰,虛擬機錯誤,內存空間不足,方法調用
棧溢出等。錯誤導致的應用程序中斷,僅靠程序本身無法恢復和預防,遇到這樣的錯誤,建議程序終止。
Exception類表示程序可以處理的異常,可以捕獲且有可能恢復。遇到這種類異常,應儘可能
處理異常,使程序恢復運行,而不是隨意終止異常。

117 Java異常處理機制

Java對異常類進行了分類,不同類型的異常分類用不用的Java類表示,所有異常類的根
類是java.lang.Throwable,Throwable下面有兩個派生的子類Error和Exception,Error
表示應用程序本身無法克服和恢復的一種嚴重問題。Exception表示程序還能恢復和克服的
問題,其中分爲系統異常和普通異常,系統異常是軟件本身缺陷導致的問題,也就是開發人
員考慮不周到所導致的問題,軟件使用者無法克服和恢復,但是這種問題下還可以讓系統運
行或者讓軟件死掉,例如,數組下標越界(ArrayIndexOfBoundsException),空指針異常
等;普通異常是運行環境的變化或異常導致的問題,是用戶能夠克服的問題,例如,網絡斷
線,硬盤空間不夠,發生這樣的異常後,程序不應該死掉。
Java爲系統異常和普通異t常提供了不同的解決方案,編譯器強制普通異常必須try…catch
處理或用throws聲明繼續上拋給調用方法處理,所以普通異常也稱爲checked異常,而系
統異常可以處理也可以不處理,所以,編譯器不強制用try…catch處理或throws聲明,所以
系統異常處理也稱爲uncheched異常。

118 寫出常見的5個RuntimeException

(1)java.lang.NullPointerException空指針異常,出現原因:調用了未經初始化的對
性愛那個或者不存在的對象。
(2)ClassNoFoundException 指定類找不到,出現原因:類的名稱和路徑加載錯誤,通
常是試圖通過字符串來加載某個類時可能引發異常。
(3)NumberFormatException字符串轉換爲數字異常,出現原因:字符串數據中包含非
數字型字符。
(4)IndexOutOfBoundsException數組下標越界異常
(5)IllegalArgumentException 方法傳遞參數錯誤

119 throw和throws區別

throw:
    (1)throw語句用在方法體內,表示拋出異常,由方法體內的語句處理
    (2)throw是具體向外拋出異常的動作,所以它拋出的是一個異常實例,執行throw
一定是拋出了某種異常
throws:
    (1)throws語句是用在方法聲明後面,表示如果拋出異常,由該方法的調用者來進
行異常處理。
    (2)throws主要是聲明這個方法會拋出某種類型的異常,讓它的使用者要知道需要
捕獲的異常的類型。
    (3)throws表示出現異常的一種可能性,並不一定會發生這種異常。

120 什麼是“異常鏈”?

“異常鏈”是Java中非常流行的異常處理概念,是指在進行一個異常處理時拋出了另外一個
異常,由此產生
了一個異常鏈條。該技術大多用於將“ 受檢查異常” ( checked exception)封裝成
爲“非受檢查異常”
(unchecked exception)或者RuntimeException。順便說一下,如果因爲因爲異常你
決定拋出一個新的異常,
你一定要包含原有的異常,這樣,處理程序纔可以通過getCause()和initCause()方法
來訪問異常最終的根源。

121 你曾經自定義實現過異常嗎?怎麼寫的?

然,我們絕大多數都寫過自定義或者業務異常,像AccountNotFoundException。在面
試過程中詢問
這個Java異常問題的主要原因是去發現你如何使用這個特性的。這可以更準確和精緻的
去處理異常,當然這也跟
你選擇checked 還是unchecked exception息息相關。通過爲每一個特定的情況創建一
個特定的異常,你就爲
調用者更好的處理異常提供了更好的選擇。相比通用異常(general exception),我
更傾向更爲精確的異常。大
量的創建自定義異常會增加項目class的個數,因此,在自定義異常和通用異常之間維
持一個平衡是成功的關鍵。

122 Java異常處理中有哪些關鍵字?

throw:有時我們需要顯式地創建並拋出異常對象來終止程序的正常執行。throw關鍵字
用來拋出並處理運行時異常。
throws:當我們拋出任何“被檢查的異常(checked exception)”並不處理時,需要在
方法簽名中使用關鍵字throws來告知調用程序此方法可能會拋出的異常。調用方法可
能會處理這些異常,或者同樣用throws來將異常傳給上一級調用方法。throws關鍵字
後可接多個潛在異常,甚至是在main()中也可以使用throws。
try-catch:我們在代碼中用try-catch塊處理異常。當然,一個try塊之後可以有多個
catch子句,try-catch塊也能嵌套。每個catch塊必須接受一個(且僅有一個)代表
異常類型的參數。

123 描述一下異常的層級。

Java異常是層級的,並通過繼承來區分不同種類的異常。
Throwable是所有異常的父類,它有兩個直接子對象Error,Exception,其中Exception
又被繼續劃分爲“被檢查的異常(checked exception)”和”運行時的異常
(runtime exception,即不受檢查的異常)”。 Error表示編譯時和系統錯誤,
通常不能預期和恢復,比如硬件故障、JVM崩潰、內存不足等。
被檢查的異常(Checked exception)在程序中能預期,並要嘗試修復,
如FileNotFoundException。我們必須捕獲此類異常,併爲用戶提供有用信
息和合適日誌來進行調試。Exception是所有被檢查的異常的父類。
運行時異常(Runtime Exception)又稱爲不受檢查異常,源於糟糕的編程。
比如我們檢索數組元素之前必須確認數組的長度,否則就可能會拋出
ArrayIndexOutOfBoundException運行時異常。RuntimeException是所有運行時異常的父類。
閉,因此,我們能使用finally進行關閉。不管異常有沒有出現,finally塊總會被執行。

124 Java異常類有哪些的重要方法?

String getMessage():方法返回Throwable的String型信息,當異常通過構造器創建後可用。
String getLocalizedMessage():此方法通過被重寫來得到用本地語言
表示的異常信息返回給調用程序。Throwable類通常只是用getMessage()方法來實現返回異常信息。
synchronized Throwable getCause():此方法返回異常產生的原因,
如果不知道原因的話返回null。(原文有拼寫錯誤 應該是if 不是id)
String toString():方法返回String格式的Throwable信息,此信息
包括Throwable的名字和本地化信息。
void printStackTrace():該方法打印棧軌跡信息到標準錯誤流。
該方法能接受PrintStream 和PrintWriter作爲參數實現重載,這樣就能實現打印棧軌跡到文件或流中。

125 Error和Exception有什麼區別?

Error表示系統級的錯誤和程序不必處理的異常,是恢復不是不可能但很困難
的情況下的一種嚴重問題;比如內存溢出,不可能指望程序能處理這樣的情況;
Exception表示需要捕捉或者需要程序進行處理的異常,是一種設計或實現問題;
也就是說,它表示如果程序運行正常,從不會發生的情況。

126 try{}裏有一個return語句,那麼緊跟在這個try後的finally{}裏的代碼會不會被執行,什麼時候被執行,在return前還是後?

會執行,在方法返回調用者前執行。

127 Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally分別如何使用?

Java通過面向對象的方法進行異常處理,把各種不同的異常進行分類,
並提供了良好的接口。在Java中,每個異常都是一個對象,它是Throwable
類或其子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中
包含有異常信息,調用這個對象的方法可以捕獲到這個異常並可以對其進行
處理。Java的異常處理是通過5個關鍵詞來實現的:try、catch、throw、
throws和finally。一般情況下是用try來執行一段程序,如果系統會拋出
(throw)一個異常對象,可以通過它的類型來捕獲(catch)它,或通過總
是執行代碼塊(finally)來處理;try用來指定一塊預防所有異常的程序;
catch子句緊跟在try塊後面,用來指定你想要捕獲的異常的類型;throw語
句用來明確地拋出一個異常;throws用來聲明一個方法可能拋出的各種異常
(當然聲明異常時允許無病呻吟);finally爲確保一段代碼不管發生什麼異
常狀況都要被執行;try語句可以嵌套,每當遇到一個try語句,異常的結構就
會被放入異常棧中,直到所有的try語句都完成。如果下一級的try語句沒有對某
種異常進行處理,異常棧就會執行出棧操作,直到遇到有處理這種異常的try語
句或者最終將異常拋給JVM。

128 闡述final、finally、finalize的區別。

final:修飾符(關鍵字)有三種用法:如果一個類被聲明爲final,意味着它
不能再派生出新的子類,即不能被繼承,因此它和abstract是反義詞。將變量聲
明爲final,可以保證它們在使用中不被改變,被聲明爲final的變量必須在聲明
時給定初值,而在以後的引用中只能讀取不可修改。被聲明爲final的方法也同
樣只能使用,不能在子類中被重寫。- finally:通常放在try…catch…的後面構
造總是執行代碼塊,這就意味着程序無論正常執行還是發生異常,這裏的代碼只要
JVM不關閉都能執行,可以將釋放外部資源的代碼寫在finally塊中。- finalize:Object
類中定義的方法,Java中允許使用finalize()方法在垃圾收集器將對象從內存中清除出去之
前做必要的清理工作。這個方法是由垃圾收集器在銷燬對象時調用的,通過重寫finalize()
方法可以整理系統資源或者執行其他清理工作。

129 什麼是比特(Bit),什麼是字節(Byte),什麼是字符(Char),它們長度是多少,各有什麼區別

Bit最小的二進制單位 ,是計算機的操作部分 取值0或者1Byte是計算機操作數據
的最小單位由8位bit組成 取值(-128-127)Char是用戶的可讀寫的最小單位,在Java
裏面由16位bit組成 取值(0-65535)
Bit 是最小單位 計算機 只能認識 0或者1 
8個字節 是給計算機看的字符 是看到的東西  一個字符=二個字節

130 什麼是流,按照傳輸的單位,分成哪兩種流,並且他們的父類叫什麼流是指數據的傳輸

字節流,字符流 字節流:InputStream OutputStream字符流:Reader Writer

131 流按照傳輸的方向可以分爲哪兩種,分別舉例說明

輸入輸出相對於程序輸入流InputStream,輸出流OutputStream

132 OutputStream裏面的write()是什麼意思,write(byte b[], int off, int len)這個方法裏面的三個參數分別是什麼意思

write將指定字節傳入數據源Byte b[ ]是byte數組b[off]是傳入的第一個
字符b[off+len-1]是傳入的最後的一個字符 len是實際長度

133 流一般需要不需要關閉,如果關閉的話在用什麼方法,一般要在那個代碼塊裏面關閉比較好,處理流是怎麼關閉的,如果有多個流互相調用傳入是怎麼關閉的?

流一旦打開就必須關閉,使用close方法放入finally語句塊中(finally 
語句一定會執行)調用的處理流就關閉處理流多個流互相調用只關閉最外層的流

134 Java中的所有的流可以分爲幾大類,它們的名字是什麼,各代表什麼

分爲 字節輸入流 InputStream 字節輸出流 OutputStream字符輸入流 Reader字
符輸出流 Writer所有流都是這四個流的子類

135 io流怎樣讀取文件的

使用File對象獲取文件路徑,通過字符流Reader加入文件,使用字符緩存流BufferedReader處
理Reader,再定義一個字符串,循環遍歷出文件。代碼如下:
File file = new File(“d:/spring.txt”);try {Reader reader = 
new FileReader(file);BufferedReader buffered = 
new BufferedReader(reader);String data = null;
while((data = buffered.readLine())!=null){System.out.println(data);}} 
catch (FileNotFoundException e) {e.printStackTrace();} catch 
(IOException e) {e.printStackTrace();}

136  PrintStream、BufferedWriter、PrintWriter的比較?

PrintStream類的輸出功能非常強大,通常如果需要輸出文本內容,都應該將輸
出流包裝成PrintStream後進行輸出。它還提供其他兩項功能。與其他輸出流不
同,PrintStream 永遠不會拋出 IOException;而是,異常情況僅設置可通過
checkError 方法測試的內部標誌。另外,爲了自動刷新,可以創建一個
PrintStreamBufferedWriter:將文本寫入字符輸出流,緩衝各個字符從而提
供單個字符,數組和字符串的高效寫入。通過write()方法可以將獲取到的字符
輸出,然後通過newLine()進行換行操作。BufferedWriter中的字符流必須通過
調用flush方法才能將其刷出去。並且BufferedWriter只能對字符流進行操作。
如果要對字節流操作,則使用BufferedInputStream。 PrintWriter的println
方法自動添加換行,不會拋異常,若關心異常,需要調用checkError方法看是否有
異常發生,PrintWriter構造方法可指定參數,實現自動刷新緩存(autoflush)

137 闡述JDBC操作數據庫的步驟。

138 Statement和PreparedStatement有什麼區別?哪個性能更好?

與Statement相比,①PreparedStatement接口代表預編譯的語句,它主要的優勢在
於可以減少SQL的編譯錯誤並增加SQL的安全性(減少SQL注射攻擊的可能性);
②PreparedStatement中的SQL語句是可以帶參數的,避免了用字符串連接拼接
SQL語句的麻煩和不安全;③當批量處理SQL或頻繁執行相同的查詢時,
PreparedStatement有明顯的性能上的優勢,由於數據庫可以將編譯優
化後的SQL語句緩存起來,下次執行相同結構的語句時就會很快(不用再次編譯和生成執行計劃)。

139 使用JDBC操作數據庫時,如何提升讀取數據的性能?如何提升更新數據的性能?

要提升讀取數據的性能,可以指定通過結果集(ResultSet)對象的setFetchSize()
方法指定每次抓取的記錄數(典型的空間換時間策略);要提升更新數據的性能可以
使用PreparedStatement語句構建批處理,將若干SQL語句置於一個批處理中執行。

140 在進行數據庫編程時,連接池有什麼作用?

由於創建連接和釋放連接都有很大的開銷(尤其是數據庫服務器不在本地時,每
次建立連接都需要進行TCP的三次握手,釋放連接需要進行TCP四次握手,造成的
開銷是不可忽視的),爲了提升系統訪問數據庫的性能,可以事先創建若干連接
置於連接池中,需要時直接從連接池獲取,使用結束時歸還連接池而不必關閉連接
,從而避免頻繁創建和釋放連接所造成的開銷,這是典型的用空間換取時間的策
略(浪費了空間存儲連接,但節省了創建和釋放連接的時間)。池化技術在Java
開發中是很常見的,在使用線程時創建線程池的道理與此相同。基於Java的開源
數據庫連接池主要有:C3P0、Proxool、DBCP、BoneCP、Druid等

141 什麼是DAO模式?

DAO(Data Access Object)顧名思義是一個爲數據庫或其他持久化機制提供了
抽象接口的對象,在不暴露底層持久化方案實現細節的前提下提供了各種數據訪問
操作。在實際的開發中,應該將所有對數據源的訪問操作進行抽象化後封裝在一個
公共API中。用程序設計語言來說,就是建立一個接口,接口中定義了此應用程序
中將會用到的所有事務方法。在這個應用程序中,當需要和數據源進行交互的時候
則使用這個接口,並且編寫一個單獨的類來實現這個接口,在邏輯上該類對應一個
特定的數據存儲。DAO模式實際上包含了兩個模式,一是Data Accessor(數據訪
問器),二是Data Object(數據對象),前者要解決如何訪問數據的問題,而後
者要解決的是如何用對象封裝數據。

142 事務的ACID是指什麼

原子性(Atomic):事務中各項操作,要麼全做要麼全不做,任何一項操作的失
敗都會導致整個事務的失敗;- 一致性(Consistent):事務結束後系統狀態是
一致的;- 隔離性(Isolated):併發執行的事務彼此無法看到對方的中間
狀態;- 持久性(Durable):事務完成後所做的改動都會被持久化,即使發
生災難性的失敗。通過日誌和同步備份可以在故障發生後重建數據。

143 髒讀

A事務讀取B事務尚未提交的數據並在此基礎上操作,而B事務執行回滾,那
麼A讀取到的數據就是髒數據。

144 不可重複讀

事務A重新讀取前面讀取過的數據,發現該數據已經被另一個已提交的事務
B修改過了。

145 幻讀

事務A重新執行一個查詢,返回一系列符合查詢條件的行,發現其中插入了被
事務B提交的行。

146 第1類丟失更新

事務A撤銷時,把已經提交的事務B的更新數據覆蓋了

147 第2類丟失更新

事務A覆蓋事務B已經提交的數據,造成事務B所做的操作丟失

148 JDBC中如何進行事務處理?

Connection提供了事務處理的方法,通過調用setAutoCommit(false)可以
設置手動提交事務;當事務完成後用commit()顯式提交事務;如果在事務處理
過程中發生異常則通過rollback()進行事務回滾。除此之外,從JDBC 3.0中還引
入了Savepoint(保存點)的概念,允許通過代碼設置保存點並讓事務回滾到指定的保存點。

149 JDBC能否處理Blob和Clob?

Blob是指二進制大對象(Binary Large Object),而Clob是指大字符對
象(Character Large Objec),因此其中Blob是爲存儲大的二進制數據
而設計的,而Clob是爲存儲大的文本數據而設計的。JDBC的PreparedStatement
和ResultSet都提供了相應的方法來支持Blob和Clob操作。

150 Java 中,編寫多線程程序的時候你會遵循哪些最佳實踐?

a)給線程命名,這樣可以幫助調試。b)最小化同步的範圍,而不是將整個方法
同步,只對關鍵部分做同步。c)如果可以,更偏向於使用 volatile 而不
是 synchronized。d)使用更高層次的併發工具,而不是使用 wait() 和 notify() 來
實現線程間通信,如 BlockingQueue,CountDownLatch 及 Semeaphore。e)優先使用
併發集合,而不是對集合進行同步。併發集合提供更好的可擴展性。

151 說出幾點 Java 中使用 Collections 的最佳實踐

a)使用正確的集合類,例如,如果不需要同步列表,使用 ArrayList 而不是 Vector。b)優
先使用併發集合,而不是對集合進行同步。併發集合提供更好的可擴展性。c)使用接口代表和
訪問集合,如使用List存儲 ArrayList,使用 Map 存儲 HashMap 等等。d)使用迭代器來循
環集合。e)使用集合的時候使用泛型。

152 說出至少 5 點在 Java 中使用線程的最佳實踐。

a)對線程命名b)將線程和任務分離,使用線程池執行器來執行 Runnable 或 Callable。c)使用線程池

153 說出 5 條 IO 的最佳實踐

a)使用有緩衝區的 IO 類,而不要單獨讀取字節或字符。b)使用 NIO 和 NIO2c)在 finally
塊中關閉流,或者使用 try-with-resource 語句。d)使用內存映射文件獲取更快的 IO。

154 列出 5 個應該遵循的 JDBC 最佳實踐

a)使用批量的操作來插入和更新數據b)使用 PreparedStatement 來避免 SQL 異常,並
提高性能。c)使用數據庫連接池d)通過列名來獲取結果集,不要使用列的下標來獲取。

155 在多線程環境下,SimpleDateFormat 是線程安全的嗎?

不是,非常不幸,DateFormat 的所有實現,包括 SimpleDateFormat 都不是線程安全
的,因此你不應該在多線程序中使用,除非是在對外線程安全的環境中使用,如
將 SimpleDateFormat 限制在 ThreadLocal 中。如果你不這麼做,在解析或
者格式化日期的時候,可能會獲取到一個不正確的結果。因此,從日期、時間處理的
所有實踐來說,我強力推薦 joda-time 庫。

156 Java 中如何格式化一個日期?如格式化爲 ddMMyyyy 的形式?

Java 中,可以使用 SimpleDateFormat 類或者 joda-time 庫來格式日期。DateFormat 類
允許你使用多種流行的格式來格式化日期。參見答案中的示例代碼,代碼中演示了將日期格式化
成不同的格式,如 dd-MM-yyyy 或 ddMMyyyy。

157 嵌套靜態類與頂級類有什麼區別?

一個公共的頂級類的源文件名稱與類名相同,而嵌套靜態類沒有這個要求。一個嵌套類位於頂
級類內部,需要使用頂級類的名稱來引用嵌套靜態類,如 HashMap.Entry 是一個嵌套靜態
類,HashMap 是一個頂級類,Entry是一個嵌套靜態類。

158 Java 中,Serializable 與 Externalizable 的區別?

Serializable 接口是一個序列化 Java 類的接口,以便於它們可以在網絡上傳輸或者可
以將它們的狀態保存在磁盤上,是 JVM 內嵌的默認序列化方式,成本高、脆弱而且
不安全。Externalizable 允許你控制整個序列化過程,指定特定的二進制格式,增加安全機制。

159 Java 中,DOM 和 SAX 解析器有什麼不同?

DOM 解析器將整個 XML 文檔加載到內存來創建一棵 DOM 模型樹,這樣可以更快的查
找節點和修改 XML 結構,而 SAX 解析器是一個基於事件的解析器,不會將整個 XML 文
檔加載到內存。由於這個原因,DOM 比 SAX 更快,也要求更多的內存,不適合於解析大 XML 文件。

160 說出 JDK 1.7 中的三個新特性?

雖然 JDK 1.7 不像 JDK 5 和 8 一樣的大版本,但是,還是有很多新的
特性,如 try-with-resource 語句,這樣你在使用流或者資源的時候,就不需
要手動關閉,Java 會自動關閉。Fork-Join 池某種程度上實現 Java 版的 Map-reduce。允
許 Switch 中有 String 變量和文本。菱形操作符(<>)用於類型推斷,不再需要在變量聲
明的右邊申明泛型,因此可以寫出可讀寫更強、更簡潔的代碼。另一個值得一提的特性是
改善異常處理,如允許在同一個 catch 塊中捕獲多個異常。

161 說出 5 個 JDK 1.8 引入的新特性?

Java 8 在 Java 歷史上是一個開創新的版本,下面 JDK 8 中 5 個主要的特
性:Lambda 表達式,允許像對象一樣傳遞匿名函數Stream API,充分利用現代多
核 CPU,可以寫出很簡潔的代碼Date 與 Time API,最終,有一個穩定、簡單
的日期和時間庫可供你使用擴展方法,現在,接口中可以有靜態、默認方法。重複
註解,現在你可以將相同的註解在同一類型上使用多次。

162 a==b”和”a.equals(b)”有什麼區別?

如果 a 和 b 都是對象,則 a==b 是比較兩個對象的引用,只有當 a 和 b 指向的
是堆中的同一個對象纔會返回 true,而 a.equals(b) 是進行邏輯比較,所以通常需
要重寫該方法來提供邏輯一致性的比較。例如,String 類重寫 equals() 方法,所
以可以用於兩個不同對象,但是包含的字母相同的比較。

163 a.hashCode() 有什麼用?與 a.equals(b) 有什麼關係?

hashCode() 方法是相應對象整型的 hash 值。它常用於基於 hash 的集合類,
如 Hashtable、HashMap、LinkedHashMap等等。它與 equals() 方法關係特別
緊密。根據 Java 規範,兩個使用 equal() 方法來判斷相等的對象,必須具有相
同的 hash code。

164 JVM爲什麼可以跨平臺

JVM能跨計算機體系結構(操作系統)來執行Java字節碼(JVM字節碼指令集),屏
蔽可與各個計算機平臺相關的軟件或者硬件之間的差異,使得與平臺相關的耦合統一
由JVM提供者來實現。
指令集:計算機所能識別的機器語言的命令集合。
每個運行中的Java程序都是一個JVM實例

165 描述JVM體系結構

(1)類加載器:JVM啓動時或者類運行時將需要的class加載到JVM中。每個被裝載的
類的類型對應一個Class實例,唯一表示該類,存於堆中。
(2)執行引擎:負責執行JVM的字節碼指令(CPU)。執行引擎是JVM的核心部分,作
用是解析字節碼指令,得到執行結果(實現方式:直接執行,JIT(just in time)
即時編譯轉成本地代碼執行,寄存器芯片模式執行,基於棧執行)。本質上就是一個
個方法串起來的流程。每個Java線程就是一個執行引擎的實例,一個JVM實例中會有
多個執行引擎在工作,有的執行用戶程序,有的執行JVM內部程序(GC).
(3)內存區:模擬物理機的存儲、記錄和調度等功能模塊,如寄存器或者PC指針記錄器
。存儲執行引擎執行時所需要存儲的數據。
(4)本地方法接口:調用操作系統本地方法返回結果。

166 描述JVM工作機制

機器如何執行代碼:源代碼-預處理器-編譯器-彙編程序-目標代碼-鏈接器-可執行程
序。
Java編譯器將高級語言編譯成虛擬機目標語言。
JVM執行字節碼指令是基於棧的架構,所有的操作數必須先入棧,然後根據操作碼選擇
從棧頂彈出若干元素進行計算後將結果壓入棧中。
通過Java編譯器將源代碼編譯成虛擬機目標語言,然後通過JVM執行引擎執行。

167 爲何JVM字節碼指令選擇基於棧的結構

JVM要設計成平臺無關性,很難設計統一的基於寄存器的指令。
爲了指令的緊湊性,讓編譯後的class文件更加緊湊,提高字節碼在網絡上的傳輸效率

168 描述執行引擎的架構設計

創建新線程時,JVM會爲這個線程創建一個棧,同時分配一個PC寄存器(指向第一行可
執行的代碼)。調用新方法時會在這個棧上創建新的棧幀數據結構。執行完成後方法對
應的棧幀將消失,PC寄存器被銷燬,局部變量區所有值被釋放,被JVM回收。

169 描述javac編譯器的基本結構

Javac編譯器的作用是將符合Java語言規範的的源代碼轉換成JVM規範的Java字節碼。
(1)詞法分析器組件:找出規範化的Token流
(2)語法分析器組件:生成符合Java語言規範的抽象語法樹
(3)語義分析器組件:將複雜的語法轉化成最簡單的語法,註解語法樹
(4)代碼生成器組件:將語法樹數據結構轉化成字節碼數據結構

170 ClassLoader(類加載器)有哪些

1)Bootstrap ClassLoader(啓動類加載器):完全由JVM控制,加載JVM自身工作需要
的類(JAVA_HOME/lib)
(2)Extension ClassLoader(擴展類加載器):屬於JVM自身一部分,不是JVM自身實
現的(JAVA_HOME/lib/ext)
(3)Appclication ClassLoader(應用程序類加載器):父類是Extension ClassLoader,
加載Classpath(用戶類路徑)上的類庫

171 描述ClassLoader的作用(什麼是類加載器)和加載過程

將Class文件加載到JVM中、審查每個類由誰加載(父優先的等級加載機制)、將Class字
節碼重新解析成JVM統一要求的對象(Class對象)格式。
.class->findclass->Liking:Class規範驗證、準備、解析->類屬性初始化賦值
(static塊的執行)->Class對象(這也就是爲什麼靜態塊只執行一次)

172 描述JVM類加載機制

ClassLoader首先不會自己嘗試去加載類,而是把這個請求委託給父類加載器完成,
每一個層次都是。只有當父加載器反饋無法完成請求時(在搜索範圍內沒有找到所需
的類),子加載器纔會嘗試加載(等級加載機制、父優先、雙親委派)。
好處:類隨着它的加載器一起具有一種帶有優先級的層次關係;保證同一個類只能
被一個加載器加載。

173 JVM加載class文件到內存的兩種方式

(1)隱式加載:繼承或者引用的類不在內存中
(2)顯式加載:代碼中通過調用ClassLoader加載

174 加載類錯誤分析及其解決

1)ClassNotFoundException:沒有找到對應的字節碼(.class)文件;檢查
classpath下有無對應文件
(2)NoClassDefFoundError:隱式加載時沒有找到,ClassNotFoundException引
發NoClassDefFoundError;確保每個類引用的類都在classpath下
(3)UnsatisfiedLinkError:(未滿足鏈接錯誤)刪除了JVM的某個lib文件或者解
析native標識的方法時找不到對應的本地庫文件
(4)ClassCastException:強制類型轉換時出現這個錯誤;容器類型最好顯示指明其
所包含對象類型、先instanceof檢查是不是目標類型,再類型轉換
(5)ExceptionInitializerError:給類的靜態屬性賦值時

175 Java應不應該動態加載類(JVM能不能動態加載類)

JVM中對象只有一份,不能被替換,對象的引用關係只有對象的創建者持有和使用,
JVM不可干預對象的引用關係,因爲JVM不知道對象是怎麼被使用的,JVM不知道對象
的運行時類型,只知道編譯時類型。
但是可以不保存對象的狀態,對象創建和使用後就被釋放掉,下次修改後,對象就是
新的了(JSP)。

176 Java中哪些組件需要使用內存

(1)Java堆:存儲Java對象
(2)線程:Java運行程序的實體
(3)類和類加載器:存儲在堆中,這部分區域叫永久代(PermGen區)
(4)NIO:基於通道和緩衝區來執行I/O的新方式。
(5)JNI:本地代碼可以調用Java方法,Java方法也可以調用本地代碼

177 描述JVM內存結構及內存溢出

JVM是按照運行時數據的存儲結構來劃分內存結構的。
PC寄存器數據:嚴格來說是一個數據結構,保存當前正在執行的程序的內存地址。
爲了線程切換後能恢復到正確的執行位置,線程私有。不會內存溢出。
(1)Java棧:方法執行的內存模型,存儲線程執行所需要的數據。線程私有。
–OutOfMemoryError:JVM擴展棧時無法申請到足夠的空間。一個不斷調用自身而
不會終止的方法。
–StackOverflowError:請求的棧深度大於JVM所允許的棧深度。創建足夠多的線程。
(2)堆:存儲對象,每一個存在堆中Java對象都是這個對象的類的副本,複製包括
繼承自他父類的所有非靜態屬性。線程共享。
–OutOfMemoryError:對象數量到達堆容量限制。可通過不斷向ArrayList中添加
對象實現。

178 描述JVM內存結構及內存溢出

(3)方法區:存儲類結構信息。包括常量池(編譯期生產的各種字面量和符號引用)
和運行時常量池。線程共享。
–OutOfMemoryError:同運行時常量池。
(4)本地方法棧:與Java棧類似,爲JVM運行Native方法準備的空間。線程私有。
(C棧)OutOfMemoryError和StackOverflowError同JVM棧。
(5)運行時常量池:代表運行時每個class文件中的常量表。運行期間產生的新的常
量放入運行時常量池。
–OutOfMemoryError:不斷向List中添加字符串,然後String.inern(),
PermGen Space(運行時常量池屬於方法區)。
(6)本地直接內存:即NIO。
–OutOfMemoryError:通過直接向操作系統申請分配內存。

179 描述JVM內存分配策略

(1)對象優先分配在Eden
(2)大對象直接進入老年代
(3)長期存活的對象將進入老年代
(4)倖存區相同年齡對象的佔倖存區空間的多於其一半,將進入老年代
(5)空間擔保分配(老年代剩餘空間需多於倖存區的一半,否則要Full GC)

180 描述JVM如何檢測垃圾

通過可達性分析算法,通過一些列稱爲GC Roots的對象作爲起始點,從這些起始
點向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引
用鏈相連(GC Roots到這個對象不可達),則證明這個對象是不可用的。
使用可達性分析算法而不是引用計數算法。因爲引用計數算法很難解決對象之間相
互循環引用的問題。

181 哪些元素可作爲GC Roots

(1)JVM棧(棧幀中的本地變量表)中的引用
(2)方法區中類靜態屬性引用
(3)方法區中常量引用
(4)本地方法棧中JNI(一般的Native方法)引用

182 描述分代垃圾收集算法的思路

把對象按照壽命長短來分組,分爲年輕代和老年代,新創建的在老年代,經歷幾次
回收後仍然存活的對象進入老年代,老年代的垃圾頻率不像年輕代那樣頻繁,減少
每次收集都去掃描所有對象的數量,提高垃圾回收效率。

183 描述基於分代的堆結構及其比例

(1)年輕代(Young區-1/4):Eden+Survior(1/8,這個比例保證只有10%的空間被
浪費,保證每次回收都只有不多於10%的對象存活)=From+To,存放新創建的對象.
(2)老年代(Old區 ):存放幾次垃圾收集後存活的對象
(3)永久區(Perm區):存放類的Class對象

184 描述垃圾收集算法

1)標記-清除算法:首先標記處所要回收的對象,標記完成後統一清除。缺點:標記
效率低,清除效率低,回收結束後會產生大量不連續的內存碎片(沒有足夠連續空間
分配內存,提前觸發另一次垃圾回收)。適用於對象存活率高的老年代。
(2)複製算法(Survivor的from和to區,from和to會互換角色):
將內存容量劃分大小相等的兩塊,每次只使用其中一塊。一塊用完,就將存活的對象
複製到另一塊,然後把使用過的一塊一次清除。不用考慮內存碎片,每次只要移動頂
端指針,按順序分配內存即可,實現簡單運行高效。適用於新生代。
缺點:內存縮小爲原來的一般,代價高。浪費50%的空間。
(3)標記-整理算法:
標記完成後,將存活的對象移動到一端,然後清除邊界以外的內存。適用於對象存活
率高的老年代。

185 描述新生代和老年代的回收策略

Eden區滿後觸發minor GC,將所有存活對象複製到一個Survivor區,另一Survivor
區存活的對象也複製到這個Survivor區中,始終保證有一個Survivor是空的。
Toung區Survivor滿後觸發minor GC後仍然存活的對象存到Old區,如果Survivor
區放不下Eden區的對象或者Survivor區對象足夠老了,直接放入Old區,如果Old
區放不下則觸發Full GC。
Perm區滿將觸發Major GC。

186 描述CMS垃圾收集器

CMS 收集器:Concurrent Mark Sweep 併發標記-清除。重視響應速度,適用於互
聯網和B/S系統的服務端上。初始標記還是需要Stop the world 但是速度很快。缺
點:CPU資源敏感,無法浮動處理垃圾,會有大量空間碎片產生。

187 MySQL的複製原理以及流程

基本原理流程,3個線程以及之間的關聯;

  1. 主:binlog線程——記錄下所有改變了數據庫數據的語句,放進master上的binlog中;
  2. 從:io線程——在使用start slave 之後,負責從master上拉取 binlog 內
    容,放進 自己的relay log中;
  3. 從:sql執行線程——執行relay log中的語句;

188 MySQL中myisam與innodb的區別

1>.InnoDB支持事物,而MyISAM不支持事物
2>.InnoDB支持行級鎖,而MyISAM支持表級鎖
3>.InnoDB支持MVCC, 而MyISAM不支持
4>.InnoDB支持外鍵,而MyISAM不支持
5>.InnoDB不支持全文索引,而MyISAM支持。

189 innodb引擎的4大特性

插入緩衝(insert buffer),二次寫(double write),自適應哈希索引(ahi),預讀(read ahead)

190 MySQL中varchar與char的區別以及varchar(50)中的50代表的涵義

1)、varchar與char的區別char是一種固定長度的類型,varchar則是一種可變
長度的類型(2)、varchar(50)中50的涵義最多存放50個字符,varchar(50)和
(200)存儲hello所佔空間一樣,但後者在排序時會消耗更多內存,因爲order by col採
用fixed_length計算col長度(memory引擎也一樣)(3)、int(20)中20的涵義是指顯
示字符的長度但要加參數的,最大爲255,比如它是記錄行數的id,插入10筆資料,它
就顯示00000000001 ~~~00000000010,當字符的位數超過11,它也只顯示11位,如果
你沒有加那個讓它未滿11位就前面加0的參數,它不會在前面加020表示最大顯示寬度爲
20,但仍佔4字節存儲,存儲範圍不變;(4)、mysql爲什麼這麼設計對大多數應用沒有
意義,只是規定一些工具用來顯示字符的個數;int(1)和int(20)存儲和計算均一樣

191 innodb有多少種日誌

錯誤日誌:記錄出錯信息,也記錄一些警告信息或者正確的信息。查詢日誌:記錄所
有對數據庫請求的信息,不論這些請求是否得到了正確的執行。慢查詢日誌:設置一個
閾值,將運行時間超過該值的所有SQL語句都記錄到慢查詢的日誌文件中。二進制日誌:記錄
對數據庫執行更改的所有操作。中繼日誌:事務日誌

192 事物的4種隔離級別

隔離級別讀未提交(RU)讀已提交(RC)可重複讀(RR)串行

193 如何設計一個高併發的系統

  • 數據庫的優化,包括合理的事務隔離級別、SQL語句優化、索引的優化
  • 使用緩存,儘量減少數據庫 IO
  • 分佈式數據庫、分佈式緩存
  • 服務器的負載均衡

194 鎖的優化策略

  • 讀寫分離
  • 分段加鎖
  • 減少鎖持有的時間
  • 多個線程儘量以相同的順序去獲取資源

195 索引的底層實現原理和優化

B+樹,經過優化的B+樹
主要是在所有的葉子結點中增加了指向下一個葉子節點的指針,因此InnoDB建議爲大部
分表使用默認自增的主鍵作爲主索引。

196 什麼情況下設置了索引但無法使用

  • 以“%”開頭的LIKE語句,模糊匹配
  • OR語句前後沒有同時使用索引
  • 數據類型出現隱式轉化(如varchar不加單引號的話可能會自動轉換爲int型)

197 實踐中如何優化MySQL

  • SQL語句及索引的優化
  • 數據庫表結構的優化
  • 系統配置的優化
  • 硬件的優化

198 簡單描述mysql中,索引,主鍵,唯一索引,聯合索引的區別,對數據庫的性能有什麼影響

索引是一種特殊的文件(InnoDB數據表上的索引是表空間的一個組成部分),它們包含着
對數據表裏所有記錄的引用指針。普通索引(由關鍵字KEY或INDEX定義的索引)的唯一任
務是加快對數據的訪問速度。普通索引允許被索引的數據列包含重複的值。如果能確定
某個數據列將只包含彼此各不相同的值,在爲這個數據列創建索引的時候就應該用關鍵字
UNIQUE把它定義爲一個唯一索引。也就是說,唯一索引可以保證數據記錄的唯一性。主
鍵,是一種特殊的唯一索引,在一張表中只能定義一個主鍵索引,主鍵用於唯一標識一條
記錄,使用關鍵字PRIMARY KEY 來創建。索引可以覆蓋多個數據列,如像
INDEX(columnA, columnB)索引,這就是聯合索引。索引可以極大的提高數據的
查詢速度,但是會降低插入、刪除、更新表的速度,因爲在執行這些寫操作時,還要操作
索引文件

199 數據庫中的事務是什麼?

事務(transaction)是作爲一個單元的一組有序的數據庫操作。如果組中的所有操作都
成功,則認爲事務成功,即使只有一個操作失敗,事務也不成功。如果所有操作完成,事
務則提交,其修改將作用於所有其他數據庫進程。如果一個操作失敗,則事務將回滾,該
事務所有操作的影響都將取消。ACID 四大特性,原子性、隔離性、一致性、持久性

200 瞭解XSS攻擊嗎?如何防止?

XSS是跨站腳本攻擊,首先是利用跨站腳本漏洞以一個特權模式去執行攻擊者構造的腳
本,然後利用不安全的Activex控件執行惡意的行爲。使用htmlspecialchars()函數
對提交的內容進行過濾,使字符串裏面的特殊符號實體化

201 SQL注入漏洞產生的原因?如何防止

SQL注入產生的原因:程序開發過程中不注意規範書寫sql語句和對特殊字符進行過濾,
導致客戶端可以通過全局變量POST和GET提交一些sql語句正常執行。
防止SQL注入的方式:
開啓配置文件中的magic_quotes_gpc 和 magic_quotes_runtime設置
執行sql語句時使用addslashes進行sql語句轉換
Sql語句書寫儘量不要省略雙引號和單引號。
過濾掉sql語句中的一些關鍵詞:update、insert、delete、select、 * 。
提高數據庫表和字段的命名技巧,對一些重要的字段根據程序的特點命名,取不易被猜到的。
Php配置文件中設置register_globals爲off,關閉全局變量註冊
控制錯誤信息,不要在瀏覽器上輸出錯誤信息,將錯誤信息寫到日誌文件中

202 解釋MySQL外連接、內連接與自連接的區別

先說什麼是交叉連接: 交叉連接又叫笛卡爾積,它是指不使用任何條件,直接將一個表的
所有記錄和另一個表中的所有記錄一一匹配。
內連接 則是隻有條件的交叉連接,根據某個條件篩選出符合條件的記錄,不符合條件的記
錄不會出現在結果集中,即內連接只連接匹配的行。外連接 其結果集中不僅包含符合連
接條件的行,而且還會包括左表、右表或兩個表中的所有數據行,這三種情況依次稱之爲左
外連接,右外連接,和全外連接。
左外連接,也稱左連接,左表爲主表,左表中的所有記錄都會出現在結果集中,對於那些
在右表中並沒有匹配的記錄,仍然要顯示,右邊對應的那些字段值以NULL來填充。右外連
接,也稱右連接,右表爲主表,右表中的所有記錄都會出現在結果集中。左連接和右連接可
以互換,MySQL目前還不支持全外連接

203 Myql中的事務回滾機制概述

事務是用戶定義的一個數據庫操作序列,這些操作要麼全做要麼全不做,是一個不可分割的工
作單位,事務回滾是指將該事務已經完成的對數據庫的更新操作撤銷。
要同時修改數據庫中兩個不同表時,如果它們不是一個事務的話,當第一個表修改完,可能
第二個表修改過程中出現了異常而沒能修改,此時就只有第二個表依舊是未修改之前的狀
態,而第一個表已經被修改完畢。而當你把它們設定爲一個事務的時候,當第一個表修改
完,第二表修改出現異常而沒能修改,第一個表和第二個表都要回到未修改的狀態,這就
是所謂的事務回滾

204 什麼是存儲過程?用什麼來調用?

存儲過程是一個預編譯的SQL語句,優點是允許模塊化的設計,就是說只需創建一次,以後
在該程序中就可以調用多次。如果某次操作需要執行多次SQL,使用存儲過程比單純SQL語
句執行要快。可以用一個命令對象來調用存儲過程

205 MySQL數據庫作發佈系統的存儲,一天五萬條以上的增量,預計運維三年,怎麼優化?

a. 設計良好的數據庫結構,允許部分數據冗餘,儘量避免join查詢,提高效率。
b. 選擇合適的表字段數據類型和存儲引擎,適當的添加索引。
c. mysql庫主從讀寫分離。
d. 找規律分表,減少單表中的數據量提高查詢速度。
e。添加緩存機制,比如memcached,apc等。
f. 不經常改動的頁面,生成靜態頁面。
g. 書寫高效率的SQL。比如 SELECT * FROM TABEL 改爲 SELECT field_1,
field_2, field_3 FROM TABLE.

206  對於大流量的網站,您採用什麼樣的方法來解決各頁面訪問量統計問題

a. 確認服務器是否能支撐當前訪問量。
b. 優化數據庫訪問。
c. 禁止外部訪問鏈接(盜鏈), 比如圖片盜鏈。
d. 控制文件下載。
e. 使用不同主機分流。
f. 使用瀏覽統計軟件,瞭解訪問量,有針對性的進行優化。

207 存儲時期

Datatime:以 YYYY-MM-DD HH:MM:SS 格式存儲時期時間,精確到秒,佔用8個字節
得存儲空間,datatime類型與時區無關
Timestamp:以時間戳格式存儲,佔用4個字節,範圍小1970-1-1到2038-1-19,顯示
依賴於所指定得時區,默認在第一個列行的數據修改時可以自動得修改timestamp列得值
Date:(生日)佔用得字節數比使用字符串.datatime.int儲存要少,使用date只需
要3個字節,存儲日期月份,還可以利用日期時間函數進行日期間得計算
Time:存儲時間部分得數據
注意:不要使用字符串類型來存儲日期時間數據(通常比字符串佔用得儲存空間小,
在進行查找過濾可以利用日期得函數)
使用int存儲日期時間不如使用timestamp類型

208 Hibernate工作原理

1.讀取並解析配置文件 2.讀取並解析映射信息,創建SessionFactory 3.打開
Sesssion 4.創建事務Transation 5.持久化操作 6.提交事務 7.關閉Session 
8.關閉SesstionFactory (finally進行執行)

209 Hibernate中get和load有什麼不同之處

把get和load放到一起進行對比是Hibernate面試時最常問到的問題,這是因爲只有
正確理解get()和load()這二者後纔有可能高效地使用Hibernate。get和load的最
大區別是,如果在緩存中沒有找到相應的對象,get將會直接訪問數據庫並返回一個
完全初始化好的對象,而這個過程有可能會涉及到多個數據庫調用;而load方法在緩
存中沒有發現對象的情況下,只會返回一個代理對象,只有在對象getId()之外的其它
方法被調用時纔會真正去訪問數據庫,這樣就能在某些情況下大幅度提高性能

210 Hibernate中save、persist和saveOrUpdate這三個方法的不同之處

除了get和load,這又是另外一個經常出現的Hibernate面試問題。 所有這三個方法,
也就是save()、saveOrUpdate()和persist()都是用於將對象保存到數據庫中的方法,
但其中有些細微的差別。例如,save()只能INSERT記錄,但是saveOrUpdate()可以
進行 記錄的INSERT和UPDATE。還有,save()的返回值是一個Serializable對象,
而persist()方法返回值爲void

211 Hibernate中的命名SQL查詢指的是什麼

Hibernate的這個面試問題同Hibernate提供的查詢功能相關。命名查詢指的是
用標籤在影射文檔中定義的SQL查詢,可以通過使用Session.getNamedQuery()
方法對它進行調用。命名查詢使你可以使用你所指定的一個名字拿到某個特定的查詢。
Hibernate中的命名查詢可以使用註解來定義,也可以使用我前面提到的xml影射問句
來定義。在Hibernate中,@NameQuery用來定義單個的命名查詢,@NameQueries用來定義多個命名查詢。

212 Hibernate中的SessionFactory有什麼作用? SessionFactory是線程安全的嗎

這也是Hibernate框架的常見面試問題。顧名思義,SessionFactory就是一個用於
創建Hibernate的Session對象的工廠。SessionFactory通常是在應用啓動時創建
好的,應用程序中的代碼用它來獲得Session對象。作爲一個單個的數據存儲,它也
是 線程安全的,所以多個線程可同時使用同一個SessionFactory。Java JEE應用
一般只有一個SessionFactory,服務於客戶請求的各線程都通過這個工廠來獲得
Hibernate的Session實例,這也是爲什麼SessionFactory接口的實現必須是線
程安全的原因。還有,SessionFactory的內部狀態包含着同對象關係影射有關的所
有元數據,它是 不可變的,一旦創建好後就不能對其進行修改了。

213 Hibernate中的Session指的是什麼? 可否將單個的Session在多個線程間進行共享

前面的問題問完之後,通常就會接着再問這兩個問題。問完SessionFactory的問題
後就該輪到Session了。Session代表着Hibernate所做的一小部分工作,它負責維
護者同數據庫的鏈接而且 不是線程安全的,也就是說,Hibernage中的Session不能
在多個線程間進行共享。雖然Session會以主動滯後的方式獲得數據庫連接,但是
Session最好還是在用完之後立即將其關閉。

214 hibernate中sorted collection和ordered collection有什麼不同

Hibernate中,對象具有三種狀態:transient、persistent和detached。同
Hibernate的session有關聯的對象是persistent對象。對這種對象進行的所有
修改都會按照事先設定的刷新策略,反映到數據庫之中,也即,可以在對象的任何
一個屬性發生改變時自動刷新,也可以通過調用Session.flush()方法顯式地進行
刷新。如果一個對象原來同Session有關聯關係,但當下卻沒有關聯關係了,這樣的
對象就是detached的對象。你可以通過調用任意一個session的update()或者
saveOrUpdate()方法,重新將該detached對象同相應的seesion建立關聯關係。
Transient對象指的是新建的持久化類的實例,它還從未同Hibernate的任何
Session有過關聯關係。同樣的,你可以調用persist()或者save()方法,將
transient對象變成persistent對象

215 Hibernate中Session的lock()方法有什麼作用

這是一個比較棘手的Hibernate面試問題,因爲Session的lock()方法重建了關聯
關係卻並沒有同數據庫進行同步和更新。因此,你在使用lock()方法時一定要多加
小心。順便說一下,在進行關聯關係重建時,你可以隨時使用Session的update()
方法同數據庫進行同步。有時這個問題也可以這麼來問:Session的lock()方法和
update()方法之間有什麼區別?。這個小節中的關鍵點也可以拿來回答這個問題。

216 Hibernate中二級緩存指的是什麼

這是同Hibernate的緩存機制相關的第一個面試問題,不出意外後面還會有更多這方
面的問題。二級緩存是在SessionFactory這個級別維護的緩存,它能夠通過節省幾
番數據庫調用往返來提高性能。還有一點值得注意,二級緩存是針對整個應用而不是
某個特定的session的。

217 Hibernate中的查詢緩存指的是什麼

這個問題有時是作爲上個Hibernate面試問題的後繼問題提出的。查詢緩存實際上保
存的是sql查詢的結果,這樣再進行相同的sql查詢就可以之間從緩存中拿到結果了。
爲了改善性能,查詢緩存可以同二級緩存一起來使用。Hibernate支持用多種不同的
開源緩存方案,比如EhCache,來實現查詢緩存

218 爲什麼在Hibernate的實體類中要提供一個無參數的構造器這一點非常重要

每個Hibernate實體類必須包含一個 無參數的構造器, 這是因爲Hibernate框架要
使用Reflection API,通過調用Class.newInstance()來創建這些實體類的實例。
如果在實體類中找不到無參數的構造器,這個方法就會拋出一個InstantiationException異常

219 可不可以將Hibernate的實體類定義爲final類

是的,你可以將Hibernate的實體類定義爲final類,但這種做法並不好。因爲
Hibernate會使用代理模式在延遲關聯的情況下提高性能,如果你把實體類定義成
final類之後,因爲 Java不允許對final類進行擴展,所以Hibernate就無法再使
用代理了,如此一來就限制了使用可以提升性能的手段。不過,如果你的持久化類實
現了一個接口而且在該接口中聲明瞭所有定義於實體類中的所有public的方法輪到
話,你就能夠避免出現前面所說的不利後果

220 hibernate的三種狀態之間如何轉換

當對象由瞬時狀態(Transient)一save()時,就變成了持久化狀態; 當我們在
Session裏存儲對象的時候,實際是在Session的Map裏存了一份, 也就是它的緩
存裏放了一份,然後,又到數據庫裏存了一份,在緩存裏這一份叫持久對象
(Persistent)。 Session 一 Close()了,它的緩存也都關閉了,整個Session也
就失效了,這個時候,這個對象變成了遊離狀態(Detached),但數據庫中還是存在
的。 當遊離狀態(Detached)update()時,又變爲了持久狀態(Persistent)。
當持久狀態(Persistent)delete()時,又變爲了瞬時狀態(Transient), 此時,
數據庫中沒有與之對應的記錄

221 Hibernate是如何延遲加載

當Hibernate在查詢數據的時候,數據並沒有存在與內存中,當程序真正對數據的
操作時,對象才存在與內存中,就實現了延遲加載,他節省了服務器的內存開銷,
從而提高了服務器的性能

222 解Hibernate中怎樣實現類之間的關係

類與類之間的關係主要體現在表與表之間的關係進行操作,它們都是對對象進行操
作,我們程序中把所有的表與類都映射在一起,它們通過配置文件中的
many-to-one、one-to-many、many-to-many

223 如何優化Hibernate

1.使用雙向一對多關聯,不使用單向一對多 2.靈活使用單向一對多關聯 
3.不用一對一,用多對一取代 4.配置對象緩存,不使用集合緩存 5.一對多
集合使用Bag,多對多集合使用Set 6. 繼承類使用顯式多態 7. 表字段要少,
表關聯不要怕多,有二級緩存撐腰

224 Hibernate的五個核心接口

Configuration 接口:配置Hibernate,根據其啓動hibernate,創建
SessionFactory 對象; SessionFactory 接口:初始化Hibernate,充當
數據存儲源的代理,創建 session 對象,sessionFactory 是線程安全的,
意味着它的同一個實例可以被應 用的多個線程共享,是重量級、二級緩存;
Session 接口:負責保存、更新、刪除、加載和查詢對象,是線程不安全的,
避免多個線程共享同一個session,是輕量級、一級緩存; Transaction 接口:
管理事務; Query 和Criteria 接口:執行數據庫的查詢

225 #{}和${}的區別是什麼

Propertiessql{}是Properties文件中的變量佔位符,它可以用於標籤屬性值和sql內部, 屬於靜態文本替換,比如{driver}會被靜態替換爲com.mysql.jdbc.Driver。#{}是sql
的參數佔位符,Mybatis會將sql中的#{}替換爲?號,在sql執行前會使用
PreparedStatement的參數設置方法,按序給sql的?號佔位符設置參數值,
比如ps.setInt(0, parameterValue),#{item.name}的取值方式爲使用反
射從參數對象中獲取item對象的name屬性值,相當於param.getItem().getName()。

226 Xml映射文件中,除了常見的select|insert|updae|delete標籤之外,還有哪些標籤

還有很多其他的標籤,、、、、
,加上動態sql的9個標籤,trim|where|set|foreach|if|choose|when|otherwise|bind等,
其中爲sql片段標籤,通過標籤引入sql片段,爲不支持自增的主鍵生成策略標籤

227 最佳實踐中,通常一個Xml映射文件,都會寫一個Dao接口與之對應,請問,這個Dao接口的工作原理是什麼?Dao接口裏的方法,參數不同時,方法能重載嗎

Dao接口,就是人們常說的Mapper接口,接口的全限名,就是映射文件中的namespace的
值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法內的參數,就
是傳遞給sql的參數。Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方
法名拼接字符串作爲key值,可唯一定位一個MappedStatement,舉例:
com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到
namespace爲com.mybatis3.mappers.StudentDao下面id = findStudentById的
MappedStatement。在Mybatis中,每一個、、、
標籤,都會被解析爲一個MappedStatement對象。
Dao接口裏的方法,是不能重載的,因爲是全限名+方法名的保存和尋找策略。
Dao接口的工作原理是JDK動態代理,Mybatis運行時會使用JDK動態代理爲Dao接口生
成代理proxy對象,代理對象proxy會攔截接口方法,轉而執行MappedStatement所代
表的sql,然後將sql執行結果返回

228 Mybatis是如何進行分頁的?分頁插件的原理是什麼

Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,
而非物理分頁,可以在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以
使用分頁插件來完成物理分頁。
分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截
方法內攔截待執行的sql,然後重寫sql,根據dialect方言,添加對應的物理分頁語句
和物理分頁參數。
舉例:select * from student,攔截sql後重寫爲:select t.* from (select

  • from student)t limit 0,10

229 爲什麼說Mybatis是半自動ORM映射工具?它與全自動的區別在哪裏?

Hibernate屬於全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對
象時,可以根據對象關係模型直接獲取,所以它是全自動的。而Mybatis在查詢關聯對
象或關聯集合對象時,需要手動編寫sql來完成,所以,稱之爲半自動ORM映射工具

230 Mybatis比IBatis比較大的幾個改進是什麼

a.有接口綁定,包括註解綁定sql和xml綁定Sql ,
b.動態sql由原來的節點配置變成OGNL(對象圖導航語言)表達式,
c. 在一對一,一對多的時候引進了association,在一對多的時候引入了collection節
點,不過都是在resultMap裏面配置

231 接口綁定有幾種實現方式,分別是怎麼實現的

接口綁定有兩種實現方式,一種是通過註解綁定,就是在接口的方法上面加上
@Select@Update等註解裏面包含Sql語句來綁定,另外一種就是通過xml裏面寫SQL來綁定,
在這種情況下,要指定xml映射文件裏面的namespace必須爲接口的全路徑名.

232 MyBatis實現一對一有幾種方式?具體怎麼操作的

有聯合查詢和嵌套查詢,聯合查詢是幾個表聯合查詢,只查詢一次,
通過在resultMap裏面配置association節點配置一對一的類就可以完成;
嵌套查詢是先查一個表,根據這個表裏面的結果的外鍵id,去再另外一個表裏面查
詢數據,也是通過association配置,但另外一個表的查詢通過select屬性配置

233 MyBatis的緩存

MyBatis的緩存分爲一級緩存和二級緩存,
一級緩存放在session裏面,默認就有,二級緩存放在它的命名空間裏,默認是打開的
, 使用二級緩存屬性類需要實現Serializable序列化接口(可用來保存對象的狀態)
,可在它的映射文件中配置<cache/

234 什麼是spring?

Spring 是個java企業級應用的開源開發框架。Spring主要用來開發Java應用,但
是有些擴展是針對構建J2EE平臺的web應用。Spring 框架目標是簡化Java企業級應
用開發,並通過POJO爲基礎的編程模型促進良好的編程習慣。

235 使用Spring框架的好處是什麼

輕量:Spring 是輕量的,基本的版本大約2MB
控制反轉:Spring通過控制反轉實現了鬆散耦合,對象們給出它們的依賴,而不是
創建或查找依賴的對象們
面向切面的編程(AOP):Spring支持面向切面的編程,並且把應用業務邏輯和系統
服務分開
容器:Spring 包含並管理應用中對象的生命週期和配置
MVC框架:Spring的WEB框架是個精心設計的框架,是Web框架的一個很好的替代品
事務管理:Spring 提供一個持續的事務管理接口,可以擴展到上至本地事務下至全
局事務(JTA)
異常處理:Spring 提供方便的API把具體技術相關的異常(比如由JDBC,Hibernate
or JDO拋出的)轉化爲一致的unchecked 異常

236 Spring由哪些模塊組成

Core module
Bean module
Context module
Expression Language module
JDBC module
ORM module
OXM module
Java Messaging Service(JMS) module
Transaction module
Web module
Web-Servlet module
Web-Struts module
Web-Portlet module

237 核心容器(應用上下文) 模塊

這是基本的Spring模塊,提供spring 框架的基礎功能,BeanFactory 是 任何
以spring爲基礎的應用的核心。Spring 框架建立在此模塊之上,它使Spring成爲一個容器

238 BeanFactory – BeanFactory 實現舉例

Bean 工廠是工廠模式的一個實現,提供了控制反轉功能,用來把應用的配置和
依賴從正真的應用代碼中分離。
最常用的BeanFactory 實現是XmlBeanFactory 類

239 XMLBeanFactory

最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,
它根據XML文件中的定義加載beans。該容器從XML 文件讀取配置元數據並用它去
創建一個完全配置的系統或應用

240 解釋AOP模塊

AOP模塊用於發給我們的Spring應用做面向切面的開發, 很多支持由AOP聯盟提供,
這樣就確保了Spring和其他AOP框架的共通性。這個模塊將元數據編程引入Spring

241 什麼是Spring IOC 容器

Spring IOC 負責創建對象,管理對象(通過依賴注入(DI),裝配對象,配置對象,
並且管理這些對象的整個生命週期

242 IOC的優點是什麼

IOC 或 依賴注入把應用的代碼量降到最低。它使應用容易測試,單元測試不再需要單例
和JNDI查找機制。最小的代價和最小的侵入性使鬆散耦合得以實現。IOC容器支持加載
服務時的餓漢式初始化和懶加載

243 ApplicationContext通常的實現是什麼

FileSystemXmlApplicationContext :此容器從一個XML文件中加載beans的定義,
XML Bean 配置文件的全路徑名必須提供給它的構造函數。
ClassPathXmlApplicationContext:此容器也從一個XML文件中加載beans的定義,
這裏,你需要正確設置classpath因爲這個容器將在classpath裏找bean配置。
WebXmlApplicationContext:此容器加載一個XML文件,此文件定義了一個WEB應用
的所有bean

244 Bean 工廠和 Application contexts  有什麼區別?

Application contexts提供一種方法處理文本消息,一個通常的做法是加載文件資源
(比如鏡像),它們可以向註冊爲監聽器的bean發佈事件。另外,在容器或容器內的對
象上執行的那些不得不由bean工廠以程序化方式處理的操作,可以在Application
contexts中以聲明的方式處理。Application contexts實現了MessageSource接口,
該接口的實現以可插拔的方式提供獲取本地化消息的方

245 什麼是Spring的依賴注入

依賴注入,是IOC的一個方面,是個通常的概念,它有多種解釋。這概念是說你不用創建
對象,而只需要描述它如何被創建。你不在代碼裏直接組裝你的組件和服務,但是要在
配置文件裏描述哪些組件需要哪些服務,之後一個容器(IOC容器)負責把他們組裝起來

246 有哪些不同類型的IOC(依賴注入)方式

構造器依賴注入:構造器依賴注入通過容器觸發一個類的構造器來實現的,該類有一系
列參數,每個參數代表一個對其他類的依賴。
Setter方法注入:Setter方法注入是容器通過調用無參構造器或無參static工廠 方法
實例化bean之後,調用該bean的setter方法,即實現了基於setter的依賴注入

247 Spring 的優點

(1)spring屬於低侵入式設計,代碼的污染極低;
(2)spring的DI機制降低了業務對象替換的複雜性;
(3)容器提供了AOP技術,利用它很容易實現如權限攔截,運行期監控等功能;
(4)降低了組件之間的耦合性 ,實現了軟件各層之間的解耦; 
(5)容器提供單例模式支持;
(6)可以使用容器提供的衆多服務,如事務管理,消息服務等;
(7)容器提供了衆多的輔助類,能加快應用的開發;
(8)spring對於主流的應用框架提供了集成支持,如hibernate,JPA,Struts等 
(9)獨立於各種應用服務器 
(10)Spring的高度開放性,並不強制應用完全依賴於Spring,開發者可以自由選擇
spring的部分或全部。

248 Spring的AOP理解

AOP,一般稱爲面向方面(切面)編程,作爲面向對象的一種補充,用於解剖封裝好的
對象內部,找出其中對多個對象產生影響的公共行爲,並將其封裝爲一個可重用的模塊,
這個模塊被命名爲“切面”(Aspect),切面將那些與業務無關,卻被業務模塊共同調用
的邏輯提取並封裝起來,減少了系統中的重複代碼,降低了模塊間的耦合度,同時提高了
系統的可維護性。可用於權限認證、日誌、事務處理。
AOP實現的關鍵在於AOP框架自動創建的AOP代理,AOP代理主要分爲靜態代理和動態代
理。靜態代理的代表爲AspectJ;動態代理則以Spring AOP爲代表。
(1)AspectJ是靜態代理的增強,所謂靜態代理,就是AOP框架會在編譯階段生成AOP
代理類,因此也稱爲編譯時增強,他會在編譯階段將AspectJ織入到Java字節碼中,
運行的時候就是增強之後的AOP對象。
(2)Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改字節碼
,而是每次運行時在內存中臨時爲方法生成一個AOP對象,這個AOP對象包含了目標對
象的全部方法,並且在特定的切點做了增強處理,並回調原對象的方法。
Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理:
①JDK動態代理通過反射來接收被代理的類,並且要求被代理的類必須實現一個接口。
JDK動態代理的核心是InvocationHandler接口和Proxy類。生成的代理對象的方法
調用都會委託到InvocationHandler.invoke()方法,當我們調用代理類對象的方
法時,這個“調用”會轉送到invoke方法中,代理類對象作爲proxy參數傳入,參數
method標識了我們具體調用的是代理類的哪個方法,args爲這個方法的參數。
②如果目標類沒有實現接口,那麼Spring AOP會選擇使用CGLIB來動態代理目標類。
CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動
態的生成指定類的一個子類對象,並覆蓋其中特定方法,覆蓋方法時可以添加增強代
碼,從而實現AOP。CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記爲
final,那麼它是無法使用CGLIB做動態代理的。
(3)靜態代理與動態代理區別在於生成AOP代理對象的時機不同,相對來說AspectJ的
靜態代理方式具有更好的性能,但是AspectJ需要特定的編譯器進行處理,而Spring
AOP則無需特定的編譯器處理

249 Spring的IoC理解

(1)IOC就是控制反轉。就是對象的創建權反轉交給Spring,由容器控制程序之間
的依賴關係,作用是實現了程序的解耦合,而非傳統實現中,由程序代碼直接操控。
(依賴)控制權由應用代碼本身轉到了外部容器,由容器根據配置文件去創建實例並管
理各個實例之間的依賴關係,控制權的轉移,是所謂反轉,並且由容器動態的將某種
依賴關係注入到組件之中。BeanFactory 是Spring IoC容器的具體實現與核心接口,
提供了一個先進的配置機制,使得任何類型的對象的配置成爲可能,用來包裝和管理
各種bean。
(2)最直觀的表達就是,IOC讓對象的創建不用去new了,可以由spring自動生產,
這裏用的就是java的反射機制,通過反射在運行時動態的去創建、調用對象。spring
就是根據配置文件在運行時動態的去創建對象,並調用對象的方法的。
(3)Spring的IOC有三種注入方式 :第一是根據屬性注入,也叫set方法
注入;第二種是根據構造方法進行注入;第三種是根據註解進行注入。
詳細的說:
(4)IoC,控制反轉:將對象交給容器管理,你只需要在spring
配置文件總配置相應的bean,以及設置相關的屬性,讓spring容器
生成類的實例對象以及管理對象。在spring容器啓動的時候,spring會
把你在配置文件中配置的bean都初始化以及裝配好,然後在你需要調用的時候
,就把它已經初始化好的那些bean分配給你需要調用這些bean的類。就是將
對象的控制權反轉給spring容器管理。
(5)DI機制(Dependency Injection,依賴注入):可以說是IoC的其中一
個內容,在容器實例化對象的時候主動的將被調用者(或者說它的依賴對象)注
入給調用對象。比如對象A需要操作數據庫,以前我們總是要在A中自己編寫代碼
來獲得一個Connection對象,有了 spring我們就只需要告訴spring,A中需要
一個Connection,至於這個Connection怎麼構造,何時構造,A不需要知道。在
系統運行時,spring會在適當的時候製造一個Connection,然後像打針一樣,注
射到A當中,這樣就完成了對各個對象之間關係的控制。
IoC讓相互協作的組件保持鬆散的耦合,而AOP編程允許你把遍佈於應用各層的功
能分離出來形成可重用的功能組件

250 BeanFactory和ApplicationContext有什麼區別

BeanFactory和ApplicationContext是Spring的兩大核心接口,而其中
ApplicationContext是BeanFactory的子接口。它們都可以當做Spring的
容器,生成Bean實例的,並管理容器中的Bean。
(1)BeanFactory:是Spring裏面最底層的接口,提供了最簡單的容器的功
能,負責讀取bean配置文檔,管理bean的加載與實例化,維護bean之間的依
賴關係,負責bean的生命週期,但是無法支持spring的aop功能和web應用。
(2)ApplicationContext接口作爲BeanFactory的派生,因而具有BeanFactory
所有的功能。而且ApplicationContext還在功能上做了擴展,以一種更面向框架的
方式工作以及對上下文進行分層和實現繼承,相較於BeanFactorty,
ApplicationContext還提供了以下的功能: ①默認初始化所有的Singleton,
也可以通過配置取消預初始化。
②繼承MessageSource,因此支持國際化。
③資源訪問,比如訪問URL和文件。
④事件機制。
⑤同時加載多個配置文件。
⑥以聲明式方式啓動並創建Spring容器。
⑦載入多個(有繼承關係)上下文 ,使得每一個上下文都專注於一個特定的層
次,比如應用的web層。
(3)①BeanFactroy採用的是延遲加載形式來注入Bean的,即只有在使用到
某個Bean時(調用getBean()),纔對該Bean進行加載實例化,這樣,我們就
不能發現一些存在的Spring的配置問題。如果Bean的某一個屬性沒有注入,
BeanFacotry加載後,直至第一次使用調用getBean方法纔會拋出異常。
②而ApplicationContext則相反,它是在容器啓動時,一次性創建了所有
的Bean。這樣,在容器啓動時,我們就可以發現Spring中存在的配置錯誤,
這樣有利於檢查所依賴屬性是否注入。 ApplicationContext啓動後預載入
所有的單實例Bean,通過預載入單實例bean ,確保當你需要的時候,你就不用
等待,因爲它們已經創建好了。
③相對於基本的BeanFactory,ApplicationContext 唯一的不足是佔用內存
空間。當應用程序配置Bean較多時,程序啓動較慢。
(4)BeanFactory通常以編程的方式被創建,ApplicationContext還能以聲
明的方式創建,如使用ContextLoader。
(5)BeanFactory和ApplicationContext都支持BeanPostProcessor、
BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory
需要手動註冊,而ApplicationContext則是自動註冊

251 解釋Spring支持的幾種bean的作用域。

Spring容器中的bean可以分爲5個範圍:
(1)singleton:這種bean範圍是默認的,這種範圍確保不管接受到多少個
請求,每個容器中只有一個bean的實例,單例的模式由bean factory自身來維護。
(2)prototype:原形範圍與單例範圍相反,爲每一個bean請求提供一個實例。
(3)request:在請求bean範圍內會每一個來自客戶端的網絡請求創建一個實例,
在請求完成以後,bean會失效並被垃圾回收器回收。
(4)Session:與請求範圍類似,確保每個session中有一個bean的實例,
在session過期後,bean會隨之失效。(5)global-session:global-session
和Portlet應用相關。當你的應用部署在Portlet容器中工作時,它包含很多portlet。
如果你想要聲明讓所有的portlet共用全局的存儲變量的話,那麼這全局變量需要存儲
在global-session中。全局作用域與Servlet中的session作用域效果相同

252 請解釋Spring Bean的生命週期

首先說一下Servlet的生命週期:實例化,初始init,接收請求service,銷燬destroy;
 Spring上下文中的Bean生命週期也類似,如下:
(1)實例化一個Bean--也就是我們常說的new;
(2)按照Spring上下文對實例化的Bean進行配置--也就是IOC注入;
(3)如果這個Bean已經實現了BeanNameAware接口,會調用它實現的
setBeanName(String)方法,此處傳遞的就是Spring配置文件中Bean的id值;
(4)如果這個Bean已經實現了BeanFactoryAware接口,會調用它實現的
setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自
身(可以用這個方式來獲取其它Bean,只需在Spring配置文件中配置一個普通的Bean就可以);
(5)如果這個Bean已經實現了ApplicationContextAware接口,會調用
setApplicationContext(ApplicationContext)方法,傳入Spring上下
文(同樣這個方式也可以實現步驟4的內容,但比4更好,因爲ApplicationContext
是BeanFactory的子接口,有更多的實現方法);
(6)如果這個Bean關聯了BeanPostProcessor接口,將會調用
postProcessBeforeInitialization(Object obj, String s)方法,
BeanPostProcessor經常被用作是Bean內容的更改,並且由於這個是在Bean
初始化結束時調用那個的方法,也可以被應用於內存或緩存技術;
(7)如果Bean在Spring配置文件中配置了init-method屬性會自動調用其配
置的初始化方法。
(8)如果這個Bean關聯了BeanPostProcessor接口,將會調用
postProcessAfterInitialization(Object obj, String s)方法、;

253  Spring中bean的加載過程

(1)獲取配置文件資源;
(2)對獲取的xml資源進行一定的處理檢驗;
(3)處理包裝資源;
(4)解析處理包裝過後的資源;
(5)加載提取bean並註冊(添加到beanDefinitionMap中)

254  Spring框架中的單例Beans是線程安全的麼

Spring框架並沒有對單例bean進行任何多線程的封裝處理。關於單例bean的線程安
全和併發問題需要開發者自行去搞定。但實際上,大部分的Spring bean並沒有可變
的狀態(比如Serview類和DAO類),所以在某種程度上說Spring的單例bean是線程安
全的。如果你的bean有多種狀態的話(比如 View Model 對象),就需要自行保證線
程安全。最淺顯的解決辦法就是將多態bean的作用域由“singleton”變更爲“prototype”

255  Spring如何處理線程併發問題

Spring使用ThreadLocal解決線程安全問題。
我們知道在一般情況下,只有有狀態的Bean纔可以在多線程環境下共享,在Spring中,絕
大部分Bean都可以聲明爲singleton作用域。就是因爲Spring對一些
Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)
中非線程安全狀態採用ThreadLocal進行處理,讓它們也成爲線程安全的狀態,
因爲有狀態的Bean就可以在多線程中共享了。
ThreadLocal和線程同步機制都是爲了解決多線程中相同變量的訪問衝突問題。
(1)在同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。
這時該變量是多個線程共享的,使用同步機制要求程序慎密地分析什麼時候對變
量進行讀寫,什麼時候需要鎖定某個對象,什麼時候釋放對象鎖等繁雜的問題,
程序設計和編寫難度相對較大。
(2)而ThreadLocal則從另一個角度來解決多線程的併發訪問。ThreadLocal會爲
每一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問衝突。因爲
每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal
提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。
(3)概括起來說,對於多線程資源共享的問題,同步機制採用了“以時間換空間”的方式,
而ThreadLocal採用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,
而後者爲每一個線程都提供了一份變量,因此可以同時訪問而互不影響

256 請解釋Spring自動裝配模式的區別

在Spring框架中共有5種自動裝配:
(1)no:這是Spring框架的默認設置,在該設置下自動裝配是關閉的,開發者需要自行
在bean定義中用標籤明確的設置依賴關係。
(2)byName:該選項可以根據bean名稱設置依賴關係。當向一個bean中自動裝配一個
屬性時,容器將根據bean的名稱自動在配置文件中查詢一個匹配的bean。如果找到的話,
就裝配這個屬性,如果沒找到的話就報錯。
(3)byType:該選項可以根據bean類型設置依賴關係。當向一個bean中自動裝配一個屬性
時,容器將根據bean的類型自動在在配置文件中查詢一個匹配的bean。如果找到的話,就裝
配這個屬性,如果沒找到的話就報錯。
(4)constructor:構造器的自動裝配和byType模式類似,但是僅僅適用於與有構造器相同
參數的bean,如果在容器中沒有找到與構造器參數類型一致的bean,那麼將會拋出異常。
(5)autodetect:該模式自動探測使用構造器自動裝配或者byType自動裝配。首先,首先
會嘗試找合適的帶參數的構造器,如果找到的話就是用構造器自動裝配,如果在bean內部沒
有找到相應的構造器或者是無參構造器,容器就會自動選擇byTpe的自動裝配方式

257 Spring 控制器的加載過程

(1)Web容器創建;
(2)上下文創建,但未初始化;
(3)監聽器創建,並註冊到Context上;
(4)上下文初始化;
(5)通知到監聽者,Spring配置文件/@Configuration加載;
(6)Load-on-startup>0的ServletConfig創建,springMVC的DispatcherServlet此時創建

258 Spring 框架中都用到了哪些設計模式

(1)代理模式—在AOP和remoting中被用的比較多。(2)單例模式—在spring配置文件中
定義的bean默認爲單例模式。(3)工廠模式—BeanFactory用來創建對象的實例。(4)模
板方法—用來解決代碼重複的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)前端控制器—Spring提供了DispatcherServlet來對請求進行分發。(6)視圖幫
助(View Helper )—Spring提供了一系列的JSP標籤,高效宏來輔助將分散的代碼整合在
視圖裏。(7)依賴注入—貫穿於BeanFactory / ApplicationContext接口的核心理念

259 Spring事務的種類和各自的區別

spring支持編程式事務管理和聲明式事務管理兩種方式:
(1)編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。
對於編程式事務管理,spring推薦使用TransactionTemplate。
(2)聲明式事務管理建立在AOP之上的。其本質是對方法前後進行攔截,然後在目標方法開始之前創建
或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。聲明式事務最大的優點
就是不需要通過編程的方式管理事務,這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,只需
在配置文件中做相關的事務規則聲明(或通過基於@Transactional註解的方式),便可以將事務規
則應用到業務邏輯中。
(3)顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲
明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上註解就可以獲得完全的事
務支持。和編程式事務相比,聲明式事務唯一不足地方是,後者的最細粒度只能作用到方法級別
,無法做到像編程式事務那樣可以作用到代碼塊級別

260 Spring事務的實現方式和實現原理

(1)劃分處理單元——IOC:
由於spring解決的問題是對單個數據庫進行局部事務處理的,具體的實現首相用spring中的
IOC劃分了事務處理單元。並且將對事務的各種配置放到了ioc容器中(設置事務管理器,設
置事務的傳播特性及隔離機制)。
(2)AOP攔截需要進行事務處理的類:
Spring事務處理模塊是通過AOP功能來實現聲明式事務處理的,具體操作(比如事務實行的
配置和讀取,事務對象的抽象),用TransactionProxyFactoryBean接口來使用AOP功能,
生成proxy代理對象,通過TransactionInterceptor完成對代理方法的攔截,將事務處理的
功能編織到攔截的方法中。 
讀取ioc容器事務配置屬性,轉化爲spring事務處理需要的內部數據結構
(TransactionAttributeSourceAdvisor),轉化爲TransactionAttribute表示的數據對象。 
(3)對事物處理實現(事務的生成、提交、回滾、掛起):
spring委託給具體的事務處理器實現。實現了一個抽象和適配。適配的具
體事務處理器:DataSource數據源支持、hibernate數據源事務處理支持、
JDO數據源事務處理支持,JPA、JTA數據源事務處理支持。這些支持都是通過設
計PlatformTransactionManager、AbstractPlatforTransaction一系列事
務處理的支持。爲常用數據源支持提供了一系列的TransactionManager。
(4)結合:
PlatformTransactionManager實現了TransactionInterception接口,讓
其與TransactionProxyFactoryBean結合起來,形成一個Spring聲明式事務
處理的設計體系

261 解釋一下Spring AOP裏面的幾個名詞

(1)切面(Aspect):一個關注點的模塊化,這個關注點可能會橫切多個對象。事
務管理是J2EE應用中一個關於橫切關注點的很好的例子。 在Spring AOP中,切面可
以使用通用類(基於模式的風格) 或者在普通類中以 @Aspect 註解(@AspectJ風格)
來實現。
(2)連接點(Joinpoint):在程序執行過程中某個特定的點,比如某方法調用的
時候或者處理異常的時候。 在Spring AOP中,一個連接點 總是 代表一個方法的
執行。 通過聲明一個org.aspectj.lang.JoinPoint類型的參數可以使通知(Advice)
的主體部分獲得連接點信息。
(3)通知(Advice):在切面的某個特定的連接點(Joinpoint)上執行的動作。通
知有各種類型,其中包括“around”、“before”和“after”等通知。 通知的類型將在後
面部分進行討論。許多AOP框架,包括Spring,都是以攔截器做通知模型, 並維護一個
以連接點爲中心的攔截器鏈。
(4)切入點(Pointcut):匹配連接點(Joinpoint)的斷言。通知和一個切入點表達
式關聯,並在滿足這個切入點的連接點上運行(例如,當執行某個特定名稱的方法時)。 
切入點表達式如何和連接點匹配是AOP的核心:Spring缺省使用AspectJ切入點語法。
(5)引入(Introduction):(也被稱爲內部類型聲明(inter-type declaration))。
聲明額外的方法或者某個類型的字段。 Spring允許引入新的接口(以及一個對應的實現)
到任何被代理的對象。例如,你可以使用一個引入來使bean實現 IsModified 接口,以便
簡化緩存機制。
(6)目標對象(Target Object): 被一個或者多個切面(aspect)所通知(advise)
的對象。也有人把它叫做 被通知(advised) 對象。 既然Spring AOP是通過運行時代理
實現的,這個對象永遠是一個 被代理(proxied) 對象
(7)織入(Weaving):把切面(aspect)連接到其它的應用程序類型或者對象上,並創
建一個被通知(advised)的對象。 這些可以在編譯時(例如使用AspectJ編譯器),類加
載時和運行時完成。 Spring和其他純Java AOP框架一樣,在運行時完成織入。
切入點(pointcut)和連接點(join point)匹配的概念是AOP的關鍵,這使得AOP不同於
其它僅僅提供攔截功能的舊技術。 切入點使得定位通知(advice)可獨立於OO層次。 
例如,一個提供聲明式事務管理的around通知可以被應用到一組橫跨多個對象中的方法上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章