Overriding(重寫) & Overloading(重載)
- Overriding - same method names with same arguments and same return types associated in a class and its subclass.(一個(gè)類和它的子類相同方法名、參數(shù)和返回類型)
- Overloading - same method name with different arguments, may or may not be same return type written in the same class itself.(同一個(gè)類中同方法名不同參數(shù))
強(qiáng)引用、弱引用,[鏈接1咨察,鏈接2]
- 如果一個(gè)對(duì)象具有強(qiáng)引用坞古,那垃圾回收器絕不會(huì)回收它亩进。當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來解決內(nèi)存不足的問題蜈敢。
- 如果一個(gè)對(duì)象只具有軟引用,則內(nèi)存空間足夠汽抚,垃圾回收器就不會(huì)回收它抓狭;如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存造烁。只要垃圾回收器沒有回收它否过,該對(duì)象就可以被程序使用。軟引用可用來實(shí)現(xiàn)內(nèi)存敏感的高速緩存惭蟋。軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用苗桂,如果軟引用所引用的對(duì)象被垃圾回收器回收,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中告组。
finalize()函數(shù)是在JVM回收內(nèi)存時(shí)執(zhí)行的煤伟,但JVM并不保證在回收內(nèi)存時(shí)一定會(huì)調(diào)用finalize()。
final惹谐, finally持偏, finalize
- 如果一個(gè)類被聲明為final驼卖,意味著它不能再派生出新的子類氨肌,不能作為父類被繼承。因此一個(gè)類不能既被聲明為 abstract的酌畜,又被聲明為final的怎囚。將變量或方法聲明為final,可以保證它們?cè)谑褂弥胁槐桓淖儭1宦暶鳛閒inal的變量必須在聲明時(shí)給定初值恳守,而在以后的引用中只能讀取考婴,不可修改。被聲明為final的方法也同樣只能使用催烘,不能重載
- 在異常處理時(shí)提供 finally 塊來執(zhí)行任何清除操作沥阱。如果拋出一個(gè)異常,那么相匹配的 catch 子句就會(huì)執(zhí)行伊群,然后控制就會(huì)進(jìn)入 finally 塊(如果有的話)考杉。
- Java 技術(shù)允許使用 finalize()方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。這個(gè)方法是由垃圾收集器在確定這個(gè)對(duì)象沒有被引用時(shí)對(duì)這個(gè)對(duì)象調(diào)用的舰始。它是在 Object 類中定義的崇棠,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作丸卷。finalize()方法是在垃圾收集器刪除對(duì)象之前對(duì)這個(gè)對(duì)象調(diào)用的枕稀。
深拷貝(deep clone),淺拷貝(shallow clone)
- 克隆就是復(fù)制一個(gè)對(duì)象的復(fù)本.但一個(gè)對(duì)象中可能有基本數(shù)據(jù)類型,如:int,long,float 等,也同時(shí)含有非基本數(shù)據(jù)類型如(數(shù)組,集合等)
- 被克隆得到的對(duì)象基本類型的值修改了,原對(duì)象的值不會(huì)改變.這種適合shadow clone(淺克隆).
- 如果你要改變一個(gè)非基本類型的值時(shí),原對(duì)象的值卻改變了,比如一個(gè)數(shù)組,內(nèi)存中只copy他的地址,而這個(gè)地址指向的值并沒有 copy,當(dāng)clone時(shí),兩個(gè)地址指向了一個(gè)值,這樣一旦這個(gè)值改變了,原來的值當(dāng)然也變了,因?yàn)樗麄児灿靡粋€(gè)值.,這就必須得用深克隆(deep clone)
結(jié)論:
- 淺克旅占怠:基本類型是可以被克隆的,但引用類型只是copy地址,并沒有copy這個(gè)地址指向的對(duì)象的值,這使得兩個(gè)地址指向同一值,修改其中一個(gè),當(dāng)然另一個(gè)也就變了.淺克隆只適合克隆基本類型,對(duì)于引用類型就不能實(shí)現(xiàn)克隆了.
- 通過
implements Cloneable
重寫clone
方法實(shí)現(xiàn)
- 可以用序列化與反序列化實(shí)現(xiàn)深克隆(deep copy)
- 通過
implements Serializable
實(shí)現(xiàn)
當(dāng)克隆的對(duì)象只有基本類型,不含引用類型時(shí),可以用淺克隆實(shí)現(xiàn).
當(dāng)克隆的對(duì)象含有引用類型時(shí),必須使用深克隆實(shí)現(xiàn).
- java提供一種叫淺拷貝(shallow copy)的默認(rèn)方式實(shí)現(xiàn)clone萎坷,創(chuàng)建好對(duì)象的副本后然后通過賦值拷貝內(nèi)容,意味著如果你的類包含引用類型骄恶,那么原始對(duì)象和克隆都將指向相同的引用內(nèi)容食铐。發(fā)生在可變的字段上任何改變將反應(yīng)到他們所引用的共同內(nèi)容上。為了避免這種情況僧鲁,需要對(duì)引用的內(nèi)容進(jìn)行深度克隆虐呻。
comparable接口與comparator
- 實(shí)現(xiàn)了 Camparable 接口表明這個(gè)類的對(duì)象之間是可以相互比較的。意味著這個(gè)類對(duì)象組成的集合就可以使用 Sort 方法排序了寞秃。
- Comparator 的作用有兩個(gè):
- 沒有實(shí)現(xiàn) Comparable 接口斟叼,可以通過 Comparator 來實(shí)現(xiàn)比較算法進(jìn)行排序
- 為了使用不同的排序標(biāo)準(zhǔn)做準(zhǔn)備
結(jié)論:
“集合框架” 中有兩種比較接口: Comparable 接口和 Comparator 接口:
- Comparable 是通用的接口,用戶可以實(shí)現(xiàn)它來完成自己特定的比較
- Comparator 可以看成一種算法的實(shí)現(xiàn)春寿,在需要容器集合實(shí)現(xiàn)比較功能的時(shí)候朗涩,來指定這個(gè)比較器,這可以看成一種設(shè)計(jì)模式绑改,將算法和數(shù)據(jù)分離谢床。
hashcode & equals [鏈接]
- 覆寫equals方法
- 覆寫hashcode
-
兩個(gè)對(duì)象如果equals那么這兩個(gè)對(duì)象的hashcode一定相等,如果兩個(gè)對(duì)象的hashcode相等那么這兩個(gè)對(duì)象是否一定equals?
- 這要看這兩個(gè)對(duì)象有沒有重寫Object的hashCode方法和equals方法厘线。如果沒有重寫识腿,是按Object默認(rèn)的方式去處理。
- '==' 比較地址造壮,equal 自定義
-
hashcode中為什么要使用 31 這個(gè)數(shù)渡讼?
- 31 是一個(gè)素?cái)?shù)(質(zhì)數(shù)),如:我們選擇素?cái)?shù)3來做系數(shù),那么3*n只能被3和n或者1來整除成箫,我們可以很容易的通過3n來計(jì)算出這個(gè)n來展箱。
- 任何數(shù)n * 31就可以被JVM優(yōu)化為 (n << 5) -n,移位和減法的操作效率要比乘法的操作效率高的多,對(duì)左移現(xiàn)在很多虛擬機(jī)里面都有做相關(guān)優(yōu)化蹬昌,并且31只占用5bits混驰!
可變類與不可變類的區(qū)別
- 當(dāng)創(chuàng)建了這個(gè)類的實(shí)例后,就不允許修改它的屬性值皂贩。在JDK的基本類庫(kù)中账胧,所有基本類型的包裝類,如Integer和Long類先紫,都是不可變類治泥,java.lang.String也是不可變類。
- 如何創(chuàng)建一個(gè)不可變實(shí)例類:
- 1. 所有成員都是private
- 2. 不提供對(duì)成員的改變方法遮精,例如:setXXXX
- 3. 確保所有的方法不會(huì)被重載居夹。手段有兩種:使用final Class(強(qiáng)不可變類),或者將所有類方法加上final(弱不可變類)本冲。
- 4. 如果某一個(gè)類成員不是原始變量(primitive)或者不可變類准脂,必須通過在成員初始化(in)或者get方法(out)時(shí)通過深度clone方法,來確保類的不可變檬洞。
字符串常量池 狸膏,深入解析String#intern。
- JAVA 語(yǔ)言中8中基本類型和一種比較特殊的類型
String
,為了使他們?cè)谶\(yùn)行過程中速度更快添怔,更節(jié)省內(nèi)存湾戳,都提供了一種常量池的概念。 - 8種基本類型的常量池都是系統(tǒng)協(xié)調(diào)的广料,
String
類型的常量池比較特殊砾脑。它的主要使用方法有兩種:- 直接使用雙引號(hào)聲明出來的
String
對(duì)象會(huì)直接存儲(chǔ)在常量池中。 - 如果不是用雙引號(hào)聲明的
String
對(duì)象艾杏,可以使用String
提供的intern
方法韧衣。intern 方法會(huì)從字符串常量池中查詢當(dāng)前字符串是否存在,若不存在就會(huì)將當(dāng)前字符串放入常量池中 -
String#intern
方法是一個(gè) native 的方法:如果常量池中存在當(dāng)前字符串, 就會(huì)直接返回當(dāng)前字符串. 如果常量池中沒有此字符串, 會(huì)將此字符串放入常量池中后, 再返回
- 直接使用雙引號(hào)聲明出來的
Q: String s = new String("abc")這個(gè)語(yǔ)句創(chuàng)建了幾個(gè)對(duì)象的題目?(考察字符串對(duì)象的常量池)
A: 上述的語(yǔ)句中是創(chuàng)建了2個(gè)對(duì)象购桑,第一個(gè)對(duì)象是"abc"字符串存儲(chǔ)在常量池中畅铭,第二個(gè)對(duì)象在JAVA Heap中的 String 對(duì)象。
- 在 Jdk6 以及以前的版本中勃蜘,字符串的常量池是放在堆的 Perm 區(qū)的硕噩,Perm 區(qū)是一個(gè)類靜態(tài)的區(qū)域,主要存儲(chǔ)一些加載類的信息元旬,常量池榴徐,方法片段等內(nèi)容,默認(rèn)大小只有4m
- jdk7 的版本中匀归,字符串常量池已經(jīng)從 Perm 區(qū)移到正常的 Java Heap 區(qū)域
Java 泛型 [泛型詳解]
- 泛型基礎(chǔ)
- 泛型類
- 泛型方法
- 邊界符
- 類似于
T extends Comparable<T>
這樣的聲明坑资,告訴編譯器類型參數(shù)T
代表的都是實(shí)現(xiàn)了Comparable
接口的類
- 類似于
- 通配符: ?
-
PECS原則[”Producer Extends, Consumer Super”]
- ? super T 與 ? extends T 的區(qū)別
- 生產(chǎn)者(Producer)使用extends穆端,消費(fèi)者(Consumer)使用super袱贮。
- “Producer Extends” - 如果你需要一個(gè)只讀List,用它來produce T体啰,那么使用
? extends T
攒巍。 - “Consumer Super” - 如果你需要一個(gè)只寫List,用它來consume T荒勇,那么使用
? super T
柒莉。 - 如果需要同時(shí)讀取以及寫入,那么我們就不能使用通配符了沽翔。
-
類型擦除
- 類型擦除就是說Java泛型只能用于在編譯期間的靜態(tài)類型檢查兢孝,然后編譯器生成的代碼會(huì)擦除相應(yīng)的類型信息,這樣到了運(yùn)行期間實(shí)際上JVM根本就不知道泛型所代表的具體類型仅偎。
- Java泛型是1.5之后才被引入的跨蟹,為了保持向下的兼容性,所以只能做類型擦除來兼容以前的非泛型代碼橘沥。
String, StringBuffer, StringBuilder的區(qū)別
- String:字符串常量窗轩,字符串長(zhǎng)度不可變,不可變類
- StringBuffer:字符串變量(Synchronized座咆,即線程安全)
- 每次都會(huì)對(duì) StringBuffer 對(duì)象本身進(jìn)行操作痢艺,而不是生成新的對(duì)象并改變對(duì)象引用
- 字符串對(duì)象經(jīng)常改變的情況
- StringBuilder:字符串變量(非線程安全)
1. 如果要操作少量的數(shù)據(jù),用String 介陶;單線程操作大量數(shù)據(jù)腹备,用StringBuilder ;多線程操作大量數(shù)據(jù)斤蔓,用StringBuffer植酥。
2.StringBuffer 或 StringBuilder 時(shí)應(yīng)盡可能指定它們的容量
類的實(shí)例化順序,比如父類靜態(tài)數(shù)據(jù)弦牡,構(gòu)造函數(shù)友驮,字段,子類靜態(tài)數(shù)據(jù)驾锰;構(gòu)造函數(shù)卸留,字段的執(zhí)行順序 [鏈接]
- 實(shí)例化順序 / 執(zhí)行順序
- 非繼承
- (靜態(tài)變量、靜態(tài)初始化塊)>(變量椭豫、初始化塊)> 構(gòu)造器
- 繼承
- 父類(靜態(tài)變量耻瑟、靜態(tài)初始化塊)> 子類(靜態(tài)變量旨指、靜態(tài)初始化塊) > 子類main方法 > 父類(變量、初始化塊) > 父類--構(gòu)造器 > 子類(變量喳整、初始化塊) > 子類--構(gòu)造器
- 靜態(tài)變量和靜態(tài)初始化塊的聲明順序決定了初始化的順序
- 非繼承
本地方法棧和虛擬機(jī)棧 谆构、堆
- 虛擬機(jī)棧
- JVM規(guī)范讓每個(gè)Java線程擁有自己的獨(dú)立的JVM棧,也就是Java方法的調(diào)用棧框都。
- 本地方法棧
- JVM規(guī)范為了允許native代碼可以調(diào)用Java代碼搬素,以及允許Java代碼調(diào)用native方法,還規(guī)定每個(gè)Java線程擁有自己的獨(dú)立的native方法棧魏保。
- 堆
- JVM里的“堆”(heap)特指用于存放Java對(duì)象的內(nèi)存區(qū)域熬尺。根據(jù)這個(gè)定義,Java對(duì)象全部都在堆上谓罗。
注意事項(xiàng)
1. 虛擬機(jī)棧粱哼、本地方法棧JVM規(guī)范所規(guī)定的概念上的東西,并不是說具體的JVM實(shí)現(xiàn)真的要給每個(gè)Java線程開兩個(gè)獨(dú)立的棧檩咱≡硭保可能只使用一個(gè)棧,融合以上兩個(gè)棧的概念
2. 堆不是數(shù)據(jù)結(jié)構(gòu)意義上的堆(Heap税手,一種有序的樹)蜂筹,而是動(dòng)態(tài)分配意義上的堆---用于管理動(dòng)態(tài)生命周期的內(nèi)存區(qū)域
3. JVM堆被同一個(gè)JVM實(shí)例中的所有線程共享,通常由自動(dòng)內(nèi)存管理機(jī)制管理(“垃圾回收”,GC,garbage collection)
Java內(nèi)存模型 【鏈接】
-
Java內(nèi)存模型芦倒,往往是指Java程序在運(yùn)行時(shí)內(nèi)存的模型
-
運(yùn)行時(shí)內(nèi)存模型艺挪,分為線程私有和共享數(shù)據(jù)區(qū)兩大類
- 線程私有
- 程序計(jì)數(shù)器:記錄正在執(zhí)行的虛擬機(jī)指令碼的地址
- 虛擬機(jī)棧:方法執(zhí)行的內(nèi)存區(qū),每個(gè)方法執(zhí)行時(shí)會(huì)在虛擬機(jī)棧中創(chuàng)建棧幀
- 本地方法區(qū):虛擬機(jī)的Native方法執(zhí)行的內(nèi)存區(qū)
- 共享數(shù)據(jù)區(qū)
- Java堆:對(duì)象分配內(nèi)存的區(qū)域
- 方法區(qū):存放類信息兵扬、常量麻裳、靜態(tài)變量、編譯器編譯后的代碼等數(shù)據(jù)
- 在方法區(qū)內(nèi)有一個(gè)常量池:存放編譯器生成的各種字面量和符號(hào)引用器钟,是方法區(qū)的一部分
- 線程私有
為什么函數(shù)調(diào)用要用棧實(shí)現(xiàn)津坑?【鏈接】
- 函數(shù)的調(diào)用有完美的嵌套關(guān)系——調(diào)用者的生命期總是長(zhǎng)于被調(diào)用者的生命期,并且后者在前者的之內(nèi)
- 被調(diào)用者的局部信息所占空間的分配總是后于調(diào)用者的(后入)傲霸,而其釋放則總是先于調(diào)用者的(先出)疆瑰,所以正好可以滿足棧的LIFO順序
- 函數(shù)調(diào)用的局部狀態(tài)之所以用棧來記錄是因?yàn)檫@些數(shù)據(jù)的存活時(shí)間滿足“后入先出”(LIFO)順序,而棧的基本操作正好就是支持這種順序的訪問昙啄。
反射和動(dòng)態(tài)代理
- Java 反射機(jī)制可以在運(yùn)行時(shí)期檢查 Java 類的信息
- 類的信息 包括:
- Class 對(duì)象
Class.forName()
必須提供一個(gè)類的全名
MyObject.class
前提知道類名 - 類名
getName()
獲取類的全限定類名(包含包名)
getSimpleName()
獲取類名(不包含包名) - 修飾符
getModifiers()
獲取類的修飾符,使用java.lang.reflect.Modifier
類中的方法來檢查修飾符的類型
Modifier.isPrivate(int modifiers);
- 包信息
getPackage()
獲取包信息 - 父類
getSuperclass()
訪問類的父類 - 實(shí)現(xiàn)的接口
getInterfaces()
獲取類所實(shí)現(xiàn)的接口集合(只有實(shí)現(xiàn)了接口才返回) - 構(gòu)造器
getConstructors()
獲取類的構(gòu)造方法 - 方法
getMethods()
獲取類的所有方法 - 變量
getFields()
獲取類的所有成員變量 - 注解
getAnnotations()
獲取類的所有注解
- Class 對(duì)象
-
代理(參考鏈接)
代理類主要負(fù)責(zé)為委托類預(yù)處理消息穆役、過濾消息、把消息轉(zhuǎn)發(fā)給委托類梳凛,以及事后處理消息等
為了保持行為的一致性耿币,代理類和委托類通常會(huì)實(shí)現(xiàn)相同的接口,所以在訪問者看來兩者沒有絲毫的區(qū)別韧拒。通過代理類這中間一層淹接,能有效控制對(duì)委托類對(duì)象的直接訪問十性,也可以很好地隱藏和保護(hù)委托類對(duì)象,同時(shí)也為實(shí)施不同控制策略預(yù)留了空間塑悼,從而在設(shè)計(jì)上獲得了更大的靈活性劲适。
按照代理的創(chuàng)建時(shí)期,代理類可以分為兩種: -
靜態(tài)代理
在程序運(yùn)行前代理類的.class文件就已經(jīng)存在了- 優(yōu)點(diǎn):業(yè)務(wù)類只需要關(guān)注業(yè)務(wù)邏輯本身拢肆,保證了業(yè)務(wù)類的重用性。這是代理的共有優(yōu)點(diǎn)靖诗。
- 缺點(diǎn):
- 一個(gè)接口只服務(wù)于一種類型的對(duì)象郭怪,代理類增多時(shí),會(huì)造成重復(fù)代碼
- 如果接口增加一個(gè)方法所有的實(shí)現(xiàn)類和代理類都要實(shí)現(xiàn)該方法刊橘,增加了代碼的維護(hù)負(fù)責(zé)度
-
動(dòng)態(tài)代理(JDK)-- 通過接口
在程序運(yùn)行時(shí)運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成- 與動(dòng)態(tài)代理相關(guān)的API:
-
java.lang.reflect.Proxy
- 這是 Java 動(dòng)態(tài)代理機(jī)制生成的所有動(dòng)態(tài)代理類的父類鄙才,它提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其對(duì)象。
-
java.lang.reflect.InvocationHandler
- 這是調(diào)用處理器接口促绵,它自定義了一個(gè) invoke 方法攒庵,用于集中處理在動(dòng)態(tài)代理類對(duì)象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對(duì)委托類的代理訪問败晴。
- 每次生成動(dòng)態(tài)代理類對(duì)象時(shí)都要指定一個(gè)對(duì)應(yīng)的調(diào)用處理器對(duì)象浓冒。
-
java.lang.ClassLoader
- Proxy 靜態(tài)方法生成動(dòng)態(tài)代理類同樣需要通過類裝載器來進(jìn)行裝載才能使用,它與普通類的唯一區(qū)別就是其字節(jié)碼是由 JVM 在運(yùn)行時(shí)動(dòng)態(tài)生成的而非預(yù)存在于任何一個(gè) .class 文件中尖坤。
- 每次生成動(dòng)態(tài)代理類對(duì)象時(shí)都需要指定一個(gè)類裝載器對(duì)象
-
-
動(dòng)態(tài)代理實(shí)現(xiàn)步驟
- 實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器
- 給Proxy類提供ClassLoader和代理接口類型數(shù)組創(chuàng)建動(dòng)態(tài)代理類
- 以調(diào)用處理器類型為參數(shù)稳懒,利用反射機(jī)制得到動(dòng)態(tài)代理類的構(gòu)造函數(shù)
- 以調(diào)用處理器對(duì)象為參數(shù),利用動(dòng)態(tài)代理類的構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類對(duì)象
- 與動(dòng)態(tài)代理相關(guān)的API:
> Proxy類的靜態(tài)方法`newProxyInstance`對(duì)上面具體步驟的后三步做了封裝慢味,簡(jiǎn)化了動(dòng)態(tài)代理對(duì)象的獲取過程
- 動(dòng)態(tài)代理的優(yōu)點(diǎn)與不足
- 優(yōu)點(diǎn):動(dòng)態(tài)代理與靜態(tài)代理相比較场梆,最大的好處是接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個(gè)集中的方法中處理(InvocationHandler.invoke)
- 缺點(diǎn):僅支持 interface 代理,__無法實(shí)現(xiàn)對(duì) class 的動(dòng)態(tài)代理__(原因是多繼承在 Java 中本質(zhì)上就行不通)
-
動(dòng)態(tài)代理(Cglib) -- 通過類繼承
- CGLIB(Code Generation Library)纯路,是一個(gè)強(qiáng)大的或油,高性能,高質(zhì)量的Code生成類庫(kù)驰唬,它可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口顶岸。
- 創(chuàng)建類A的動(dòng)態(tài)代理類的模式
- 查找A上的所有非final 的public類型的方法定義;
- 將這些方法的定義轉(zhuǎn)換成字節(jié)碼叫编;
- 將組成的字節(jié)碼轉(zhuǎn)換成相應(yīng)的代理的class對(duì)象蜕琴;
- 實(shí)現(xiàn) MethodInterceptor 接口,用來處理 對(duì)代理類上所有方法的請(qǐng)求(這個(gè)接口和JDK動(dòng)態(tài)代理InvocationHandler的功能和角色是一樣的)
總結(jié):
1. 為了解決使用靜態(tài)代理會(huì)造成系統(tǒng)結(jié)構(gòu)臃腫的問題宵溅,在運(yùn)行狀態(tài)中凌简,需要代理的地方,根據(jù)Subject 和RealSubject恃逻,動(dòng)態(tài)地創(chuàng)建一個(gè)Proxy雏搂,用完之后藕施,就會(huì)銷毀,這樣就可以避免了Proxy 角色的class在系統(tǒng)中冗雜的問題了凸郑。(動(dòng)態(tài)代理的優(yōu)勢(shì))
2.為了構(gòu)造出具有通用性和簡(jiǎn)單性的代理類裳食,可以將所有的觸發(fā)真實(shí)角色動(dòng)作交給一個(gè)觸發(fā)的管理器,讓這個(gè)管理器統(tǒng)一地管理觸發(fā)芙沥。這種管理器就是Invocation Handler诲祸。(Invocation Handler角色的由來)
3.動(dòng)態(tài)代理工作的基本模式就是將自己的方法功能的實(shí)現(xiàn)交給 InvocationHandler角色,外界對(duì)Proxy角色中的每一個(gè)方法的調(diào)用而昨,Proxy角色都會(huì)交給InvocationHandler來處理救氯,而InvocationHandler則調(diào)用具體對(duì)象角色的方法。
4.約定Proxy 和RealSubject可以實(shí)現(xiàn)相同的功能歌憨,有兩種方式:
- 定義一個(gè)功能接口着憨,然后讓Proxy 和RealSubject來實(shí)現(xiàn)這個(gè)接口。(JDK創(chuàng)建動(dòng)態(tài)代理用的這種思路)
- 通過繼承务嫡。因?yàn)槿绻鸓roxy 繼承自RealSubject甲抖,這樣Proxy則擁有了RealSubject的功能,Proxy還可以通過重寫RealSubject中的方法心铃,來實(shí)現(xiàn)多態(tài)准谚。(cglib使用這種思路)
5.兩種方式的比較:
- JDK:接口特點(diǎn)可代理多個(gè)接口性湿,無法代理未實(shí)現(xiàn)接口的類
- Cglib:繼承特點(diǎn)可直接代理類無需實(shí)現(xiàn)接口乎婿,無法一次代理多個(gè)類
-
Java 動(dòng)態(tài)代理機(jī)制詳解 (參考博客)
反射中Class.forName 和 ClassLoader的區(qū)別
-
類裝載過程
- 加載
通過累的全限定名獲取二進(jìn)制字節(jié)流,將二進(jìn)制字節(jié)流轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)乖篷,在內(nèi)存中生成Java.lang.class對(duì)象 - 鏈接
- 驗(yàn)證
檢查導(dǎo)入類或接口的二進(jìn)制數(shù)據(jù)的正確性厅篓;(文件格式驗(yàn)證秀存,元數(shù)據(jù)驗(yàn)證,字節(jié)碼驗(yàn)證羽氮,符號(hào)引用驗(yàn)證) - 準(zhǔn)備
給類的靜態(tài)變量分配并初始化存儲(chǔ)空間或链; - 解析
將常量池中的符號(hào)引用轉(zhuǎn)成直接引用;
- 驗(yàn)證
- 初始化
激活類的靜態(tài)變量的初始化Java代碼和靜態(tài)Java代碼塊档押,并初始化程序員設(shè)置的變量值澳盐。 - 使用
- 卸載
- 加載
- Java對(duì)類的使用分為兩種方式:主動(dòng)使用(new)和被動(dòng)使用(類裝載)
- 調(diào)用ClassLoader類的loadClass方法加載一個(gè)類,并不是對(duì)類的主動(dòng)使用令宿,不會(huì)導(dǎo)致類的初始化
-
Class.forName()前者除了將類的.class文件加載到j(luò)vm中之外叼耙,默認(rèn)還會(huì)對(duì)類進(jìn)行解釋,執(zhí)行類中的static塊粒没。
-
classLoader只干一件事情筛婉,就是將.class文件加載到j(luò)vm中,不會(huì)執(zhí)行static中的內(nèi)容,只有在newInstance才會(huì)去執(zhí)行static塊癞松。
-
Class.forName(className)
默認(rèn)是需要初始化的
一旦初始化爽撒,就會(huì)觸發(fā)目標(biāo)對(duì)象的 static塊代碼執(zhí)行入蛆,static參數(shù)也也會(huì)被再次初始化。 -
ClassLoader.loadClass(className)
內(nèi)部實(shí)際調(diào)用的方法是ClassLoader.loadClass(className,false)
第2個(gè) boolean參數(shù)硕勿,表示目標(biāo)對(duì)象是否進(jìn)行鏈接哨毁,false表示不進(jìn)行鏈接
不進(jìn)行鏈接意味著不進(jìn)行包括初始化等一些列步驟,那么靜態(tài)塊和靜態(tài)對(duì)象就不會(huì)得到執(zhí)行
數(shù)據(jù)庫(kù)鏈接為什么使用Class.forName(className)?
JDBC Driver源碼static{ try{ java.sql.DriverManager.registerDriver(new Driver()); } catch(SQLException e){ throw new RuntimeException("Can't register driver!"); } }
因此使用Class.forName(classname)才能在反射回去類的時(shí)候執(zhí)行static塊
Iterator設(shè)計(jì)思想
如何實(shí)現(xiàn)兩種容器的可替換性(底層容器的實(shí)現(xiàn)隨意改變不影響用戶使用)源武?
統(tǒng)一接口扼褪、面向接口編程
以ArrayList和LinkedList為例
ArrayList的繼承圖:
LinkedList的繼承圖:
ArrayList和LinkedList都實(shí)現(xiàn)了Collection接口、Iterator接口
//通過實(shí)現(xiàn)統(tǒng)一Collection接口粱栖,面向接口編程话浇,實(shí)現(xiàn)底層容器切換不影響使用,達(dá)到對(duì)用戶透明的效果
Collection<String> collection = new ArrayList<String>();
//Collection<String> collection = new LinkedList<String>();
collection.add("hello");
collection.add("java");
collection.remove("hello");
Iterator 基于以上思想實(shí)現(xiàn) Collection 所有實(shí)現(xiàn)類的統(tǒng)一遍歷方式查排,不需關(guān)心具體實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)(數(shù)組凳枝,鏈表)抄沮,具體的遍歷方式由容器自己根據(jù)自身特點(diǎn)實(shí)現(xiàn)
1. 實(shí)現(xiàn)Iterable接口的類都可以使用“foreach”操作
/** Implementing this interface allows an object to be the target of
* the "foreach" statement.
* @since 1.5
*/
public interface Iterable<T> {
/**
* Returns an iterator over a set of elements of type T.
*
* @return an Iterator.
*/
Iterator<T> iterator();
····
}
2. 統(tǒng)一 Iterator 接口
public interface Iterator<E> {
// 是否還有元素
boolean hasNext();
// 下一個(gè)元素
E next();
// 將迭代器返回的元素刪除
void remove();
···
}
3. ArrayList的Iterator的具體實(shí)現(xiàn)
通過繼承 Iterable 接口跋核,定義 Iterator 規(guī)范 迭代方法,Collection 的所有實(shí)現(xiàn)類實(shí)現(xiàn)自己的 Iterator 接口叛买,實(shí)現(xiàn)統(tǒng)一的砂代、對(duì)用戶透明的迭代方法
使用示例:
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
System. out.println(iterator.next());
}
AbstractList 抽象類中 modCount 變量分析
總結(jié):
這個(gè)數(shù)統(tǒng)計(jì)list 發(fā)生結(jié)構(gòu)性改變的次數(shù)
這個(gè)變量被 iterator 和 listIterator 方法返回的 iterator 實(shí)現(xiàn)使用,如果這個(gè)變量發(fā)生非預(yù)期的改變率挣,iterator 將在 next , remove , previous , set 或者 add操作時(shí)拋出 ConcurrentModificationException 異常刻伊,提供了一個(gè)快速響應(yīng)失敗的機(jī)制(fail-fast)而不是在遍歷時(shí)進(jìn)行不確定的行為。
private class Itr implements Iterator<E>{
int expectedModCount = modCount; //遍歷前獲取當(dāng)前l(fā)ist 的 修改數(shù)
···
//實(shí)現(xiàn)Iterator接口定義的方法
···
final void checkForComodification() {
//當(dāng)Iterator中的存儲(chǔ)的 修改數(shù) 與 list 中的修改數(shù)不一致時(shí)椒功,說明遍歷時(shí)捶箱,list結(jié)構(gòu)發(fā)生了改變
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
當(dāng)多線程操作同一容器的時(shí)候,一個(gè)線程通過Iterator遍歷容器的同時(shí)动漾,另一個(gè)線程修改了改容器的內(nèi)容(add丁屎、remove等操作),Iterator及時(shí)拋出異常旱眯,防止發(fā)生 不確定性 的 行為晨川。