摘要:但遠不止如此,內部類了解外部類,并能與之通信。內部類還擁有其外圍類的所有元素的訪問權。普通內部類內不能有域和方法。特點普通內部類對象隱式的保存了外部類對象,但嵌套類并非如此。局部內部類可以訪問當前代碼塊內的常量,以及此外圍類的成員。
點擊進入我的博客
可以把一個類的定義放在另一個類的定義內部,這就是內部類。
Java最晦澀的部分之一。
內部類看起來就像是一種代碼隱藏機制,將類只與其他類的內部。但遠不止如此,內部類了解外部類,并能與之通信。
創建內部類的方式就如同你想的一樣——把類的定義置于外圍類的里邊
10.2 鏈接到外部類當生成一個內部類的對象時,此對象與制造它的外部對象之間就有了一種聯系,所以它能訪問其外圍對象的所有成員。
內部類還擁有其外圍類的所有元素的訪問權。當某個外部類當對象創建了一個內部類對象時,此內部類對象必定會秘密地捕獲一個指向那個外部類對象當引用。當你訪問外部類成員時,就是用那個引用來選擇外部類當成員。
10.3 使用.this與.new如果你需要生成對外類對象的引用,可以使用外部類.this。這樣產生的引用自動地具有正確類型,這一點在編譯期就被知曉并收到檢查,因此沒有任何運行開銷。
通過外部類的對象創建內部類(非static內部類)對象,可以通過外部類.new創建內部類。
public class Outer { void func() { System.out.println("Test"); } class Inner { void func() { System.out.println("Inner"); // .this語法 Outer.this.func(); } } public static void main(String[] args) { // .new語法 new Outer().new Inner().func(); } }10.4 內部類與向上轉型
內部類的優點:可以更好的隱藏細節
特點:
外部類可以訪問內部類的所有元素,無論什么修飾符。
普通內部類內不能有static域和方法。
一個內部類可以被嵌套多層,而且可以訪問所有外圍類的成員。
10.5 局部內部類可以在一個方法或任意作用域內定義內部類,成為局部內部類。這么做的理由:
你實現了某類型的接口,于是可以創建并返回對其的引用
你要解決一個復雜的問題,想創建一個類來復制你的解決方案,但又不希望這個類是公共可用的。
public class Outer { public void func() { // 方法內部的內部類 class InnerMethod { void func() { System.out.println("class in method"); } } new InnerMethod().func(); } public void f() { if(true) { // 作用域內部的內部類 class InnerScope { void func() { System.out.println("class in scope"); } } new InnerScope().func(); } } public static void main(String[] args) { new Outer().func(); new Outer().f(); } }
局部內部類類似方法的局部變量,所以在類外或者類的其他方法中不能訪問這個內部類。但這并不代表局部內部類的實例和定義了它的方法中的局部變量具有相同的生命周期。
可以在同一個子目錄(包)下起一個跟局部內部類相同的類,不會有沖突。
InnerScope類被嵌套到if語句中,這并不是說該類到創建是有條件的,他跟其他的類一樣被編譯過了。
因為不存在外部可見性,局部內部類不能用權限修飾符。
不能在局部內部類中使用可變的局部變量,可以使用final的局部變量。
可以訪問外圍類的成員變量。如果是static方法,則只能訪問static修飾的成員變量。
可以使用final或abstract修飾。
10.6 匿名內部類inner()方法將返回值的生成與表示這個返回值的類定義結合在一起,而且這個類沒有名字。
創建一個繼承某個類(或者實現某個接口)的匿名類對象。
public class Outer { private final String outerStr = "Outer"; class Inner { public Inner(String str) { System.out.println("Inner Constructor " + str); } public void func() { System.out.println("Inner"); } } public Inner inner() { return new Inner("Dota") { { // 跟構造方法一樣初始化 str3 = "LOL"; } private String str1 = Outer.this.outerStr; private String str2 = outerStr; private String str3; @Override public void func() { System.out.println(str1); System.out.println(str2); System.out.println(str3); } }; } public static void main(String[] args) { new Outer().inner().func(); } }
返回的類型被自動向上轉型成Inner的引用。
如果構造方法帶參數,也可以在new Inner()中傳遞參數給基類的構造器。
在匿名內部類定義字段時,可以初始化。
在匿名內部類使用外部類的對象時,只能使用final的。
str1和str2是一樣的
匿名內部類沒有名字,也就沒有構造器,但是可以通過實例初始化模擬構造器。但是你不能重載實例初始化方法,所以只能有一個這樣的構造器。
變量str不要求是final的,因為str是傳遞給基類的構造器的,匿名內部類無法使用。
匿名內部類可以繼承類或者實現接口,但不能兩者兼得。
10.6.1 再訪工廠方法代碼更加簡潔
10.7 嵌套類(靜態內部類)如果不需要內部類對象與其外圍對象之間有聯系,那么可以將內部類聲明為static。
普通內部類對象隱式的保存了外部類對象,但嵌套類并非如此。
要創建嵌套類的對象,并不需要外部類的對象
不能從嵌套類對象中訪問外部類的非靜態對象。
普通內部類不能有static域和static方法,但嵌套類可以有。
10.7.1 接口內部的類嵌套類可以作為接口的一部分,還可以實現其外部接口。
如果你想創建某些公共代碼,使得它們可以被某個接口的所有不同實現所共用,那么使用接口內部的嵌套類會很方便。
可以使用嵌套類的main方法來實現調試。
10.7.2 多層嵌套一個內部類可以嵌套多層
一個嵌套類也可以被嵌套多層。
10.8 為什么需要內部類外部類可以有多個內部類,每個內部類都能獨立的繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對于內部類都沒有影響。
接口解決類部分“多重繼承”,內部類補充的實現了“多重繼承。
內部類可以有多個實例,每個實例都有自己的狀態信息,并且與外圍類對象的信息相互獨立。
再單個外圍類中,可以讓多個內部類以不同的方式實現同一個接口。
創建內部類對象的時候并不一定依賴外部類對象的創建。
內部類并沒有令人迷惑的“is-a”關系,他就是一個獨立的實體。
10.8.1 閉包與回調閉包(closure)是一個可調用的對象,它記錄了一些信息,這些信息來自于創建它的作用域。
通過上述定義,可以看出內部類就是面向對象的閉包,因為它不僅包含外圍類對象(創建內部類的作用域)的信息,還自動擁有一個指向此外圍類對象的引用,在此作用域內,內部類有權操作所有成員。
通過內部類實現閉包的功能是優良的解決方案,它比指針更靈活、更安全。
回調函數的定義:在計算機程序設計中,回調函數是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼的引用。這一設計允許了底層代碼調用在高層定義的子程序。
非回調函數的場景:一個程序B有一個方法b(),要調用程序A中的另一個方法a()。這個很簡單,只需要在程序B的方法b()中new A().a()就可以了。
回調函數:跟上述一樣,但是程序A中的方法a()在完成任務后,還會調用一個預定義好的回調函數;B在方法b()中,可以按照預定義好的回調函數接口實現相關邏輯,然后把這段邏輯傳遞給A,這樣在B.b()調用A.a()的時候,就會執行這段邏輯。
// A定義好的回調接口 interface Callback { void callback(); } // A定義 public class A { Callback callback; public A(Callback callback) { this.callback = callback; } public void a() { System.out.println("a"); callback.callback(); } } class B { public static void main(String[] args) { A a = new A(new Callback() { @Override public void callback() { System.out.println("callback"); } }); a.a(); } } // Output: a callback10.8.2 內部類與控制框架
應用程序框架就是被設計用來解決某類特定問題的一個或者一組類。
要運用某個應用程序框架,通常是繼承一個或多個類,并覆蓋某些方法。在覆蓋后的方法中,編寫代碼定制應用程序框架提供的通用解決方案(這是模板方法的一個例子)。
控制框架是一類特殊的應用程序框架,他用來解決響應事件的需求。主要用來響應事件的系統被稱為事件驅動系統。
public class Test { private boolean light; private boolean water; class LightEvent extends SwitchEvent { @Override public void on() { light = true; } @Override public void off() { light = false; } } class WaterEvent extends SwitchEvent { @Override public void on() { water = true; } @Override public void off() { water = false; } } } abstract class SwitchEvent { public abstract void on(); public abstract void off(); }
上述代碼描述了一個開關事件的抽象類,和兩個繼承該抽象類的內部類。這些內部類能夠自由地訪問Test類中的字段,無需任何條件。
記得看!!!
10.9 內部類的繼承public class Test extends Outer.Inner { // 如果沒有下面的構造方法會編譯失敗 public Test(Outer outer) { outer.super(); } } class Outer { class Inner {} }
可以看到Test只繼承了內部類Inner,而不是外部類。
當要生成一個構造器時,必須要增加這樣一段代碼outer.super();
解釋:內部類的構造器必須連接到指向外部類對象的引用,而在內部類的子類中不再存在可連接的默認對象。所以需要在子類的構造器中包含指向外部類的引用,必須是帶參數的,而且參數類型是外部類。說白了就是,內部類的對象依賴外部類的對象,內部類的子類的對象,也仍舊是依賴外部類的對象的。
10.10 內部類可以被覆蓋嗎public class Test extends Outer { class Inner {} } class Outer { class Inner {} }
上述代碼中:Test繼承了Outer并“覆蓋”了Inner,但這沒有用;這兩個Inner是完全毫不相干但兩個類,各自活在各自的命名空間里。
public class Test extends Outer { class Inner extends Outer.Inner { @Override void func() { System.out.println("Test.Inner.func()"); } } public Test() { setInner(new Inner()); } public static void main(String[] args) { new Test().getInner().func(); } } class Outer { private Inner inner; class Inner { void func() { System.out.println("Outer.Inner.func()"); } } public Inner getInner() { return inner; } public void setInner(Inner inner) { this.inner = inner; } }
上述代碼中:Test繼承了Outer,Test.Inner繼承了Outer.Inner。此時如果覆蓋Inner中的方法,當構造器調用setInner(new Inner());的時候,是把Test.Inner向上轉型為Outer中的引用inner。
10.11 局部內部類(見10.5)
前面提到過,可以在代碼塊里創建內部類,典型的方式是在方法體內。
局部內部類不能有訪問說明符,因為他不是外部類的一部分。
局部內部類可以訪問當前代碼塊內的常量,以及此外圍類的成員。
局部內部類可以有構造器以及重載構造器,而匿名內部類只能用于實例初始化。
局部內部類可以創建多個對象,而匿名內部類最多有一個
10.12 內部類標識符每個類都會產生一個.class文件,其中包含了如何創建該類的對象的全部信息(此信息產生一個“meta-class”,叫做Class;對象),內部類也是如此。
外部類的名字:外部類名.class
普通內部類:外部類名$內部類名.class
匿名內部類:外部類名$編譯器分配的數字.class
多層嵌套:按從外到內用$分割.class
對于Unix shell而言,$是一個元字符,所以在列出.class文件的時候,有時會有問題。
10.13 總結內部類涉及內容相對復雜,多花點時間吧~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72196.html
摘要:抽象類和抽象方法抽象方法這種方法是不完整的,僅有聲明而沒有方法。創建抽象類和抽象方法非常有用,因為他們可以使累的抽象性明確起來,并告訴用戶和編譯器打算怎樣來使用它們。接口用于建立類于類之間的協議。與抽象類相同,防止客戶端程序員創建該類對象。 點擊進入我的博客 接口和內部類為我們提供了一種將接口與實現分離的更加結構化的方法。 9.1抽象類和抽象方法 抽象方法:這種方法是不完整的,僅有...
摘要:方法的基本組成包括名稱參數返回值方法體方法名和參數列表唯一的標識出某個方法。如果返回的類型是,則的作用僅是退出方法否則必須返回正確的返回值包名名字可見性約定以域名反轉作為包名,用來劃分子目錄,并且全部小寫。 點擊進入我的博客 2.1用引用操縱對象 盡管一切都看作對象,但操縱的標識符實際上是對象的一個引用。 String s; // s是一個String類型的引用, 并沒有任何對象與其...
摘要:在設計模式中,所有的設計模式都遵循這一原則。其實就是說在應用程序中,所有的類如果使用或依賴于其他的類,則應該依賴這些其他類的抽象類,而不是這些其他類的具體類。使用設計模式是為了可重用代碼讓代碼更容易被他人理解保證代碼可靠性。 這是劉意老師的JAVA基礎教程的筆記講的賊好,附上傳送門 傳智風清揚-超全面的Java基礎 一、面向對象思想設計原則 1.單一職責原則 其實就是開發人員經常說的高...
摘要:類最基本的作用,在于通過類獲取到相應的對象,在向對象發送消息時以期望對象做某些特定的事情。先導概念引用中一切皆對象,因此采用一個指向對象的引用來操縱對象。對象可以存活于作用域之外。 歡迎各位讀者關注我的微信公眾號,共同探討Java相關技術。生命不止,學習不休! showImg(https://segmentfault.com/img/bVboaBO?w=129&h=129); 也許你慢...
摘要:包命名規范使用小寫字母。包訪問權限為把類聚在一個包中的做法提供了意義和理由。接口訪問權限使用關鍵字,意味著被它修飾的成員對所有類可見。繼承訪問權限基類的創建者希望某些特定成員,把它的訪問權限賦予派生類也不是所有類。 點擊進入我的博客 6.1包:庫單元 import java.util.ArrayList; import java.util.*; 當編寫一個Java源代碼文件(編譯單...
閱讀 1193·2021-11-15 18:00
閱讀 1789·2021-10-08 10:15
閱讀 752·2021-09-04 16:48
閱讀 2373·2021-09-04 16:48
閱讀 1313·2019-08-29 18:40
閱讀 965·2019-08-29 13:08
閱讀 2987·2019-08-26 14:06
閱讀 1111·2019-08-26 13:35