溫故而知新-Java 提升篇

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é)論:

  1. 淺克旅占怠:基本類型是可以被克隆的,但引用類型只是copy地址,并沒有copy這個(gè)地址指向的對(duì)象的值,這使得兩個(gè)地址指向同一值,修改其中一個(gè),當(dāng)然另一個(gè)也就變了.淺克隆只適合克隆基本類型,對(duì)于引用類型就不能實(shí)現(xiàn)克隆了.
  • 通過implements Cloneable 重寫 clone方法實(shí)現(xiàn)

  1. 可以用序列化與反序列化實(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ì)將此字符串放入常量池中后, 再返回

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ū)兩大類


    • 線程私有
      1. 程序計(jì)數(shù)器:記錄正在執(zhí)行的虛擬機(jī)指令碼的地址
      2. 虛擬機(jī)棧:方法執(zhí)行的內(nèi)存區(qū),每個(gè)方法執(zhí)行時(shí)會(huì)在虛擬機(jī)棧中創(chuàng)建棧幀
      3. 本地方法區(qū):虛擬機(jī)的Native方法執(zhí)行的內(nèi)存區(qū)
    • 共享數(shù)據(jù)區(qū)
      1. Java堆:對(duì)象分配內(nèi)存的區(qū)域
      2. 方法區(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 類的信息
  • 類的信息 包括:
    1. Class 對(duì)象
      Class.forName()必須提供一個(gè)類的全名
      MyObject.class前提知道類名
    2. 類名
      getName()獲取類的全限定類名(包含包名)
      getSimpleName()獲取類名(不包含包名)
    3. 修飾符
      getModifiers()獲取類的修飾符,使用java.lang.reflect.Modifier類中的方法來檢查修飾符的類型
      Modifier.isPrivate(int modifiers);
    4. 包信息
      getPackage()獲取包信息
    5. 父類
      getSuperclass()訪問類的父類
    6. 實(shí)現(xiàn)的接口
      getInterfaces()獲取類所實(shí)現(xiàn)的接口集合(只有實(shí)現(xiàn)了接口才返回)
    7. 構(gòu)造器
      getConstructors()獲取類的構(gòu)造方法
    8. 方法
      getMethods()獲取類的所有方法
    9. 變量
      getFields()獲取類的所有成員變量
    10. 注解
      getAnnotations()獲取類的所有注解
  • 代理(參考鏈接


    代理類主要負(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):
      1. 一個(gè)接口只服務(wù)于一種類型的對(duì)象郭怪,代理類增多時(shí),會(huì)造成重復(fù)代碼
      2. 如果接口增加一個(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)步驟
      1. 實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器
      2. 給Proxy類提供ClassLoader和代理接口類型數(shù)組創(chuàng)建動(dòng)態(tài)代理類
      3. 以調(diào)用處理器類型為參數(shù)稳懒,利用反射機(jī)制得到動(dòng)態(tài)代理類的構(gòu)造函數(shù)
      4. 以調(diào)用處理器對(duì)象為參數(shù),利用動(dòng)態(tài)代理類的構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類對(duì)象
> 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)代理類的模式
      1. 查找A上的所有非final 的public類型的方法定義;
      2. 將這些方法的定義轉(zhuǎn)換成字節(jié)碼叫编;
      3. 將組成的字節(jié)碼轉(zhuǎn)換成相應(yīng)的代理的class對(duì)象蜕琴;
      4. 實(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)成直接引用;
    • 初始化
      激活類的靜態(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ā)生 不確定性 的 行為晨川。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市删豺,隨后出現(xiàn)的幾起案子共虑,更是在濱河造成了極大的恐慌,老刑警劉巖呀页,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妈拌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蓬蝶,警方通過查閱死者的電腦和手機(jī)供炎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門渴逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人音诫,你說我怎么就攤上這事惨奕。” “怎么了竭钝?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵梨撞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我香罐,道長(zhǎng)卧波,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任庇茫,我火速辦了婚禮港粱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旦签。我一直安慰自己查坪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布宁炫。 她就那樣靜靜地躺著偿曙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪羔巢。 梳的紋絲不亂的頭發(fā)上望忆,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音竿秆,去河邊找鬼启摄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛幽钢,可吹牛的內(nèi)容都是我干的歉备。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼搅吁,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼威创!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谎懦,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤肚豺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后界拦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吸申,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了截碴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梳侨。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖日丹,靈堂內(nèi)的尸體忽然破棺而出走哺,到底是詐尸還是另有隱情,我是刑警寧澤哲虾,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布丙躏,位于F島的核電站,受9級(jí)特大地震影響束凑,放射性物質(zhì)發(fā)生泄漏晒旅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一汪诉、第九天 我趴在偏房一處隱蔽的房頂上張望废恋。 院中可真熱鬧,春花似錦扒寄、人聲如沸鱼鼓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚓哩。三九已至构灸,卻和暖如春上渴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喜颁。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工稠氮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人半开。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓隔披,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親寂拆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奢米,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容