要點
- 匿名類的概念和用法
- 語言規(guī)范以及語言的橫向?qū)Ρ鹊?/li>
- 內(nèi)存泄漏的切入點
總結(jié)
- 沒有人類認(rèn)知意義上的名字
- 只能繼承一個父類或?qū)崿F(xiàn)一個接口
- 父類是非靜態(tài)的類型六荒,則需父類外部實例來初始化
- 如果定義在非靜態(tài)作用域內(nèi),會引用外部類實例
- 只能捕獲外部作用域內(nèi)的final變量
- 創(chuàng)建時只有單一方法的接口可以用Lambda轉(zhuǎn)換
a.匿名內(nèi)部類的名字
表面上是沒有引用名的矾端,
但其實是有用于定位的“名字”掏击,
new Foo()在定義的時候秩铆,
重寫了bar()這個方法砚亭,
如此一來
new Foo(){...}
這里就是一個匿名內(nèi)部類
了;吶這個匿名內(nèi)部類殴玛,實際上在
字節(jié)碼
中是會定義出來的捅膘,!9鏊凇篓跛!定義出來一個用于定位的“名字”,
這個“名字”可見上面代碼的第二行坦刀,
“
com.bennyhuo.iiv.ch1.
”即代碼包名
愧沟,“
OuterClass$1
”即外部內(nèi)名
$1
,1
代表這個匿名內(nèi)部類
鲤遥,是前綴的外部類中沐寺,定義的第一個匿名內(nèi)部類,
再創(chuàng)建第二個匿名內(nèi)部類 就是
$2
了盖奈;所以
匿名內(nèi)部類
跟普通類
一樣混坞,是可以加載
出來的!8痔埂究孕!只不過
參數(shù)格式
不一樣,普通類
是“class 類名
”匿名內(nèi)部類
是“class 包名.外部類名$num
”b.匿名內(nèi)部類的繼承結(jié)構(gòu)
-
匿名內(nèi)部類
被創(chuàng)建的時候爹凹,
就默認(rèn)匿名內(nèi)部類
是作為一個子類
去繼承
其對應(yīng)的父類
了:(接口亦同)
- 但是下面這種類型厨诸,
既 繼承某個父類 又 實現(xiàn)某個接口 的“匿名內(nèi)部類” 的 這種類型,
在Java中是不被接受的禾酱,
因為這其實是一種“或類型”微酬,
即Runnable或上Foo的結(jié)果,作為一種類颤陶,
這在Java中是不被接受的:
**即使使用Java 10 的var關(guān)鍵字來定義颗管,
- 只能繼承一個父類或?qū)崿F(xiàn)一個接口
>- 父類是非靜態(tài)的類型,則需父類外部實例來初始化
>- 如果定義在非靜態(tài)作用域內(nèi)滓走,會引用外部類實例
>- 只能捕獲外部作用域內(nèi)的final變量
>- 創(chuàng)建時只有單一方法的接口可以用Lambda轉(zhuǎn)換
####a.匿名內(nèi)部類的名字
表面上是沒有引用名的垦江,
但其實是有用于定位的“名字”,
如上代碼搅方,
new Foo()在定義的時候比吭,
重寫了bar()這個方法茬斧,
如此一來new Foo(){...}
這里就是一個匿名內(nèi)部類
了;
吶這個匿名內(nèi)部類梗逮,實際上在字節(jié)碼
中是會定義出來的,P辶铩?锻!
定義出來一個用于定位的“名字”怖喻,
這個“名字”可見上面代碼的第二行底哗,
“com.bennyhuo.iiv.ch1.
”即代碼包名
,
“OuterClass$1
”即外部內(nèi)名
$1
锚沸,
1
代表這個匿名內(nèi)部類
跋选,
是前綴的外部類中,定義的第一個匿名內(nèi)部類哗蜈,
再創(chuàng)建第二個匿名內(nèi)部類 就是$2
了前标;
所以匿名內(nèi)部類
跟普通類
一樣,是可以加載
出來的>嗯恕A读小!
只不過參數(shù)格式
不一樣音比,
普通類
是“class 類名
”
匿名內(nèi)部類
是“class 包名.外部類名$num
”
####b.匿名內(nèi)部類的繼承結(jié)構(gòu)
-匿名內(nèi)部類
被創(chuàng)建的時候俭尖,
就默認(rèn)匿名內(nèi)部類
是作為一個子類
去繼承
其對應(yīng)的父類
了:(接口亦同)
- 但是下面這種類型,
既 繼承某個父類 又 實現(xiàn)某個接口 的“匿名內(nèi)部類” 的 這種類型洞翩,
在Java中是不被接受的稽犁,
因為這其實是一種“或類型”,
即Runnable或上Foo的結(jié)果骚亿,作為一種類已亥,
這在Java中是不被接受的:
>>即使使用Java 10 的var關(guān)鍵字來定義,
也是不行的来屠,
這種類型還是不能被接受:
(在處理 var時陷猫,編譯器先是查看表達(dá)式右邊部分,
也就是所謂的構(gòu)造器的妖,并將它作為變量的類型绣檬,然后將該類型寫入字節(jié)碼當(dāng)中)
嗯,
可是如果實在是想實現(xiàn)一個
如既 繼承某個父類 又 實現(xiàn)某個接口 的“匿名內(nèi)部類”
這樣的類型嫂粟,
但要不想占用太多資源
娇未,要求同匿名內(nèi)部類
一樣用完即銷毀
,怎么辦星虹?
那別用匿名內(nèi)部類
唄零抬,
在方法體內(nèi)部
實現(xiàn)即可镊讼,!F揭埂蝶棋!
便可以在方法調(diào)用完畢后將其回收
,
也可以達(dá)到需求
:
另外忽妒,Kotlin是可以實現(xiàn)玩裙,
既 繼承某個父類 又 實現(xiàn)某個接口 的“匿名內(nèi)部類” 的 這種類型的
:
(object類似于class與定義一個引用,
object與后面冒號之間不接名字表示匿名段直,
冒號后面要繼承什么吃溅,實現(xiàn)什么,直接寫出來就是了)
c.匿名內(nèi)部類的構(gòu)造方法(關(guān)注:匿名內(nèi)部類
對外部類
的引用
)
匿名內(nèi)部類會有外部類的引用鸯檬,
這個可能導(dǎo)致內(nèi)存泄漏决侈!匿名內(nèi)部類
的構(gòu)造方法
是編譯器
幫忙定義
的!P瘛赖歌!
開發(fā)者沒有權(quán) 定義匿名內(nèi)部類
的構(gòu)造方法
;
編譯器 會 根據(jù)代碼 為 匿名內(nèi)部類
的構(gòu)造方法
引入一些參數(shù)功茴,
如下面圖中例子俏站,
(右上)有一個OuterClass
,里邊有一個InnerClass
痊土,
(左上)這時候在Client類中肄扎,
new出來一個匿名內(nèi)部類
,
匿名內(nèi)部類——父類非靜態(tài)赁酝、所在方法(匿`類被new出來的位置所處的方法)非靜態(tài)
例子中這個new出來的匿名內(nèi)部類
犯祠,
實際上它的父類就是InnerClass
,
而InnerClass本身是一個非靜態(tài)的內(nèi)部類
酌呆,
:庠亍!O对L涤椤!非靜態(tài)的內(nèi)部類本身就會引用外部類的實例F惺铡@嬲觥!D榷F潞亍!
,
所以O(shè)uterClass()的實例也會在這里(左上第四行)new出來遍坟;
而下方的Client$1
就是上述所說的匿名內(nèi)部類的類型
了拳亿,
Client$1
的命名格式其實就是剛剛說的外部內(nèi)名$匿名內(nèi)部類序號
;
所以圖中下方代碼塊的第二行愿伴,
即編譯器
根據(jù)代碼 為 匿名內(nèi)部類
生成的構(gòu)造方法
肺魁,
其第一個參數(shù)
,就是Client隔节,即匿名內(nèi)部類所在方法
對應(yīng)的 外部作用域
(最外部類)鹅经;
因為這里的匿名內(nèi)部類所在的方法
是非靜態(tài)方法
,
所以一定是被某個實例(最外部類實例)
引用
著的9倭薄!C烈辍9艉纭!
其第二個參數(shù)
呢诬,即匿名內(nèi)部類的父類
涌哲,
這個父類
如果是某外部類
的非靜態(tài)內(nèi)部類
,
那把這個對應(yīng)的外部類實例
傳進(jìn)來即可尚镰,
因為這個外部類實例
可以應(yīng)用到其成員
(包括非靜態(tài)內(nèi)部類
)阀圾;
匿名內(nèi)部類——父類靜態(tài)、所在方法非靜態(tài)
interface
跟靜態(tài)內(nèi)部類
的效果是差不多的狗唉,
就是靜態(tài)
的初烘,
也就是不會去引用其外部類的實例!7指I隹稹!缸剪!
所以這時候匿名內(nèi)部類
的構(gòu)造方法
的參數(shù)
就只有一個所在方法的最外部類實例
了吗铐;
匿名內(nèi)部類--父類靜態(tài)、所在方法靜態(tài)
而杏节,當(dāng)匿名內(nèi)部類所在的方法是靜態(tài)的唬渗,
則其構(gòu)造方法的參數(shù)中,
不存在所在方法的最外部類實例
了奋渔;
即镊逝,
匿名內(nèi)部類
的構(gòu)造方法
的參數(shù)個數(shù)
,
由其父類
以及其所在方法
是否靜態(tài)
決定嫉鲸,
父類非靜態(tài)
蹋半,則需傳入父類相關(guān)實例
;
所在方法非靜態(tài)
,則需傳入所在方法的最外部類實例
减江;
反則染突,
哪個靜態(tài)了,就不用傳哪個辈灼;
00 01 10 11
捕獲 匿名內(nèi)部類 所在方法內(nèi)
(外部作用域) 的 局部變量
快照的情況
匿名內(nèi)部類
重寫父類方法
時份企,引用到的外部方法體內(nèi)
的局部final變量
通常,要求要被捕獲的局部變量
需要是final
修飾的巡莹;
雖然說如果不final
的話司志,
對匿名內(nèi)部類
的構(gòu)造方法
也不是很有影響
,
因為傳給匿名內(nèi)部類構(gòu)造方法
的這個局部變量實例
降宅,
實際上只是捕獲局部變量實例
的一個快照
骂远,
快照
即復(fù)制一份引用
,給匿名內(nèi)部類
的構(gòu)造方法
去使用腰根,
但是激才,
如果這個局部變量實例
不是final
的,
那局部變量實例
被重新賦值
了额嘿,
外部的局部變量實例
跟傳給匿名內(nèi)部類構(gòu)造方法
的局部變量實例
瘸恼,就不一樣了!
這個肯定是不行的吧册养,
所以东帅,
Java要求,
這個地方(圖中左邊球拦,第三行靠闭,也即被傳給 匿' 構(gòu)造方法
的這個 局部實例
)一定
要是final
的,
原因就是為了讓這個局部變量實例
坎炼,在外部和在 匿’ 構(gòu)造方法
中阎毅,
給保持一致
!
匿名內(nèi)部類的構(gòu)造方法小結(jié)
是編譯器生成的
參數(shù)列表包括
外部對象(定義在非靜態(tài)域內(nèi))<如上的Client>
父類的外部對象(父類非靜態(tài))<如上的OuterClass>
父類的構(gòu)造方法參數(shù)(父類有構(gòu)造方法且參數(shù)列表不為空)
外部捕獲的變量(方法體內(nèi)有引用外部final變量)<如上的Object>
事實上是可以通過
反射
点弯,
去修改匿名內(nèi)部類
的構(gòu)造方法
持有的外部引用
(參數(shù)列表
)的
Lambda轉(zhuǎn)換(SAM轉(zhuǎn)換)
-
SAM--single abstract method 單一方法類型
一個接口扇调,只有一個抽象方法時,可以用Lambda表達(dá)式替換實現(xiàn)抢肛;
關(guān)注語言版本的變化
- 體現(xiàn)對技術(shù)的熱情
- 體現(xiàn)好學(xué)的品質(zhì)
- 顯得專業(yè)