Java 中的內部類
前言
在第一次把Java 程式設計思想中的內部類這一章擼完後,有點印象。大概知道了什麼時內部類,區域性內部類,匿名內部類,巢狀內部類。隨著時間的推移,自己慢慢的就忘記了,總感覺自己思考的東西不多,於是
看了第二遍,並把自己的想法和一些筆記寫下來。供以後參考。
內部類
定義:如果把A類定義再B類裡面,那麼把A類叫做 內部類
程式碼如下:
public class B { public class A{} }
這樣看內部類是不是感覺很簡單?定義確實很簡單,但是思考一下,這樣定義一個內部類有什麼意義嗎?或者說能帶來什麼好處? 上面那樣定義,個人感覺是意義不大。所以 ,我們一般定義內部類,都是需要內部類實現一個介面或著抽象類。有實際意義的程式碼如下(例子來自,java程式設計思想):
/** * @ClassName Selector * @Description 選擇器 * @Author ouyangkang * @Date 2019-03-12 14:21 **/ public interface Selector { // 是否結束 boolean end(); // 當前資料 Object current(); // 下一個節點 void next(); }
/** * @ClassName Squence * @Description TODO * @Author ouyangkang * @Date 2019-03-12 14:24 **/ public class Squence { private Object[] items; private int next; public Squence(int size) { items = new Object[size]; } public void add(Object x) { if (next < items.length) { items[next++] = x; } } private class SequceneSelector implements Selector { private int i; @Override public boolean end() { return i == items.length; } @Override public Object current() { return items[i]; } @Override public void next() { if (i < items.length) { i++; } } } public Selector selector(){ return new SequceneSelector(); } } class Test{ public static void main(String[] args) { Squence squence = new Squence(10); for (int i = 0; i < 10 ; i++) { squence.add(i); } // 呼叫內部類這是迭代器模式的一個例子。 通過內部類 ,訪問類中的私有屬性。 Selector selector = squence.selector(); while (!selector.end()){ System.out.print(selector.current()+" "); selector.next(); } } }
輸出: 0 1 2 3 4 5 6 7 8 9
請仔細檢視上面程式碼。 這是個非常好的例子。訪問許可權為private
的內部類SequceneSelector
實現了Selector
介面 ,該內部類可以訪問外部類Squence
中私有屬性items
。 並提供一個公開的方法selector
,返回一個,向上轉型為Selector
型別。 在測試程式碼中。先建立squence
物件。 往裡面新增10個元素。 並呼叫該物件的中的selector()
方法,返回一個Selector
型別的物件。 根據我們定義的Selector
介面中方法的含義,編碼。列印輸出。
上面程式碼說明了內部類的幾個好處:
- 隱藏了細節,實現一個介面,向上轉型。
- 可以訪問外部類中的所有私有屬性,方法。就像是擁有他們一樣。但是不是擁有(你可以把它想成一個成員方法)
我覺得第一點沒什麼好說的,反倒是第二點,自己是這樣理解的:外部類就像是一個房子,裡面的成員變數,方法,內部類。就像是房子裡面的人。可以相互通訊。而內部類實現了一個介面或著抽象類後,就有點像細作一樣,表面看起來是房子裡面的人,其實真正是外面的人。只要我建立它,並通過向上轉型,就可以到外面去通訊。
區域性內部類
定義: 如果把A類定義再B類的方法中,那麼把A類叫做區域性內部類
程式碼如下:
public class A { private void getB(){ class B{} } }
其實上面程式碼意義並不大。 下面看下一些有意義的程式碼。程式碼如下:
/** * @InterfaceName Destination * @Description TODO * @Author ouyangkang * @Date 2019-03-12 19:59 **/ public interface Destination { String readLabel(); }
/** * @ClassName Parcel * @Description TODO * @Author ouyangkang * @Date 2019-03-12 20:00 **/ public class Parcel { public Destination destination(String str){ class PDestination implements Destination{ private String label = str; public PDestination(String label){ this.label = label; } @Override public String readLabel() { return label; } } return new PDestination(str); } public static void main(String[] args) { Parcel parcel = new Parcel(); Destination destination = parcel.destination("hello"); System.out.println(destination.readLabel()); } }
輸出 hello
定義一個為Destination
的介面,方法為readLabel()
。 Parcel 類中定義了一個 返回Destination
型別的方法。 該方法中定義了一個PDestination
類,並實現了Destination
介面。 在最後返回PDestination
的物件。 上面區域性內部類很熟悉把。下面,我們看下匿名內部類。
匿名內部類
將上面Parcel類修改 ,程式碼如下
/** * @ClassName Parcel1 * @Description TODO * @Author ouyangkang * @Date 2019-03-13 15:33 **/ public class Parcel1 { public Destination destination(final String str){ return new Destination() { private String label = str; @Override public String readLabel() { return label; } }; } public static void main(String[] args) { Parcel1 parcel1 = new Parcel1(); Destination ouyangkang = parcel1.destination("hello"); System.out.println(ouyangkang.readLabel()); } }
輸出:hello
類Parce1
中的destination()
方法直接new
了一個Destination
物件,重寫該readLabel()
方法。 這樣返回沒有名字的區域性內部類類,稱為匿名內部類。
區域性內部類 VS 匿名內部類
在使用區域性內部類或著匿名內部類的時候,要使用外部類中的區域性變數是,該變數要為final ,要問為為什麼,因為區域性內部類或著匿名內部類內部對區域性變數操作並不會改變改內容,所以為了防止使用錯誤。就用final修飾。不可變。其根本原因就是不會有任何的變化。
那麼什麼時候用區域性內部類,什麼時候用匿名內部類?
在你需要一個已命名的構造器,或著需要重構構造器。需要的不止是該一個內部類物件。就是你要定義多個構造器的時候用區域性內部類。如果不需要就用匿名內部類。
JAVA 8 可以用Lambda
表示式表示
上面Parcel1
類 用 JAVA 8 編碼如下
public class Parcel2 { public Destination destination(final String str) { return ()->{ String label = str; return label; }; } public static void main(String[] args) { Parcel2 parcel2 = new Parcel2(); Destination ouyangkang = parcel2.destination("hello"); System.out.println(ouyangkang.readLabel()); } }
輸出:hello
巢狀類
如果把A類定義在B類中,並A類用static關鍵字修飾,那麼把A叫做巢狀類。
程式碼如下:
publc class B{ static class A{} }
如果不需要內部類和外部類有關係,就把該內部類宣告為static
。
建立巢狀類代表:
- 並不需要外圍類物件
- 不能從巢狀類的物件中訪問外部類中的 非靜態的東西。
我一般用巢狀類來做測試類。巢狀類理解就到此為止了
總結
總的來說,Java 中的 內部類並沒有想象的的那麼難理解和認知,但是內部類使用起來就比較深奧了,其中多型這一特性在,配合著內部類,可謂說賊強了。隱藏細節,關注介面中方法本身的意思。