1.HashMap和HashTable的區(qū)別
- [x] HashMap去掉了contains方法
- [x] HashTable是同步的(線程安全)
- [x] HashMap允許空鍵值
- [x] HashMap執(zhí)行快速失敗機(jī)制
- [ ]
Fast-fail
機(jī)制:在使用迭代器的過(guò)程中有其它線程修改了集合對(duì)象結(jié)構(gòu)或元素?cái)?shù)量,都將拋出ConcurrentModifiedException
- HashMap幾乎可以等價(jià)于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受為null的鍵值(key)和值(value),而Hashtable則不行)。
- Hashtable和HashMap有幾個(gè)主要的不同:線程安全以及速度。僅在你需要完全的線程安全的時(shí)候使用Hashtable,而如果你使用Java 5或以上的話,請(qǐng)使用ConcurrentHashMap吧。
2.java的線程安全類
Vector、Stack、HashTable、ConcurrentHashMap、Properties
3.java集合框架
- Collection - List
- 有順序以線性方式存儲(chǔ),可以存放重復(fù)對(duì)象
- Collection - List - ArrayList
- 數(shù)組方式存儲(chǔ)數(shù)據(jù) 索引數(shù)據(jù)快插入數(shù)據(jù)慢 線程不安全
- Collection - List - LinkedList
- 雙向鏈表實(shí)現(xiàn)存儲(chǔ) 索引數(shù)據(jù)慢插入數(shù)度較快 線程不安全(比安全性能好)
- Collection - List - Vector
- 數(shù)組方式存儲(chǔ)數(shù)據(jù) 索引數(shù)據(jù)快插入數(shù)據(jù)慢 線程安全
- Collection - List - Vector - Stack
- 繼承自Vector,實(shí)現(xiàn)一個(gè)后進(jìn)先出的堆棧
- Collection - Set
- 無(wú)順序,不包含重復(fù)的元素
- Collection - Set - HashSet
- 為快速查找設(shè)計(jì)的Set。存入HashSet的對(duì)象必須定義hashCode()。
- Collection - Set - TreeSet
- 保存次序的Set, 底層為樹(shù)結(jié)構(gòu)。使用它可以從Set中提取有序的序列。
- Collection - Set - LinkedHashSet
- 具有HashSet的查詢速度,且內(nèi)部使用鏈表維護(hù)元素的順序(插入的次序)。于是在使用迭代器遍歷Set時(shí),結(jié)果會(huì)按元素插入的次序顯示。
- Map
- 鍵必須是唯一
- Map - HashMap
- HashMap:基于散列表的實(shí)現(xiàn) 允許空鍵空值 線程不安全 (與Hashtable基本一致)
- Map - TreeMap
- TreeMap: 基于紅黑樹(shù)數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn) 不允許空鍵空值 線程不安全
- Map - HashTable
- Hashtable:基于散列表的實(shí)現(xiàn) 允許空鍵空值 線程安全
- Map - WeakHashMap
- 改進(jìn)的HashMap,它對(duì)key實(shí)行“弱引用”,如果一個(gè)key不再被外部所引用,那么該key可以被GC回收。
- Map - LinkedHashMap
- 具有HashMap的所有特性。
- 內(nèi)部對(duì)數(shù)據(jù)的存儲(chǔ)也是數(shù)組加鏈表的形式。
- 多了一個(gè)雙向鏈表來(lái)維護(hù)內(nèi)部數(shù)據(jù)的順序關(guān)系。
- SparseArray
- 采用了二分法方式存儲(chǔ)數(shù)據(jù)(安卓的一個(gè)集合類)
- key必須為int類型,這中情況下的HashMap可以用SparseArray代替
- 避免了HashMap自動(dòng)裝箱得到內(nèi)存消耗
- ArrayMap
-
ArrayMap
實(shí)現(xiàn)了implements Map<K, V>
接口,所以它也是一個(gè)關(guān)聯(lián)數(shù)組、哈希表。 - 存儲(chǔ)以
key->value
結(jié)構(gòu)形式的數(shù)據(jù)。 - 它也是線程不安全的,允許key為null,value為null。
- 內(nèi)部實(shí)現(xiàn)是基于兩個(gè)數(shù)組。
- 一個(gè)
int[]
數(shù)組,用于保存每個(gè)item的hashCode
. - 一個(gè)
Object[]
數(shù)組,保存key/value
鍵值對(duì)。容量是上一個(gè)數(shù)組的兩倍。
- 一個(gè)
- 使用二分查找法得到相應(yīng)的index
-
在除需要排序時(shí)使用TreeSet,TreeMap外,都應(yīng)使用HashSet,HashMap,因?yàn)樗麄兊男矢摺?/p>
3.1 ArrayList
的構(gòu)造函數(shù)有三個(gè)
- 無(wú)參構(gòu)造 容量為10
- ArrayList(Collections<?extends E> c)構(gòu)造包含指定collection的元素的列表
- ArrayList(int initialCapacity) 指定初始容量
3.2 Iterator(迭代器)
支持從源集合安全地刪除對(duì)象,防止并發(fā)修改異常(ConcurrentModifiedException)
4.Java垃圾回收機(jī)制
4.1 調(diào)用system.gc() Runtime.getRuntime.gc()
4.2 垃圾回收:釋放那些不再持有任何引用的對(duì)象的內(nèi)存
4.3 怎樣判斷是否需要收集:
- 引用計(jì)數(shù)法:對(duì)象沒(méi)有任何引用與之關(guān)聯(lián)(無(wú)法解決循環(huán)引用)
- 對(duì)象引用遍歷法:對(duì)象引用遍歷從一組對(duì)象開(kāi)始,沿著對(duì)象圖的每條鏈接,遞歸確定可以到達(dá)的對(duì)象,如果某對(duì)象不能從這些根對(duì)象的一個(gè)(至少一個(gè))到達(dá),則將它作為垃圾收集。
4.4 垃圾回收方法
- 標(biāo)記清除法(Mark-Sweeping):易產(chǎn)生內(nèi)存碎片
- 復(fù)制回收法(Copying):為了解決Mark-Sweep法而提出,內(nèi)存空間減至一半
- 標(biāo)記壓縮法(Mark-Compact):為了解決Copying法的缺陷,標(biāo)記后移動(dòng)到一端再清楚
- 分代回收法(GenerationalCollection):新生代對(duì)象存活周期短,需要大量回收對(duì)象,需要復(fù)制的少,執(zhí)行copying算法;老年代對(duì)象存活周期相對(duì)長(zhǎng),回收少量對(duì)象,執(zhí)行mark-compact算法.
新生代劃分:較大的eden區(qū) 和 2個(gè)survivor區(qū)
4.5 內(nèi)存分配
- 新生代 |Eden Space|From Space|To Space|
- 對(duì)象主要分配在新生代的EdenSpace和FromSpace
- 如果EdenSapce和FromSpace空間不足,則發(fā)起一次GC
- 若進(jìn)行GC后,EdenSpace和FromSpace能夠容納該對(duì)象,就放在Eden和FromSpace。在GC過(guò)程中會(huì)將EdenSpace和FromSpace存活的對(duì)象移動(dòng)到ToSpace,然后清理Eden和From。若在清理過(guò)程中,ToSpace無(wú)法足夠容納該對(duì)象,則將該對(duì)象移入老年代中。在進(jìn)行GC后,Eden和From為空,MinorGC完成。From和To標(biāo)記互換。To區(qū)(邏輯上)始終為空。
- 新生代的回收成為MinorGC,對(duì)老年代的回收成為MajorGC又名FullGC
其他
- 優(yōu)先在Eden上分配
- 大對(duì)象直接進(jìn)入老年代
- 長(zhǎng)期存活的對(duì)象進(jìn)入老年代
- 動(dòng)態(tài)對(duì)象年齡判定 suvivor區(qū)同年齡對(duì)象總和大于suvivor區(qū)空間的一半,MinorGC時(shí)復(fù)制至老年代
- 空間分配擔(dān)保 新生代放不下借用老年代,虛擬機(jī)檢測(cè)GC租借的老年代內(nèi)存是否大于剩余的老年代內(nèi)存。若大于,MinorGC變?yōu)橐淮蜦ullGC。若小于,查看虛擬機(jī)是否允許擔(dān)保失敗,若允許則執(zhí)行一次MinorGC,否則也要變?yōu)橐淮蜦ullGC
5.一些重要的關(guān)鍵字
volatile
Java 語(yǔ)言提供了一種稍弱的同步機(jī)制,即volatile
變量.用來(lái)確保將變量的更新操作通知到其他線程,保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新。 當(dāng)把變量聲明為volatile類型后,編譯器與運(yùn)行時(shí)都會(huì)注意到這個(gè)變量是共享的。volatile
修飾變量,每次被線程訪問(wèn)時(shí)強(qiáng)迫其從主內(nèi)存重讀該值,修改后再寫回共享內(nèi)存。保證讀取的可見(jiàn)性,對(duì)其他線程立即可見(jiàn)。由于不保證原子性,也就不能保證線程安全。由于及時(shí)更新,很可能導(dǎo)致另一線程訪問(wèn)最新變量值,無(wú)法跳出循環(huán)的情況。同時(shí),volatile屏蔽了VM中必要的代碼優(yōu)化,效率上較低。另一個(gè)優(yōu)點(diǎn):禁止指令重排序final
final
修飾的變量是常量,必須進(jìn)行初始化,可以顯示初始化,也可以通過(guò)構(gòu)造進(jìn)行初始化,如果不初始化編譯會(huì)報(bào)錯(cuò)。
6.多線程 & 并發(fā) & 同步 & 鎖
6.1 線程的run方法和start方法
-
start方法
用start方法來(lái)啟動(dòng)線程,是真正實(shí)現(xiàn)了多線程。調(diào)用thread類的start方法來(lái)啟動(dòng)一個(gè)線程,此時(shí)線程處于就緒狀態(tài),一旦得到cpu時(shí)間片,就開(kāi)始執(zhí)行run方法。注:此時(shí)無(wú)需等待run方法執(zhí)行完畢,即可執(zhí)行下面的代碼,所以run方法并沒(méi)有實(shí)現(xiàn)多線程。 -
run方法
只是thread類的一個(gè)普通方法,若直接調(diào)用程序中依然只有主線程這一個(gè)線程,還要順序執(zhí)行,依然要等待run方法體執(zhí)行完畢才可執(zhí)行下面的代碼。
6.2 ReadWriteLock(讀寫鎖)
寫寫互斥 讀寫互斥 讀讀并發(fā)
在讀多寫少的情況下可以提高效率
6.3 resume(繼續(xù)掛起的線程)和suspend(掛起線程)一起用
6.4 wait與notify、notifyall一起用
6.5 sleep與wait的異同點(diǎn)
- sleep是Thread類的靜態(tài)方法,wait來(lái)自object類
- sleep不釋放鎖,wait釋放鎖
- wait,notify,notifyall必須在同步代碼塊中使用,sleep可以在任何地方使用
- 都可以拋出InterruptedException
6.6 讓一個(gè)線程停止執(zhí)行
異常 - 停止執(zhí)行
休眠 - 停止執(zhí)行
阻塞 - 停止執(zhí)行
6.7 ThreadLocal相關(guān)
-
ThreadLocal解決了變量并發(fā)訪問(wèn)的沖突問(wèn)題
- 當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,每個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本,是線程隔離的。線程隔離的秘密在于ThreadLocalMap類(ThreadLocal的靜態(tài)內(nèi)部類)
-
原理
- 這個(gè)類之所以能夠存儲(chǔ)每個(gè)thread的信息,是因?yàn)樗膬?nèi)部有一個(gè)Values內(nèi)部類,而Values中有一個(gè)Object組。
- Objec數(shù)組是以一種近似于map的形式來(lái)存儲(chǔ)數(shù)據(jù)的,其中偶數(shù)位存ThreadLocal的弱引用,它的下一位存值。
- 在尋址的時(shí)候,Values采用一種很神奇的方式——斐波拉契散列尋址Values里面的getAfterMiss()方式讓人覺(jué)得很奇怪
-
與synchronized同步機(jī)制的比較
- 首先,它們都是為了解決多線程中相同變量訪問(wèn)沖突問(wèn)題。不過(guò),在同步機(jī)制中,要通過(guò)對(duì)象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問(wèn)該變量。該變量是線程共享的,使用同步機(jī)制要求程序縝密地分析什么時(shí)候?qū)υ撟兞孔x寫,什么時(shí)候需要鎖定某個(gè)對(duì)象,什么時(shí)候釋放對(duì)象鎖等復(fù)雜的問(wèn)題,程序設(shè)計(jì)編寫難度較大,是一種“以時(shí)間換空間”的方式。
而ThreadLocal采用了以“以空間換時(shí)間”的方式。
- 首先,它們都是為了解決多線程中相同變量訪問(wèn)沖突問(wèn)題。不過(guò),在同步機(jī)制中,要通過(guò)對(duì)象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問(wèn)該變量。該變量是線程共享的,使用同步機(jī)制要求程序縝密地分析什么時(shí)候?qū)υ撟兞孔x寫,什么時(shí)候需要鎖定某個(gè)對(duì)象,什么時(shí)候釋放對(duì)象鎖等復(fù)雜的問(wèn)題,程序設(shè)計(jì)編寫難度較大,是一種“以時(shí)間換空間”的方式。
7.接口與抽象類
- 一個(gè)子類只能繼承一個(gè)抽象類,但能實(shí)現(xiàn)多個(gè)接口
- 抽象類可以有構(gòu)造方法,接口沒(méi)有構(gòu)造方法
- 抽象類可以有普通成員變量,接口沒(méi)有普通成員變量
- 抽象類和接口都可有靜態(tài)成員變量,抽象類中靜態(tài)成員變量訪問(wèn)類型任意,接口只能public static final(默認(rèn))
- 抽象類可以沒(méi)有抽象方法,抽象類可以有普通方法,接口中都是抽象方法
- 抽象類可以有靜態(tài)方法,接口不能有靜態(tài)方法
- 抽象類中的方法可以是public、protected和默認(rèn);接口方法只有public
8.Statement接口
8.1
- Statement是最基本的用法,不傳參,采用字符串拼接,存在注入漏洞
- PreparedStatement傳入?yún)?shù)化的sql語(yǔ)句,同時(shí)檢查合法性,效率高,可以重用,防止sql注入
- CallableStatement接口擴(kuò)展PreparedStatement,用來(lái)調(diào)用存儲(chǔ)過(guò)程
- public interface CallableStatement extends PreparedStatement
- public interface PreparedStatement extends Statement
- BatchedStatement用于批量操作數(shù)據(jù)庫(kù),BatchedStatement不是標(biāo)準(zhǔn)的Statement類
8.2 Statement與PrepareStatement的區(qū)別
- 創(chuàng)建時(shí)的區(qū)別
Statement statement = conn.createStatement();
PreparedStatement preStatement = conn.prepareStatement(sql);
- 執(zhí)行的時(shí)候
ResultSet rSet = statement.executeQuery(sql);
ResultSet pSet = preStatement.executeQuery();
由上可以看出,PreparedStatement有預(yù)編譯的過(guò)程,已經(jīng)綁定sql,之后無(wú)論執(zhí)行多少遍,都不會(huì)再去進(jìn)行編譯,
而 statement 不同,如果執(zhí)行多遍,則相應(yīng)的就要編譯多少遍sql,所以從這點(diǎn)看,preStatement 的效率會(huì)比 Statement要高一些
- 安全性
preStatement是預(yù)編譯的,所以可以有效的防止SQL注入等問(wèn)題
- 代碼的可讀性和可維護(hù)性
PreparedStatement更勝一籌
9.抽象類和最終類
抽象類可以沒(méi)有抽象方法,最終類可以,沒(méi)有最終方法
最終類不能被繼承,最終方法不能被重寫(可以重載)
10.異常
10.1 throw、throws、try...catch、finally
- throws用在方法上,方法內(nèi)部通過(guò)throw拋出異常
- try用于檢測(cè)包住的語(yǔ)句塊,若有異常,拋出并執(zhí)行catch子句
- catch捕獲try塊中拋出的異常并處理
10.2 關(guān)于finally
- finally不管有沒(méi)有異常都要處理
- finally{}比return先執(zhí)行,多個(gè)return執(zhí)行一個(gè)后就不在執(zhí)行
- 不管有木有異常拋出,finally在return返回前執(zhí)行
10.3 受檢查異常和運(yùn)行時(shí)異常
粉紅色的是受檢查的異常(checked exceptions),其必須被try...catch語(yǔ)句塊所捕獲,或者在方法簽名里通過(guò)throws子句聲明。受檢查的異常必須在編譯時(shí)被捕捉處理,命名為Checked Exception是因?yàn)镴ava編譯器要進(jìn)行檢查,Java虛擬機(jī)也要進(jìn)行檢查,以確保這個(gè)規(guī)則得到遵守。
綠色的異常是運(yùn)行時(shí)異常(runtime exceptions),需要程序員自己分析代碼決定是否捕獲和處理,比如空指針,被0除...
而聲明為Error的,則屬于嚴(yán)重錯(cuò)誤,如系統(tǒng)崩潰、虛擬機(jī)錯(cuò)誤、動(dòng)態(tài)鏈接失敗等,這些錯(cuò)誤無(wú)法恢復(fù)或者不可能捕捉,將導(dǎo)致應(yīng)用程序中斷,Error不需要捕捉。
11.this & super
11.1 super出現(xiàn)在父類的子類中。有三種存在方式
- super.xxx(xxx為變量名或?qū)ο竺?意思是獲取父類中xxx的變量或引用
- super.xxx(); (xxx為方法名)意思是直接訪問(wèn)并調(diào)用父類中的方法
- super() 調(diào)用父類構(gòu)造
- super只能指代其直接父類
11.2 this() & super()在構(gòu)造方法中的區(qū)別
- 調(diào)用super()必須寫在子類構(gòu)造方法的第一行,否則編譯不通過(guò)
- super從子類調(diào)用父類構(gòu)造,this在同一類中調(diào)用其他構(gòu)造
- 均需要放在第一行
- 盡管可以用this調(diào)用一個(gè)構(gòu)造器,卻不能調(diào)用2個(gè)
- this和super不能出現(xiàn)在同一個(gè)構(gòu)造器中,否則編譯不通過(guò)
- this()、super()都指的對(duì)象,不可以在static環(huán)境中使用
- 本質(zhì)this指向本對(duì)象的指針。super是一個(gè)關(guān)鍵字
12.修飾符一覽
修飾符 類內(nèi)部 同一個(gè)包 子類 任何地方
private yes
default yes yes
protected yes yes yes
public yes yes yes yes
13.構(gòu)造內(nèi)部類對(duì)象
public class Enclosingone {
public class Insideone {}
public static class Insideone{}
}
public class Test {
public static void main(String[] args) {
Enclosingone.Insideone obj1 = new Enclosingone().new Insideone();
Enclosingone.Insideone obj2 = new Enclosingone.Insideone();
}
}
14.序列化
聲明為static和transient類型的數(shù)據(jù)不能被序列化,序列化的筆記參見(jiàn)[Java-note-序列化.md][5]
15.Java的方法區(qū)
與堆一樣,是線程共享的區(qū)域。方法區(qū)中存儲(chǔ):被虛擬機(jī)加載的類信息,常量,靜態(tài)變量,編譯器編譯后的代碼等數(shù)據(jù)。這個(gè)區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的對(duì)象的回收和對(duì)類型的卸載。
16.正則表達(dá)式
次數(shù)符號(hào)
* 0或多次
+ 1或多次
?0或1次
{n} 恰n次
{n,m} 從n到m次
其他符號(hào)
符號(hào) 等價(jià)形式
\d [0-9]
\D [^0-9]
\w [a-zA-Z_0-9]
\W [^a-zA-Z_0-9]
\s [\t\n\r\f]
\S [^\t\n\r\f]
. 任何字符
邊界匹配器
行開(kāi)頭 ^
行結(jié)尾 $
單詞邊界 \b
貪婪模式:最大長(zhǎng)度匹配 非貪婪模式:匹配到結(jié)果就好,最短匹配
環(huán)視
字符 描述 匹配對(duì)象
. 單個(gè)任意字符
[...] 字符組 列出的任意字符
[^...] 未列出的任意字符
^ caret 行的起始位置
$ dollar 行的結(jié)束位置
\< 單詞的起始位置
\> 單詞的結(jié)束位置
\b 單詞邊界
\B 非單詞邊界
(?=Expression) 順序肯定環(huán)視 成功,如果右邊能夠匹配
(?!Expression) 順序否定環(huán)視 成功,如果右邊不能夠匹配
(?<=Expression) 逆序肯定環(huán)視 成功,如果左邊能夠匹配
(?<!Expression) 逆序否定環(huán)視 成功,如果左邊不能夠匹配
舉例:北京市(海定區(qū))(朝陽(yáng)區(qū))(西城區(qū))
Regex: .*(?=\()
模式和匹配器的典型調(diào)用次序
- 把正則表達(dá)式編譯到模式中
Pattern p = Pattern.compile("a*b"); - 創(chuàng)建給定輸入與此模式的匹配器
Matcher m = p.matcher("aaab"); - 嘗試將整個(gè)區(qū)域與此模式匹配
boolean b = m.matches();
17.Servlet & JSP & Tomcat
17.1 Servlet繼承實(shí)現(xiàn)結(jié)構(gòu)
Servlet (接口) --> init|service|destroy方法
GenericServlet(抽象類) --> 與協(xié)議無(wú)關(guān)的Servlet
HttpServlet(抽象類) --> 實(shí)現(xiàn)了http協(xié)議
自定義Servlet --> 重寫doGet/doPost
17.2 編寫Servlet的步驟
- 繼承HttpServlet
- 重寫doGet/doPost方法
- 在web.xml中注冊(cè)servlet
17.3 Servlet生命周期
-
init
:僅執(zhí)行一次,負(fù)責(zé)裝載servlet時(shí)初始化servlet對(duì)象 -
service
:核心方法,一般get/post兩種方式 -
destroy
:停止并卸載servlet,釋放資源
17.4 過(guò)程
- 客戶端request請(qǐng)求 -> 服務(wù)器檢查Servlet實(shí)例是否存在 -> 若存在調(diào)用相應(yīng)service方法
- 客戶端request請(qǐng)求 -> 服務(wù)器檢查Servlet實(shí)例是否存在 -> 若不存在裝載Servlet類并創(chuàng)建實(shí)例 -> 調(diào)用init初始化 -> 調(diào)用service
- 加載和實(shí)例化、初始化、處理請(qǐng)求、服務(wù)結(jié)束
17.5 doPost方法要拋出的異常:ServletExcception、IOException
17.6 Servlet容器裝載Servlet
- web.xml中配置load-on-startup啟動(dòng)時(shí)裝載
- 客戶首次向Servlet發(fā)送請(qǐng)求
- Servlet類文件被更新后,重新裝載Servlet
17.7 HttpServlet容器響應(yīng)web客戶請(qǐng)求流程
- Web客戶向servlet容器發(fā)出http請(qǐng)求
- servlet容器解析Web客戶的http請(qǐng)求
- servlet容器創(chuàng)建一個(gè)HttpRequest對(duì)象,封裝http請(qǐng)求信息
- servlet容器創(chuàng)建一個(gè)HttpResponse對(duì)象
- servlet容器調(diào)用HttpServlet的service方法,把HttpRequest和HttpResponse對(duì)象作為service方法的參數(shù)傳給HttpServlet對(duì)象
- HttpServlet調(diào)用httprequest的有關(guān)方法,獲取http請(qǐng)求信息
- httpservlet調(diào)用httpresponse的有關(guān)方法,生成響應(yīng)數(shù)據(jù)
- Servlet容器把HttpServlet的響應(yīng)結(jié)果傳給web客戶
17.8 HttpServletRequest完成的功能
- request.getCookie()
- request.getHeader(String s)
- request.getContextPath()
17.9 HttpServletResponse完成的功能
- 設(shè)http頭
- 設(shè)置Cookie
- 輸出返回?cái)?shù)據(jù)
17.10 session
HttpSession session = request.getSession(boolean create)
返回當(dāng)前請(qǐng)求的會(huì)話
17.11 JSP的前身就是Servlet
17.12 Tomcat容器的等級(jí)
Tomcat - Container - Engine - Host - Servlet - 多個(gè)Context(一個(gè)Context對(duì)應(yīng)一個(gè)web工程)-Wrapper
17.13 Servlet與JSP九大內(nèi)置對(duì)象的關(guān)系
JSP對(duì)象 怎樣獲得
1. out -> response.getWriter
2. request -> Service方法中的req參數(shù)
3. response -> Service方法中的resp參數(shù)
4. session -> request.getSession
5. application -> getServletContext
6. exception -> Throwable
7. page -> this
8. pageContext -> PageContext
9. Config -> getServletConfig
exception是JSP九大內(nèi)置對(duì)象之一,其實(shí)例代表其他頁(yè)面的異常和錯(cuò)誤。只有當(dāng)頁(yè)面是錯(cuò)誤處理頁(yè)面時(shí),即isErroePage為 true時(shí),該對(duì)象才可以使用。
18.struts
- struts可進(jìn)行文件上傳
- struts基于MVC模式
- struts讓流程結(jié)構(gòu)更清晰
- struts有許多action類,會(huì)增加類文件數(shù)目
19.Hibernate的7大鼓勵(lì)措施
- 盡量使用many-to-one,避免使用單項(xiàng)one-to-many
- 靈活使用單項(xiàng)one-to-many
- 不用一對(duì)一,使用多對(duì)一代替一對(duì)一
- 配置對(duì)象緩存,不使用集合對(duì)象
- 一對(duì)多使用bag,多對(duì)一使用set
- 繼承使用顯示多態(tài)
- 消除大表,使用二級(jí)緩存
20.JVM
20.1 JVM內(nèi)存配置參數(shù)
- -Xmx:最大堆大小
- -Xms:初始堆大小(最小內(nèi)存值)
- -Xmn:年輕代大小
- -XXSurvivorRatio:3 意思是Eden:Survivor=3:2
- -Xss棧容量
- -XX:+PrintGC 輸出GC日志
- -XX:+PrintGCDetails 輸出GC的詳細(xì)日志
20.2 JVM內(nèi)存結(jié)構(gòu)
- 堆:Eden、Survivor、old 線程共享
- 方法區(qū)(非堆):持久代,代碼緩存,線程共享
- JVM棧:中間結(jié)果,局部變量,線程隔離
- 本地棧:本地方法(非Java代碼)
- 程序計(jì)數(shù)器 :線程私有,每個(gè)線程都有自己獨(dú)立的程序計(jì)數(shù)器,用來(lái)指示下一條指令的地址
- 注:持久代Java8消失,取代的稱為元空間(本地堆內(nèi)存的一部分)
21.面向?qū)ο蟮奈宕蠡驹瓌t(solid)
S單一職責(zé)
SRP
:Single-Responsibility Principle
一個(gè)類,最好只做一件事,只有一個(gè)引起它的變化。單一職責(zé)原則可以看做是低耦合,高內(nèi)聚在面向?qū)ο笤瓌t的引申,將職責(zé)定義為引起變化的原因,以提高內(nèi)聚性減少引起變化的原因。O開(kāi)放封閉原則
OCP
:Open-Closed Principle
軟件實(shí)體應(yīng)該是可擴(kuò)展的,而不是可修改的。對(duì)擴(kuò)展開(kāi)放,對(duì)修改封閉L里氏替換原則
LSP
:Liskov-Substitution Principle
子類必須能夠替換其基類。這一思想表現(xiàn)為對(duì)繼承機(jī)制的約束規(guī)范,只有子類能夠替換其基類時(shí),才能夠保證系統(tǒng)在運(yùn)行期內(nèi)識(shí)別子類,這是保證繼承復(fù)用的基礎(chǔ)。I接口隔離原則
ISP
:Interface-Segregation Principle
使用多個(gè)小的接口,而不是一個(gè)大的總接口D依賴倒置原則
DIP
:Dependency-Inversion Principle
依賴于抽象。具體而言就是高層模塊不依賴于底層模塊,二者共同依賴于抽象。抽象不依賴于具體,具體依賴于抽象。
面向?qū)ο笤O(shè)計(jì)原則
- 封裝變化
- 少用繼承 多用組合
- 針對(duì)接口編程 不針對(duì)實(shí)現(xiàn)編程
- 為交互對(duì)象之間的松耦合設(shè)計(jì)而努力
- 類應(yīng)該對(duì)擴(kuò)展開(kāi)發(fā) 對(duì)修改封閉(開(kāi)閉OCP原則)
- 依賴抽象,不要依賴于具體類(依賴倒置DIP原則)
- 密友原則:只和朋友交談(最少知識(shí)原則)
說(shuō)明:將方法調(diào)用保持在界限內(nèi),只調(diào)用屬于以下范圍的方法:
該對(duì)象本身(本地方法)對(duì)象的組件 被當(dāng)作方法參數(shù)傳進(jìn)來(lái)的對(duì)象 此方法創(chuàng)建或?qū)嵗娜魏螌?duì)象
- 別找我(調(diào)用我) 我會(huì)找你(調(diào)用你)(好萊塢原則)
- 一個(gè)類只有一個(gè)引起它變化的原因(單一職責(zé)SRP原則)
22.null可以被強(qiáng)制轉(zhuǎn)型為任意類型的對(duì)象。
23.代碼執(zhí)行次序
- 多個(gè)靜態(tài)成員變量,靜態(tài)代碼塊按順序執(zhí)行
- 單個(gè)類中: 靜態(tài)代碼 -> main方法 -> 構(gòu)造塊 -> 構(gòu)造方法
- 構(gòu)造塊在每一次創(chuàng)建對(duì)象時(shí)執(zhí)行
- 涉及父類和子類的初始化過(guò)程
a.初始化父類中的靜態(tài)成員變量和靜態(tài)代碼塊
b.初始化子類中的靜態(tài)成員變量和靜態(tài)代碼塊
c.初始化父類的普通成員變量和構(gòu)造代碼塊(按次序),再執(zhí)行父類的構(gòu)造方法(注意父類構(gòu)造方法中的子類方法覆蓋)
d.初始化子類的普通成員變量和構(gòu)造代碼塊(按次序),再執(zhí)行子類的構(gòu)造方法
24.紅黑樹(shù)
二叉搜索樹(shù):(Binary Search Tree又名:二叉查找樹(shù),二叉排序樹(shù))它或者是一棵空樹(shù),或者是具有下列性質(zhì)的二叉樹(shù): 若它的左子樹(shù)不空,則左子樹(shù)上所有結(jié)點(diǎn)的值均小于它的根結(jié)點(diǎn)的值;若它的右子樹(shù)不空,則右子樹(shù)上所有結(jié)點(diǎn)的值均大于它的根結(jié)點(diǎn)的值;它的左、右子樹(shù)也分別為二叉搜索樹(shù)。
紅黑樹(shù)的定義:滿足以下五個(gè)性質(zhì)的二叉搜索樹(shù)
- 每個(gè)結(jié)點(diǎn)或是紅色的或是黑色的
- 根結(jié)點(diǎn)是黑色的
- 每個(gè)葉結(jié)點(diǎn)是黑色的
- 如果一個(gè)結(jié)點(diǎn)是紅色的,則它的兩個(gè)子結(jié)點(diǎn)是黑色的
- 對(duì)于每個(gè)結(jié)點(diǎn),從該結(jié)點(diǎn)到其后代葉結(jié)點(diǎn)的簡(jiǎn)單路徑上,均包含相同數(shù)目的黑色結(jié)點(diǎn)
黑高
從某個(gè)結(jié)點(diǎn)x出發(fā)(不含x)到達(dá)一個(gè)葉結(jié)點(diǎn)的任意一條簡(jiǎn)單路徑上的黑色結(jié)點(diǎn)個(gè)數(shù)稱為該結(jié)點(diǎn)的黑高。
紅黑樹(shù)的黑高為其根結(jié)點(diǎn)的黑高。
其他
- 一個(gè)具有n個(gè)內(nèi)部結(jié)點(diǎn)的紅黑樹(shù)的高度h<=2lg(n+1)
- 結(jié)點(diǎn)的屬性(五元組):color key left right p
- 動(dòng)態(tài)集合操作最壞時(shí)間復(fù)雜度為O(lgn)
25.排序
穩(wěn)定排序:插入排序、冒泡排序、歸并排序、基數(shù)排序
插入排序[穩(wěn)定]
適用于小數(shù)組,數(shù)組已排好序或接近于排好序速度將會(huì)非常快
復(fù)雜度:O(n^2) - O(n) - O(n^2) - O(1)[平均 - 最好 - 最壞 - 空間復(fù)雜度]歸并排序[穩(wěn)定]
采用分治法
復(fù)雜度:O(nlogn) - O(nlgn) - O(nlgn) - O(n)[平均 - 最好 - 最壞 - 空間復(fù)雜度]冒泡排序[穩(wěn)定]
復(fù)雜度:O(n^2) - O(n) - O(n^2) - O(1)[平均 - 最好 - 最壞 - 空間復(fù)雜度]基數(shù)排序 分配+收集[穩(wěn)定]
復(fù)雜度: O(d(n+r)) r為基數(shù)d為位數(shù) 空間復(fù)雜度O(n+r)樹(shù)排序
應(yīng)用:TreeSet的add方法、TreeMap的put方法
不支持相同元素,沒(méi)有穩(wěn)定性問(wèn)題
復(fù)雜度:平均最差O(nlogn)堆排序(就地排序)
復(fù)雜度:O(nlogn) - O(nlgn) - O(nlgn) - O(1)[平均 - 最好 - 最壞 - 空間復(fù)雜度]快速排序
復(fù)雜度:O(nlgn) - O(nlgn) - O(n^2) - O(1)[平均 - 最好 - 最壞 - 空間復(fù)雜度]
棧空間0(lgn) - O(n)選擇排序
復(fù)雜度:O(n^2) - O(n^2) - O(n^2) - O(1)[平均 - 最好 - 最壞 - 空間復(fù)雜度]希爾排序
復(fù)雜度 小于O(n^2) 平均 O(nlgn) 最差O(n^s)[1<s<2] 空間O(1)
26.查找與散列
26.1 散列函數(shù)設(shè)計(jì)
- 直接定址法:
f(key) = a*key+b
簡(jiǎn)單、均勻,不易產(chǎn)生沖突。但需事先知道關(guān)鍵字的分布情況,適合查找表較小且連續(xù)的情況,故現(xiàn)實(shí)中并不常用
除留余數(shù)法:
f(key) = key mod p (p<=m) p取小于表長(zhǎng)的最大質(zhì)數(shù) m為表長(zhǎng)
DJBX33A算法(time33哈希算法
hash = hash*33+(unsigned int)str[i];
平方取中法 折疊法 更多....
26.2 沖突處理
閉散列(開(kāi)放地址方法):要求裝填因子a較小,閉散列方法把所有記錄直接存儲(chǔ)在散列表中
- 線性探測(cè):易產(chǎn)生堆積現(xiàn)象(基地址不同堆積在一起)
- 二次探測(cè):f(key) = (f(key)+di) % m di=12,-12,22,-22...可以消除基本聚集
- 隨機(jī)探測(cè):f(key) = (f(key)+di),di采用隨機(jī)函數(shù)得到,可以消除基本聚集
- 雙散列:避免二次聚集
開(kāi)散列(鏈地址法):原地處理
- 同義詞記錄存儲(chǔ)在一個(gè)單鏈表中,散列表中子存儲(chǔ)單鏈表的頭指針。
- 優(yōu)點(diǎn):無(wú)堆積 事先無(wú)需確定表長(zhǎng) 刪除結(jié)點(diǎn)易于實(shí)現(xiàn) 裝載因子a>=1,缺點(diǎn):需要額外空間
27.枚舉類
JDK1.5出現(xiàn) 每個(gè)枚舉值都需要調(diào)用一次構(gòu)造函數(shù)
28.數(shù)組復(fù)制方法
- for逐一復(fù)制
- System.arraycopy() -> 效率最高native方法
- Arrays.arrayOf() -> 本質(zhì)調(diào)用arraycopy
- clone方法 -> 返回Object[],需要強(qiáng)制類型轉(zhuǎn)換
29.多態(tài)
- Java通過(guò)方法重寫和方法重載實(shí)現(xiàn)多態(tài)
- 方法重寫是指子類重寫了父類的同名方法
- 方法重載是指在同一個(gè)類中,方法的名字相同,但是參數(shù)列表不同
30.Java文件
.java文件可以包含多個(gè)類,唯一的限制就是:一個(gè)文件中只能有一個(gè)public類, 并且此public類必須與
文件名相同。而且這些類和寫在多個(gè)文件中沒(méi)有區(qū)別。
31.Java移位運(yùn)算符
java中有三種移位運(yùn)算符
- << :左移運(yùn)算符,x << 1,相當(dāng)于x乘以2(不溢出的情況下),低位補(bǔ)0
:帶符號(hào)右移,x >> 1,相當(dāng)于x除以2,正數(shù)高位補(bǔ)0,負(fù)數(shù)高位補(bǔ)1
:無(wú)符號(hào)右移,忽略符號(hào)位,空位都以0補(bǔ)齊
32.形參&實(shí)參
- 形式參數(shù)可被視為local variable.形參和局部變量一樣都不能離開(kāi)方法。只有在方法中使用,不會(huì)在方法外可見(jiàn)。
- 形式參數(shù)只能用final修飾符,其它任何修飾符都會(huì)引起編譯器錯(cuò)誤。但是用這個(gè)修飾符也有一定的限制,就是在方法中不能對(duì)參數(shù)做任何修改。不過(guò)一般情況下,一個(gè)方法的形參不用final修飾。只有在特殊情況下,那就是:方法內(nèi)部類。一個(gè)方法內(nèi)的內(nèi)部類如果使用了這個(gè)方法的參數(shù)或者局部變量的話,這個(gè)參數(shù)或局部變量應(yīng)該是final。
- 形參的值在調(diào)用時(shí)根據(jù)調(diào)用者更改,實(shí)參則用自身的值更改形參的值(指針、引用皆在此列),也就是說(shuō)真正被傳遞的是實(shí)參。
33.IO
34.局部變量為什么要初始化
局部變量是指類方法中的變量,必須初始化。局部變量運(yùn)行時(shí)被分配在棧中,量大,生命周期短,如果虛擬機(jī)給每個(gè)局部變量都初始化一下,是一筆很大的開(kāi)銷,但變量不初始化為默認(rèn)值就使用是不安全的。出于速度和安全性兩個(gè)方面的綜合考慮,解決方案就是虛擬機(jī)不初始化,但要求編寫者一定要在使用前給變量賦值。
35.JDK提供的用于并發(fā)編程的同步器
- Semaphore Java并發(fā)庫(kù)的Semaphore可以很輕松完成信號(hào)量控制,Semaphore可以控制某個(gè)資源可被同時(shí)訪問(wèn)的個(gè)數(shù),通過(guò) acquire() 獲取一個(gè)許可,如果沒(méi)有就等待,而 release() 釋放一個(gè)許可。
- CyclicBarrier 主要的方法就是一個(gè):await()。await()方法每被調(diào)用一次,計(jì)數(shù)便會(huì)減少1,并阻塞住當(dāng)前線程。當(dāng)計(jì)數(shù)減至0時(shí),阻塞解除,所有在此CyclicBarrier上面阻塞的線程開(kāi)始運(yùn)行。
- 直譯過(guò)來(lái)就是倒計(jì)數(shù)(CountDown)門閂(Latch)。倒計(jì)數(shù)不用說(shuō),門閂的意思顧名思義就是阻止前進(jìn)。在這里就是指 CountDownLatch.await() 方法在倒計(jì)數(shù)為0之前會(huì)阻塞當(dāng)前線程。
36.Java類加載器
一個(gè)jvm中默認(rèn)的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader,分別各司其職:
- Bootstrap ClassLoader(引導(dǎo)類加載器) 負(fù)責(zé)加載java基礎(chǔ)類,主要是 %JRE_HOME/lib/目錄下的rt.jar、resources.jar、charsets.jar等
- Extension ClassLoader(擴(kuò)展類加載器) 負(fù)責(zé)加載java擴(kuò)展類,主要是 %JRE_HOME/lib/ext目錄下的jar等
- App ClassLoader(系統(tǒng)類加載器) 負(fù)責(zé)加載當(dāng)前java應(yīng)用的classpath中的所有類。
classloader 加載類用的是全盤負(fù)責(zé)委托機(jī)制。 所謂全盤負(fù)責(zé),即是當(dāng)一個(gè)classloader加載一個(gè)Class的時(shí)候,這個(gè)Class所依賴的和引用的所有 Class也由這個(gè)classloader負(fù)責(zé)載入,除非是顯式的使用另外一個(gè)classloader載入。
所以,當(dāng)我們自定義的classloader加載成功了com.company.MyClass以后,MyClass里所有依賴的class都由這個(gè)classLoader來(lái)加載完成。
37.Java語(yǔ)言的魯棒性
Java在編譯和運(yùn)行程序時(shí),都要對(duì)可能出現(xiàn)的問(wèn)題進(jìn)行檢查,以消除錯(cuò)誤的產(chǎn)生。它提供自動(dòng)垃圾收集來(lái)進(jìn)行內(nèi)存管理,防止程序員在管理內(nèi)存時(shí)容易產(chǎn)生的錯(cuò)誤。通過(guò)集成的面向?qū)ο蟮睦馓幚頇C(jī)制,在編譯時(shí),Java揭示出可能出現(xiàn)但未被處理的例外,幫助程序員正確地進(jìn)行選擇以防止系統(tǒng)的崩潰。另外,Java在編譯時(shí)還可捕獲類型聲明中的許多常見(jiàn)錯(cuò)誤,防止動(dòng)態(tài)運(yùn)行時(shí)不匹配問(wèn)題的出現(xiàn)。
38.Java語(yǔ)言特性
- Java致力于檢查程序在編譯和運(yùn)行時(shí)的錯(cuò)誤
- Java虛擬機(jī)實(shí)現(xiàn)了跨平臺(tái)接口
- 類型檢查幫助檢查出許多開(kāi)發(fā)早期出現(xiàn)的錯(cuò)誤
- Java自己操縱內(nèi)存減少了內(nèi)存出錯(cuò)的可能性
- Java還實(shí)現(xiàn)了真數(shù)組,避免了覆蓋數(shù)據(jù)的可能
39.Hibernate延遲加載
- Hibernate2延遲加載實(shí)現(xiàn):a)實(shí)體對(duì)象 b)集合(Collection)
- Hibernate3 提供了屬性的延遲加載功能
當(dāng)Hibernate在查詢數(shù)據(jù)的時(shí)候,數(shù)據(jù)并沒(méi)有存在與內(nèi)存中,當(dāng)程序真正對(duì)數(shù)據(jù)的操作時(shí),對(duì)象才存在與內(nèi)存中,就實(shí)現(xiàn)了延遲加載,他節(jié)省了服務(wù)器的內(nèi)存開(kāi)銷,從而提高了服務(wù)器的性能。 - hibernate使用Java反射機(jī)制,而不是字節(jié)碼增強(qiáng)程序來(lái)實(shí)現(xiàn)透明性。
- hibernate的性能非常好,因?yàn)樗莻€(gè)輕量級(jí)框架。映射的靈活性很出色。它支持各種關(guān)系數(shù)據(jù)庫(kù),從一對(duì)一到多對(duì)多的各種復(fù)雜關(guān)系。
40.包裝類的equals()方法不處理數(shù)據(jù)轉(zhuǎn)型,必須類型和值都一樣才相等。
41.子類可以繼承父類的靜態(tài)方法!但是不能覆蓋。因?yàn)殪o態(tài)方法是在編譯時(shí)確定了,不能多態(tài),也就是不能運(yùn)行時(shí)綁定。
42.Java語(yǔ)法糖
- Java7的switch用字符串 - hashcode方法 switch用于enum枚舉
- 偽泛型 - List< E>原始類型
- 自動(dòng)裝箱拆箱 - Integer.valueOf和Integer.intValue
- foreach遍歷 - Iterator迭代器實(shí)現(xiàn)
- 條件編譯
- enum枚舉類、內(nèi)部類
- 可變參數(shù) - 數(shù)組
- 斷言語(yǔ)言
- try語(yǔ)句中定義和關(guān)閉資源
43.JVM工具
命令行
- jps(jvm processor status)虛擬機(jī)進(jìn)程狀況工具
- jstat(jvm statistics monitoring)統(tǒng)計(jì)信息監(jiān)視
- jinfo(configuration info for java)配置信息工具
- jmap(memory map for java)Java內(nèi)存映射工具
- jhat(JVM Heap Analysis Tool)虛擬機(jī)堆轉(zhuǎn)儲(chǔ)快照分析工具
- jstack(Stack Trace for Java)Java堆棧跟蹤工具
- HSDIS:JIT生成代碼反匯編
可視化
- JConsole(Java Monitoring and Management Console):Java監(jiān)視與管理控制臺(tái)
- VisualVM(All-in-one Java Troubleshooting Tool):多合一故障處理工具
44.內(nèi)部類為什么可以訪問(wèn)外部類的私有屬性
在內(nèi)部類構(gòu)造的時(shí)候,會(huì)將外部類的引用傳遞進(jìn)來(lái),并且作為內(nèi)部類的一個(gè)屬性,所以內(nèi)部類會(huì)持有一個(gè)其外部類的引用。
當(dāng)內(nèi)部類調(diào)用外部類的私有屬性時(shí),其真正的執(zhí)行是調(diào)用了編譯器生成的屬性的靜態(tài)方法(即acess1等)來(lái)獲取這些屬性值。這一切都是編譯器的特殊處理。
外部類可以通過(guò)內(nèi)部類的實(shí)例獲取私有屬性x的操作.
45.如何讓內(nèi)部類私有成員不被外部訪問(wèn)
使用匿名內(nèi)部類
public class PrivateToOuter {
Runnable mRunnable = new Runnable(){
private int x=10;
@Override
public void run() {
System.out.println(x);
}
};
public static void main(String[] args){
PrivateToOuter p = new PrivateToOuter();
//System.out.println("anonymous class private filed= "+ p.mRunnable.x); //not allowed
p.mRunnable.run(); // allowed
}
}
由于mRunnable對(duì)象的類型為Runnable,而不是匿名內(nèi)部類的類型(我們無(wú)法正常拿到),而Runanble中沒(méi)有x這個(gè)屬性,所以mRunnable.x是不被允許的。
46. Java匿名內(nèi)部類訪問(wèn)外部變量,為何需被標(biāo)志為final?
- 這要從閉包說(shuō)起,匿名內(nèi)部類和外部方法形成了一個(gè)閉包,因此,匿名內(nèi)部類能夠訪問(wèn)外部方法的變量,看起來(lái)是一種“天經(jīng)地義”的事情,Java語(yǔ)言當(dāng)然也需要實(shí)現(xiàn)這種特性,但是這里遇到了一個(gè)問(wèn)題。
- 匿名內(nèi)部類的生命周期可能比外部的類要長(zhǎng),因此訪問(wèn)外部局部變量有可能是訪問(wèn)不到的。
- 那怎么辦呢?Java語(yǔ)言為了實(shí)現(xiàn)這種特性, 只好將外部的局部變量偷偷的賦值了一份給匿名內(nèi)部類。那這樣匿名內(nèi)部類就可以肆無(wú)忌憚的訪問(wèn)外部局部變量了。
- 問(wèn)題又來(lái)了,這種通過(guò)賦值的形式有一個(gè)缺陷,匿名內(nèi)部類不可以修改“原來(lái)的局部變量”,因?yàn)槭且环荨皬?fù)制品”,修改復(fù)制品對(duì)原變量沒(méi)什么影響啊。
- 那怎么辦? Java語(yǔ)言干脆強(qiáng)制要求被匿名內(nèi)部類訪問(wèn)的外部局部變量必須是final的,什么意思呢?就是“一刀切”,不讓修改了。
47. 非靜態(tài)內(nèi)部類為什么不能有靜態(tài)成員
- static類型的屬性和方法,在類加載的時(shí)候就會(huì)存在于內(nèi)存中。
- 要想使用某個(gè)類的static屬性和方法,那么這個(gè)類必須要加載到虛擬機(jī)- 中。
- 非靜態(tài)內(nèi)部類并不隨外部類一起加載,只有在實(shí)例化外部類之后才會(huì)加載。
在外部類并沒(méi)有實(shí)例化,內(nèi)部類還沒(méi)有加載,這時(shí)候如果調(diào)用內(nèi)部類的靜態(tài)成員或方法,內(nèi)部類還沒(méi)有加載,卻試圖在內(nèi)存中創(chuàng)建該內(nèi)部類的靜態(tài)成員,這明顯是矛盾的。
所以非靜態(tài)內(nèi)部類不能有靜態(tài)成員變量或靜態(tài)方法。
48. 手寫實(shí)現(xiàn)二分查找
/**
* 使用遞歸的二分查找
**title:recursionBinarySearch
**@param arr 有序數(shù)組
**@param key 待查找關(guān)鍵字
**@return 找到的位置
*/
public static int recursionBinarySearch( int[] arr, int key, int low, int high )
{
if ( key < arr[low] || key > arr[high] || low > high )
{
return(-1);
}
int middle = (low + high) / 2; /* 初始中間位置 */
if ( arr[middle] > key )
{
/* 比關(guān)鍵字大則關(guān)鍵字在左區(qū)域 */
return(recursionBinarySearch( arr, key, low, middle - 1 ) );
}else if ( arr[middle] < key )
{
/* 比關(guān)鍵字小則關(guān)鍵字在右區(qū)域 */
return(recursionBinarySearch( arr, key, middle + 1, high ) );
}else {
return(middle);
}
}