文本已收錄至個人GitHub倉庫,歡迎Star: https://github.com/bin3923282...
種一棵樹最好的時間是十年前,其次是如今
我知道不少人不玩 qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼: 549684836 鼓勵你們在技術的路上寫博客
看了不少源碼,都有用到內部類,可是本身之前在生產環境上,用的確實少,也有用過可是不多,因此今天就打算好好的把它從頭至尾的過一遍。
可能我寫文章有點亂,可是我是發現本身少了啥,我就補啥,若是是寫系列的話,就確定是從頭至尾的
能夠將一個類的定義放在裏另外一個類的內部,這就是內部類,所謂的內部類的概念只是出如今編譯階段,對於jvm層是沒有內部類這個概念的。咱們能夠利用內部類來解決git
內部類能夠分爲:github
非靜態內部類又能夠分爲:編程
我感受這個是用的最多的,你好比說Redis的key的設計, 由於咱們要中間拼接:號,因此用靜態內部類去組成不一樣的key是很是好的,這樣可讓相同類型的key再同一個文件目錄下jvm
靜態內部類的定義和普通的靜態變量或者靜態方法的定義方法是同樣的,使用static關鍵字,只不過此次static是修飾在class上的,通常而言,只有靜態內部類才容許使用static關鍵字修飾,普通類的定義是不能用static關鍵字修飾的,這一點須要注意一下。下面定義一個靜態內部類:ide
public class Out { private static String name; private int age; public static class In{ private int age; public void sayHello(){ System.out.println("my name is : "+name); //--編譯報錯--- //System.out.println("my age is :"+ age); } } }
在上述代碼中,In這個類就是一個靜態內部類。咱們說內部類是能夠訪問外部類的私有字段和私有方法的,對於靜態內部類,它遵循一致的原則,只能訪問外部類的靜態成員。上述代碼中,外部類的非靜態私有字段age在靜態內部類中使不容許訪問的,而靜態字段name則是可訪問的。下面咱們看,如何建立一個靜態內部類的實例對象。函數式編程
public static void main(String [] args){ Out.In innerClass = new Out.In(); innerClass.sayHello(); }
使用場景,通常來講,對於和外部類聯繫緊密可是並不依賴於外部類實例的狀況下,能夠考慮定義成靜態內部類。下面咱們看稍顯複雜的成員內部類。函數
咱們說了,四種不一樣類型的內部類都各自有各自的使用場景,靜態內部類適合於那種和外部類關係密切可是並不依賴外部類實例的狀況。可是對於須要和外部類實例相關聯的狀況下,能夠選擇將內部類定義成成員內部類。如下代碼定義了一個簡單的成員內部類:學習
public class Out { private String name; public void showName(){ System.out.println("my name is : "+name); } public class In{ public void sayHello(){ System.out.println(name); Out.this.showName(); } } }
以上定義了一個簡單的內部類In,咱們的成員內部類能夠直接訪問外部類的成員字段和成員方法,由於它是關聯着一個外部類實例的。下面咱們看看在外部是如何建立該內部類實例的。優化
public static void main(String [] args){ Out out = new Out(); out.setName("六脈神劍") Out.In in = out.new In(); in.sayHello(); }
由於成員內部類是關聯着一個具體的外部類實例的,因此它的實例建立必然是由外部類實例來建立的。對於實例的建立,咱們只須要記住便可,成員內部類的實例建立須要關聯外部類實例對象,靜態內部類實例建立相對簡單。下面咱們主要看看在編譯階段編譯器是如何保持內部類對外部類成員信息可訪問的。this
使用場景,對於那種要高度依賴外部類實例的狀況下,定義一個成員內部類則會顯的更加明智。
方法內部類,顧名思義,定義在一個方法內部的類。方法內部類相對而言要複雜一些,下面定義一個方法內部類:
public class Out { private String name; public void sayHello(){ class In{ public void showName(){ System.out.println("my name is : "+name); } } In in = new In(); in.showName(); } }
咱們定義了一個類,在該類中又定義了一個方法sayHello,然而在該方法中咱們定義了一個內部類,類In就是一個方法內部類。咱們的方法內部類的生命週期不超過包含它的方法的生命週期,也就是說,方法內部類只能在方法中使用。因此在聲明的時候,任何的訪問修飾符都是沒有意義的,因而Java乾脆不容許使用任何的訪問修飾符修飾方法內部類。其中還須要注意一點的是,定義和使用時兩回事,別看那一大串定義類的代碼,你實際想要使用該類,就必須new對象,而對於方法內部類而言,只能在方法內部new對象。這就是方法內部類的簡單介紹,下面咱們看看其實現原理。
有關方法內部類的實現原理實際上是和成員內部類差不太多的,也是在內部類初始化的時候爲其傳入一個外部類實例,區別在哪呢?就在於方法內部類是定義在具體方法的內部的,因此該類除了能夠經過傳入的外部實例訪問外部類中的字段和方法,對於包含它的方法中被傳入的參數也會隨着外部類實例一塊兒初始化給內部類。
毋庸置疑的是,方法內部類的封裝性比以前介紹的兩種都要完善。因此通常只有在須要高度封裝的時候纔會將類定義成方法內部類。
可能內部類的全部分類中,匿名內部類的名號是最大的,也是咱們最經常使用到的,多見於函數式編程,lambda表達式等。下面咱們重點看看這個匿名內部類。
匿名內部類就是沒有名字的內部類,在定義完成同時,實例也建立好了,經常和new關鍵字緊密結合。固然,它也不侷限於類,也能夠是接口
,能夠出如今任何位置。下面咱們定義一個匿名內部類:
若是您必須重寫類或接口的方法,則應該使用它。能夠經過兩種方式建立Java匿名內部類
//首先定義一個普通類 public class Out { private String name; public void sayHello(){ System.out.println("my name is :" + name); } }
//定義和使用一個匿名內部類 public static void main(String [] args){ Out out = new Out(){ @Override public void sayHello(){ System.out.println("my name is cyy"); } public void showName(){ System.out.println("hello single"); } }; out.sayHello(); }
從上述代碼中能夠很顯然的讓咱們看出來,咱們的匿名內部類一定是要依託一個父類的,由於它是沒有名字的,沒法用一個具體的類型來表示。因此匿名內部類每每都是經過繼承一個父類,重寫或者從新聲明一些成員來實現一個匿名內部類的定義。實際上仍是利用了裏式轉換原理。
其實在看了上述三種內部類的原理以後,反而以爲匿名內部類的實現較爲簡單了。主要思路仍是將內部類抽離出來,經過初始化傳入外部類的實例以達到對外部類全部成員的訪問。只是在匿名內部類中,被依託的父類不是他的外部類。匿名內部類的主要特色在於,沒有名字,對象只能被使用一次,能夠出如今任意位置。因此它的使用場景也是呼之欲出,對於一些對代碼簡潔度有所要求的狀況下,可首選匿名內部類。
以上完成了對四種內部類的簡單介紹,對於他們各自實現的原理也都已經介紹過了。其實大體相同,因爲jvm對每一個類都要求一個單獨的源碼文件,因此編譯階段就完成了分離的操做,可是在分離的過程當中又要保持內部類和外部類之間的這種聯繫,因而編譯器添加了一些接口保持這種信息共享的結構。使用內部類能夠大大增長程序的封裝性,使得代碼總體簡潔度較高。
講完這個後面的函數式接口 引用就好講一點了
內部類就講那麼多,但願你們之後看源碼會輕鬆點,哈哈
好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是 人才。創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見
六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !