thinking-in-java(10)内部类
生活随笔
收集整理的這篇文章主要介紹了
thinking-in-java(10)内部类
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【0】開場白
1)內部類:將一個類的定義放在另一個類的定義內部,這個類就是內部類;
2)內部類優點:匿名內部類的一個優點就是可以將解決問題的代碼隔離,聚攏在一點;
【10.1】創建內部類 【荔枝】把類的定義置于外圍類的里面 public class Parcel1 {class Contents { // 內部類private int i = 11;public int value() {return i;}}class Destination { // 內部類private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}// Using inner classes looks just like// using any other class, within Parcel1:// public void ship(String dest) {Contents c = new Contents(); // Destination d = new Destination(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel1 p = new Parcel1();p.ship("Tasmania");} } /* Tasmania */ 【荔枝】外部類有一個方法, 該方法返回一個指向內部類的引用, 如下: public class Parcel2 {class Contents { // 內部類private int i = 11;public int value() { return i; }}class Destination { // 內部類private String label;Destination(String whereTo) {label = whereTo;}String readLabel() { return label; }}public Destination to(String s) { // 外部類中的方法 返回一個指向 內部類的引用return new Destination(s);}public Contents contents() { // 外部類中的方法 返回一個指向 內部類的引用return new Contents();}public void ship(String dest) {Contents c = contents();Destination d = to(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel2 p = new Parcel2();p.ship("Tasmania");Parcel2 q = new Parcel2();// Defining references to inner classes:Parcel2.Contents c = q.contents(); // 注意它調用內部類的方式 是 Parcel2.ContentsParcel2.Destination d = q.to("Borneo"); // // 注意它調用內部類的方式 是 Parcel2.Destination} } /* Tasmania */ 【補充】如果想從外部類的非靜態方法之外的任意位置創建某個內部類的對象, 那么必須像在main方法中那樣,具體指明這個對象的類型: OuterClassName.InnerClassName;
【10.2】鏈接到外部類 1)內部類擁有訪問外圍類的所有元素的訪問權;
【荔枝】基于內部類實現迭代器設計模式 interface Selector {boolean end();Object current();void next(); } public class Sequence {private Object[] items;private int next = 0;public Sequence(int size) { items = new Object[size]; }public void add(Object x) {if (next < items.length)items[next++] = x;}// 迭代器設計模式private class SequenceSelector implements Selector { // 內部類 訪問外部類的 items 實例變量private int i = 0;public boolean end() { return i == items.length; }public Object current() { return items[i]; }public void next() {if (i < items.length)i++;}public void reverseSelector() { }}public Selector selector() { // 外部類方法 創建內部類并返回該實例.return new SequenceSelector();}public static void main(String[] args) {Sequence sequence = new Sequence(10);for (int i = 0; i < 10; i++)sequence.add(Integer.toString(i));Selector selector = sequence.selector();while (!selector.end()) {System.out.print(selector.current() + " ");selector.next();}} } /** Output: 0 1 2 3 4 5 6 7 8 9*/// :~ 【補充】當某個外圍類的對象創建一個內部類對象時,此內部類對象必定會秘密捕獲一個指向那個外圍類對象的引用;然后,在你訪問此外圍類的成員時, 就是用那個引用來選擇外圍類的成員;
【10.3】使用.this(生成對外部類對象的引用) 與 .new(在 new 表達式中提供對其他外部類對象的引用) 【荔枝】如何使用 .this public class DotThis {void f() {System.out.println("DotThis.f()");}public class Inner { // 內部類public DotThis outer() {return DotThis.this; // key: 生成對外部類對象的引用.}}public Inner inner() {return new Inner();}public static void main(String[] args) {DotThis dt = new DotThis();DotThis.Inner dti = dt.inner();dti.outer().f(); } } /** Output: DotThis.f()*/// :~ 【荔枝】如何使用.new public class DotNew {public class Inner { }public static void main(String[] args) {DotNew dn = new DotNew();// .new : 在new 表達式中 提供對其他外部類對象的引用, 使用 .new 語法.DotNew.Inner dni = dn.new Inner(); }public void main2() {DotThis dt = new DotThis();DotThis.Inner inner = dt.new Inner();System.out.println(inner);} } // /:~ 【補充】 補充1)在擁有外部類對象之前是不可能創建內部類對象的。這是因為內部類對象會暗暗地連接到創建它的外部類對象上; 補充2)如果創建的是靜態內部類, 則靜態內部類不需要對外部類對象的引用;
【荔枝】.new 應用于 Parcel public class Parcel3 {class Contents { // 內部類private int i = 11;public int value() {return i;}}class Destination { // 內部類private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}public static void main(String[] args) {Parcel3 p = new Parcel3();// Must use instance of outer class// to create an instance of the inner class:// 必須使用外部類對象實例 創建 內部類實例Parcel3.Contents c = p.new Contents();Parcel3.Destination d = p.new Destination("Tasmania");} } // /:~ 【10.4】內部類與向上轉型 1)當將內部類向上轉型為其基類, 尤其轉型為一個接口時,內部類就有了用武之地;
【荔枝】內部類向上轉型 // 荔枝:內部類向上轉型 class Parcel4 {// private 訪問修飾符 的內部類 隱藏子類的實現細節.private class PContents implements Contents {private int i = 11;public int value() { return i; }}protected class PDestination implements Destination {private String label;private PDestination(String whereTo) { label = whereTo; }public String readLabel() { return label; }}public Destination destination(String s) {return new PDestination(s);}public Contents contents() {return new PContents();} } public class TestParcel {public static void main(String[] args) {Parcel4 p = new Parcel4();Contents c = p.contents(); // 內部類向上轉型.Destination d = p.destination("Tasmania");} } 【補充】 補充1)注意private內部類的訪問權限;
【10.5】在方法和作用域內的內部類 1)可以在一個方法里面或在任意的作用域內定義內部類;
【荔枝】局部內部類:在方法作用域內創建一個完整的類 // 局部內部類的經典荔枝 public class Parcel5 {public Destination destination(String s) {class PDestination implements Destination { // 局部內部類,在方法中進行定義private String label;private PDestination(String whereTo) {label = whereTo;}public String readLabel() {return label;}}return new PDestination(s); // 在同一個方法中 返回 局部內部類的實例}public static void main(String[] args) {Parcel5 p = new Parcel5();Destination d = p.destination("Tasmania");} } // 【荔枝】如何在任意的作用域內嵌入一個內部類 // 荔枝:在任意作用域中嵌入一個內部類. public class Parcel6 {private void internalTracking(boolean b) {if (b) { class TrackingSlip { // if 條件語句中 定義 局部內部類private String id;TrackingSlip(String s) {id = s;}String getSlip() { return id; }}TrackingSlip ts = new TrackingSlip("slip");String s = ts.getSlip();System.out.println(s);} // 在 定義 TrackingSlip 的作用域之外 創建 TrackingSlip 實例是不可行的. // TrackingSlip instance = new TrackingSlip("str"); // syntax error.}public void track() {internalTracking(true);}public static void main(String[] args) {Parcel6 p = new Parcel6();p.track();} } 【10.6】匿名內部類 1)匿名內部類荔枝: public interface Contents {int value(); } // 荔枝-匿名內部類 public class Parcel7 {public Contents contents() {return new Contents() { // 插入一個類的定義 == 匿名內部類private int i = 11;public int value() {return i;}}; // 匿名內部類需要分號.}public static void main(String[] args) {Parcel7 p = new Parcel7();Contents c = p.contents();} } 【補充】 補充1)contents方法將返回值的生成與表示這個返回值的類的定義結合在一起; 補充2)匿名內部類語法說明: 創建一個繼承自Contents的匿名類的對象; 通過new表達式返回的引用被自動向上轉型為 對 Contents的引用;
【荔枝】上述匿名內部類(Parcel7.java)的語法是以下代碼的簡化版本,如下: // 匿名內部類的等同版本(不過本版本要比匿名內部類復雜得多) public class Parcel7b {// 創建一個繼承自 Contents 的 匿名類的對象class MyContents implements Contents {private int i = 11;public int value() {return i;}}// 創建方法返回的引用被自動 向上轉型為 對 Contents 的 引用public Contents contents() {return new MyContents();}public static void main(String[] args) {Parcel7b p = new Parcel7b();Contents c = p.contents();} } 以上代碼使用了默認構造器來生成 Contents對象, 如果構造器是有參數的, 怎么辦?
【荔枝】基于有參構造器定義匿名內部類 public class Wrapping {private int i;public Wrapping(int x) { i = x; }public int value() { return i; } } // 荔枝-基于有參構造器 定義匿名內部類 public class Parcel8 {public Wrapping wrapping(int x) {return new Wrapping(x) { // 傳遞給有參構造器. public int value() {return super.value() * 47; // super.value() 是基類方法返回值}}; // 匿名內部類需要分號}public static void main(String[] args) {Parcel8 p = new Parcel8();Wrapping w = p.wrapping(10);System.out.println(w.value());} } 【荔枝】在匿名內部類中定義字段時, 可以對其執行初始化操作 // 荔枝-在匿名內部類中定義字段時, 可以對其執行初始化操作 public class Parcel9 {// 希望 匿名內部類 使用一個 在其外部定義的 對象,其參數引用必須為finalpublic Destination destination(final String dest) { return new Destination() {private String label = dest;public String readLabel() { return label; }}; // 需要分號}public static void main(String[] args) {Parcel9 p = new Parcel9();Destination d = p.destination("Tasmania");System.out.println(d.readLabel());}public static void f1() {}public void f2(){ f1(); } } /* Tasmania */ 【補充】希望 匿名內部類 使用一個 在其外部定義的 對象,其參數引用必須為final
在匿名內部類中不可能有命名構造器(因為它根本沒名字)。但通過實例初始化, 就能夠達到為匿名內部類創建一個構造器的效果,就像這樣: 【荔枝】通過實例初始化為匿名內部類創建一個構造器 // 荔枝-通過實例初始化為匿名內部類創建一個構造器 abstract class Base {public Base(int i) { print("Base constructor, i = " + i); }public abstract void f(); } public class AnonymousConstructor {public static Base getBase(int i) {// 通過實例初始化, 就能夠達到為 匿名內部類創建一個構造器的效果.return new Base(i) {{print("Inside instance initializer");}public void f() {print("In anonymous f(), and i = " + i);}};}public static void main(String[] args) {Base base = getBase(47);base.f();} } /* Base constructor, i = 47 Inside instance initializer In anonymous f(), and i = 47 */ 【補充】在上述荔枝中, 不要求變量i一定是final的。因為 i 被傳遞給匿名類的基類構造器, 他并不會在匿名內部類的內部被直接使用;
【荔枝】為內部類字段進行賦值,則方法參數必須是 final // 荔枝-為內部類字段進行賦值, 則方法參數 必須是 final public class Parcel10 { // 為內部類字段進行賦值, 則方法參數 必須是 finalpublic Destination destination(final String dest, final float price) {return new Destination() {private int cost;// 對每個對象進行初始化{cost = Math.round(price); // 四舍五入if (cost > 100)System.out.println("Over budget!");}private String label = dest;public String readLabel() {return label;}};}public static void main(String[] args) {Parcel10 p = new Parcel10();Destination d = p.destination("Tasmania", 101.395F);System.out.println(d.readLabel());} } /* Over budget! Tasmania */ 【補充】匿名內部類與正規的繼承相比有些受限:因為匿名內部類既可以擴展類,也可以實現接口,但不能兩者兼備。如果實現接口,也只能實現一個接口;
【10.6.1】在訪工廠方法 【荔枝】基于匿名內部類的工廠方法 interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } // 荔枝-通過匿名內部類 實現工廠方法模式(經典荔枝) class Implementation1 implements Service {private Implementation1() { }public void method1() { print("Implementation1 method1"); }public void method2() { print("Implementation1 method2"); }public static ServiceFactory factory = new ServiceFactory() { // 靜態匿名內部類public Service getService() { return new Implementation1(); }}; } class Implementation2 implements Service {private Implementation2() { }public void method1() { print("Implementation2 method1"); }public void method2() { print("Implementation2 method2"); }public static ServiceFactory factory = new ServiceFactory() { // 靜態匿名內部類public Service getService() { return new Implementation2(); }}; } public class Factories {public static void serviceConsumer(ServiceFactory fact) {Service s = fact.getService();s.method1();s.method2();}public static void main(String[] args) {serviceConsumer(Implementation1.factory);serviceConsumer(Implementation2.factory);} } /** Output: * Implementation1 method1 * Implementation1 method2 * Implementation2 method1* Implementation2 method2*/// :~ 【荔枝】通過匿名內部類 實現工廠方法模式(經典荔枝) interface Game { boolean move(); } interface GameFactory { Game getGame(); } //通過匿名內部類 實現工廠方法模式(經典荔枝) class Checkers implements Game {private Checkers() {}private int moves = 0;private static final int MOVES = 3;public boolean move() {print("Checkers move " + moves);return ++moves != MOVES;}public static GameFactory factory = new GameFactory() { // 匿名內部類.public Game getGame() { return new Checkers(); }}; } class Chess implements Game {private Chess() {}private int moves = 0;private static final int MOVES = 4;public boolean move() {print("Chess move " + moves);return ++moves != MOVES;}public static GameFactory factory = new GameFactory() { // 匿名內部類.public Game getGame() { return new Chess(); }}; } public class Games {public static void playGame(GameFactory factory) {Game s = factory.getGame();while (s.move()) ;}public static void main(String[] args) {playGame(Checkers.factory);playGame(Chess.factory);} } /* Checkers move 0 Checkers move 1 Checkers move 2 Chess move 0 Chess move 1 Chess move 2 Chess move 3 */ 【10.7】嵌套類(靜態內部類) 1)靜態內部類意味著: 1.1)要創建匿名內部類的對象,并不需要其外圍類對象; 1.2)不能從匿名內部類的對象中訪問非靜態的外圍類對象; 2)靜態內部類與普通內部類的區別:普通內部類的字段與方法, 只能放在類的外部層次上, 所以普通內部類不能有static數據和static字段,也不能包含 靜態內部類。 但是靜態內部類里是可以包含所有這些東西的;
【荔枝】靜態內部類可以包含static數據,static字段和方法,也可以包含普通的字段和方法 // 嵌套類(靜態內部類)的荔枝 public class Parcel11 {// 靜態內部類private static class ParcelContents implements Contents {private int i = 11;public int value() { return i; }}// 靜態內部類protected static class ParcelDestination implements Destination {private String label; // 普通變量private ParcelDestination(String whereTo) {label = whereTo;}public String readLabel() { // 普通方法return label;}public static void f() { } // 靜態方法static int x = 10; // 靜態變量static class AnotherLevel { // 靜態內部類( 嵌套類 )public static void f() { } // 靜態方法static int x = 10; // 靜態變量int y = 10; // 普通變量}class A { // 普通內部類class B {class C { }}}}public static Destination destination(String s) {return new ParcelDestination(s);}public static Contents contents() {return new ParcelContents();}public static void main(String[] args) {Contents c = contents();Destination d = destination("Tasmania");} } 【10.7.1】接口內部的類 1)正常情況下, 不能在接口內部放置任何代碼,但靜態內部類可以作為接口的一部分; 2)放置到接口中的任何類都默認是 public static;
【荔枝】在接口內部定義靜態內部類 // 在接口作用域內放置 嵌套類(靜態內部類) // 接口中的類 自動是 public 和 static 的. interface ClassInInterface{void howdy();class Test implements ClassInInterface { // 默認是 public staticpublic void howdy() {System.out.println("Howdy!");}public static void test() {System.out.println("my name is test.");}public static void main(String[] args) {new Test().howdy(); }} } /* 錯誤: 找不到或無法加載主類 chapter10.ClassInInterfaceTest$Test */ 【補充】在每個類中都寫main方法來測試。這樣做有一個缺點: 那就是必須帶著那些已經編譯過的額外代碼。如果這對你是個麻煩,那就可以使用匿名內部類來放置測試代碼; //荔枝-使用匿名內部類來放置測試代碼 public class TestBed {public void f() {System.out.println("f()");}public static class Tester {public static void main(String[] args) {TestBed t = new TestBed();t.f();}} } /** Output: f()*/ 【說明】這生成了一個獨立的類 TestBed$Tester(要運行這個程序, 執行 java TestBed$Tester 即可);可以使用這個類來做測試, 但不必再發布的產品中包含它, 在將產品打包前可以簡單地刪除 TestBed$Tester.class; 【10.7.2】從多層嵌套類中訪問外部類的成員 1)一個內部類被嵌套多少層并不重要:它能透明地訪問所有它所嵌入的外圍類的所有成員;
【荔枝】從多層嵌套類中訪問外部類的成員 // 荔枝-從多層嵌套類中訪問外部類的成員 class MNA {private void f() {}class A {private void g() {}public class B {// 從多層嵌套類中 訪問外部類的成員.void h() {g(); // 調用 A.g()f(); // 調用 MNA.f()}}} }public class MultiNestingAccess {public static void main(String[] args) {MNA mna = new MNA();MNA.A mnaa = mna.new A(); // .new 表達式提供對其他外部類對象的引用.MNA.A.B mnaab = mnaa.new B();mnaab.h();} } // /:~ 【10.8】為什么需要內部類? 1)內部類最吸引人的原因: 每個內部類都能獨立繼承自一個(接口的)實現, 所以無論外圍類是否已經繼承了某個(接口的)實現, 對于內部類都沒有影響; 2)內部類使得多重繼承的解決方案變得完整。接口解決了多重繼承的部分問題, 內部類有效地實現了 多重繼承。也就是說, 內部類允許繼承多個非接口類型(譯注:類或抽象類);
3)考慮以下情形:必須在一個類中以某種方式實現兩個接口。 有兩種實現方式:使用單一類, 或者使用內部類; // 荔枝-實現多重接口的荔枝 // 方式1-使用單一類 class X implements A, B {}// 方式2-使用內部類, 如下: // 外部類本身實現一個接口; // 外部類的方法返回一個匿名內部類(匿名內部類就是一個接口類型), 以達到實現兩個接口的目的; class Y implements A {B makeB() {// 匿名內部類return new B() {};} } public class MultiInterfaces {static void takesA(A a) {}static void takesB(B b) {}public static void main(String[] args) {X x = new X();Y y = new Y();takesA(x);takesA(y);takesB(x);takesB(y.makeB()); // this line.(bingo)} } 4)如果擁有的是抽象類或具體類,而不是接口, 那就只能使用內部類才能實現多重繼承; (干貨——使用內部類才能實現多重繼承)
【荔枝】使用內部類才能實現多重繼承 class D {} // 具體類 abstract class E {} // 抽象類// 荔枝-使用內部類才能實現多重繼承 class Z extends D {E makeE() {return new E() {};} }public class MultiImplementation {static void takesD(D d) {}static void takesE(E e) {}public static void main(String[] args) {Z z = new Z();takesD(z);takesE(z.makeE());} } 5)內部類有以下特性: 特性1)內部類可以有多個實例, 每個實例都有自己的狀態信息,并且與其外圍類對象的信息相互獨立; 特性2)在單個外圍類中, 可以讓多個內部類以不同方式實現同一個接口,或繼承同一個類; 特性3)創建內部類對象的時刻并不依賴于外圍類對象的創建; 特性4)內部類并沒有 is-a 關系, 他就是一個獨立實體;
【10.8.1】閉包與回調 1)閉包是一個可調用的對象,他記錄了一些信息,這些信息來自于創建它的作用域; 2)內部類是面向對象的閉包: 因為內部類不僅包含外圍類對象(創建內部類的作用域)的信息,還自動擁有一個指向指向此外圍類對象的引用,在此作用域內,內部類有權操作所有成員,包括private成員;
【荔枝】通過內部類提供閉包功能 // 荔枝-通過內部類提供閉包功能 interface Incrementable { void increment(); } // 類本身實現接口 class Callee1 implements Incrementable {private int i = 0;public void increment() { i++; print(i); } }class MyIncrement {public void increment() { print("Other operation"); }static void f(MyIncrement mi) { mi.increment(); } } // 類的內部類實現接口 class Callee2 extends MyIncrement {private int i = 0;@Overridepublic void increment() {super.increment(); i++; print(i);}// 閉包內部類private class Closure implements Incrementable {public void increment() {// 指定外部類方法,否則你將得到一個無限循環.Callee2.this.increment(); // 返回外部類對象的引用(鉤子),利用鉤子調用外部類的方法,稱為回調}}// 返回回調引用Incrementable getCallbackReference() { return new Closure(); } } class Caller {private Incrementable callbackReference;Caller(Incrementable cbh) { callbackReference = cbh; }void go() { callbackReference.increment(); } } public class Callbacks {public static void main(String[] args) {Callee1 c1 = new Callee1();Callee2 c2 = new Callee2();MyIncrement.f(c2); // Other operation 1Caller caller1 = new Caller(c1);Caller caller2 = new Caller(c2.getCallbackReference()); // 獲得回調引用caller1.go(); // 1 caller1.go(); // 2caller2.go(); // Other operation 2caller2.go(); // Other operation 3} } /* Other operation 1 1 2 Other operation 2 Other operation 3 */ 分析1)內部類Closure 實現了 Incrementable接口,以提供返回 Callee2的鉤子; 分析2)Caller的構造器需要一個 Incrementable 的引用作為參數(雖然可以在任意時刻捕獲回調引用),然后在以后的某個時刻,Caller對象可以使用此引用回調Callee類;
【10.9】內部類的繼承 // 荔枝-內部類的繼承 class WithInner {class Inner {} }public class InheritInner extends WithInner.Inner {// InheritInner() {} // 這個無參構造器 無法編譯,Won't compileInheritInner(WithInner wi) {wi.super();}public static void main(String[] args) {WithInner wi = new WithInner();InheritInner ii = new InheritInner(wi);} } // /:~ 分析1)InheritInner只繼承自內部類,而不是外圍類; 分析2)當要生成一個構造器時,默認的構造器并不算好,而且不能只是傳遞一個指向外圍類對象的引用。 分析3)必須在構造器內使用如下語法: enclosingClassReference.super(); 這樣才提供了必要的引用, 然后程序才能通過編譯;
【10.10】內部類可以被覆蓋嗎? 1)內部類覆蓋:如果創建一個內部類,然后繼承其外圍類并重新定義此內部類時,會發生什么呢? 2)覆蓋內部類并不起什么作用; // 荔枝-覆蓋內部類(不起任何作用) class Egg {private Yolk y;protected class Yolk {public Yolk() { print("Egg.Yolk()"); } // 2, 而是調用這個 Yolk 構造方法。}public Egg() {print("New Egg()"); // 1y = new Yolk();} }public class BigEgg extends Egg {// 內部類 BigEgg.Yolk 沒有覆蓋 內部類 Egg.Yolk public class Yolk {public Yolk() { print("BigEgg.Yolk()"); } // not this one. 并沒有調用這個 Yolk 構造方法。}public static void main(String[] args) {new BigEgg();} } /* New Egg() Egg.Yolk() */ 3)明確地繼承某個內部類是奏效的, 如下: // 荔枝-明確地繼承某個內部類是奏效的 class Egg2 {protected class Yolk {public Yolk() { print("Egg2.Yolk()"); } // 1, 3public void f() { print("Egg2.Yolk.f()"); }}private Yolk y = new Yolk();public Egg2() { print("New Egg2()"); } // 2public void insertYolk(Yolk yy) { y = yy; }public void g() { y.f(); } }public class BigEgg2 extends Egg2 {// 內部類 BigEgg2.Yolk 明確繼承繼承 另一個外部類的內部類 Egg2.Yolkpublic class Yolk extends Egg2.Yolk {public Yolk() { print("BigEgg2.Yolk()"); } // 4public void f() { print("BigEgg2.Yolk.f()"); } // 5}public BigEgg2() { insertYolk(new Yolk()); }public static void main(String[] args) {Egg2 e2 = new BigEgg2();e2.g();} } /* Egg2.Yolk() New Egg2() Egg2.Yolk() BigEgg2.Yolk() BigEgg2.Yolk.f() */ 【10.11】局部內部類 1)介紹: 局部內部類不能有訪問說明符,因為它不是外圍類的一部分;但是他可以訪問當前代碼塊內的常量,以及此外圍類的所有成員; 2)對局部內部類與匿名內部類的創建進行了比較,荔枝如下: // 荔枝-局部內部類 // 荔枝-對局部內部類與匿名內部類的創建進行了比較 interface Counter { int next(); } public class LocalInnerClass {private int count = 0;Counter getCounter(final String name) {class LocalCounter implements Counter { // 方法域中聲明 局部內部類public LocalCounter() { print("LocalCounter()"); }public int next() {printnb(name); return count++; // 共同操作 外部類的字段}}return new LocalCounter();}Counter getCounter2(final String name) {return new Counter() { // 匿名內部類完成 與 局部內部類相同的工作{ print("Counter()"); }public int next() {printnb(name); return count++; // 共同操作 外部類的字段}};}public static void main(String[] args) {LocalInnerClass lic = new LocalInnerClass();Counter c1 = lic.getCounter("Local inner "), c2 = lic.getCounter2("Anonymous inner ");for (int i = 0; i < 5; i++)print(c1.next());for (int i = 0; i < 5; i++)print(c2.next());} } /* LocalCounter() Counter() Local inner 0 Local inner 1 Local inner 2 Local inner 3 Local inner 4 Anonymous inner 5 Anonymous inner 6 Anonymous inner 7 Anonymous inner 8 Anonymous inner 9 */ 【補充】為什么有些時候仍然使用局部內部類,不是已經有匿名內部類了嗎? 理由1)需要一個已命名的構造器,或者需要重載構造器,而匿名內部類只能用于實例化; 理由2)需要不止一個該內部類的對象;
【10.12】內部類標識符 1)內部類生成一個 .class文件以包含他們的 Class 對象信息; 2)這些類文件的命名有嚴格的規則: 外圍類的名字, 加上 $ , 再加上內部類的名字; 3)荔枝: LocalInnerClass.java 生成的 .class 文件包括: Counter.class // 接口 LocalInnerClass$1.class // 匿名內部類 LocalInnerClass$1LocalCounter.class // 局部內部類 LocalInnerClass.class // LocalInnerClass類
【10.1】創建內部類 【荔枝】把類的定義置于外圍類的里面 public class Parcel1 {class Contents { // 內部類private int i = 11;public int value() {return i;}}class Destination { // 內部類private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}// Using inner classes looks just like// using any other class, within Parcel1:// public void ship(String dest) {Contents c = new Contents(); // Destination d = new Destination(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel1 p = new Parcel1();p.ship("Tasmania");} } /* Tasmania */ 【荔枝】外部類有一個方法, 該方法返回一個指向內部類的引用, 如下: public class Parcel2 {class Contents { // 內部類private int i = 11;public int value() { return i; }}class Destination { // 內部類private String label;Destination(String whereTo) {label = whereTo;}String readLabel() { return label; }}public Destination to(String s) { // 外部類中的方法 返回一個指向 內部類的引用return new Destination(s);}public Contents contents() { // 外部類中的方法 返回一個指向 內部類的引用return new Contents();}public void ship(String dest) {Contents c = contents();Destination d = to(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel2 p = new Parcel2();p.ship("Tasmania");Parcel2 q = new Parcel2();// Defining references to inner classes:Parcel2.Contents c = q.contents(); // 注意它調用內部類的方式 是 Parcel2.ContentsParcel2.Destination d = q.to("Borneo"); // // 注意它調用內部類的方式 是 Parcel2.Destination} } /* Tasmania */ 【補充】如果想從外部類的非靜態方法之外的任意位置創建某個內部類的對象, 那么必須像在main方法中那樣,具體指明這個對象的類型: OuterClassName.InnerClassName;
【10.2】鏈接到外部類 1)內部類擁有訪問外圍類的所有元素的訪問權;
【荔枝】基于內部類實現迭代器設計模式 interface Selector {boolean end();Object current();void next(); } public class Sequence {private Object[] items;private int next = 0;public Sequence(int size) { items = new Object[size]; }public void add(Object x) {if (next < items.length)items[next++] = x;}// 迭代器設計模式private class SequenceSelector implements Selector { // 內部類 訪問外部類的 items 實例變量private int i = 0;public boolean end() { return i == items.length; }public Object current() { return items[i]; }public void next() {if (i < items.length)i++;}public void reverseSelector() { }}public Selector selector() { // 外部類方法 創建內部類并返回該實例.return new SequenceSelector();}public static void main(String[] args) {Sequence sequence = new Sequence(10);for (int i = 0; i < 10; i++)sequence.add(Integer.toString(i));Selector selector = sequence.selector();while (!selector.end()) {System.out.print(selector.current() + " ");selector.next();}} } /** Output: 0 1 2 3 4 5 6 7 8 9*/// :~ 【補充】當某個外圍類的對象創建一個內部類對象時,此內部類對象必定會秘密捕獲一個指向那個外圍類對象的引用;然后,在你訪問此外圍類的成員時, 就是用那個引用來選擇外圍類的成員;
【10.3】使用.this(生成對外部類對象的引用) 與 .new(在 new 表達式中提供對其他外部類對象的引用) 【荔枝】如何使用 .this public class DotThis {void f() {System.out.println("DotThis.f()");}public class Inner { // 內部類public DotThis outer() {return DotThis.this; // key: 生成對外部類對象的引用.}}public Inner inner() {return new Inner();}public static void main(String[] args) {DotThis dt = new DotThis();DotThis.Inner dti = dt.inner();dti.outer().f(); } } /** Output: DotThis.f()*/// :~ 【荔枝】如何使用.new public class DotNew {public class Inner { }public static void main(String[] args) {DotNew dn = new DotNew();// .new : 在new 表達式中 提供對其他外部類對象的引用, 使用 .new 語法.DotNew.Inner dni = dn.new Inner(); }public void main2() {DotThis dt = new DotThis();DotThis.Inner inner = dt.new Inner();System.out.println(inner);} } // /:~ 【補充】 補充1)在擁有外部類對象之前是不可能創建內部類對象的。這是因為內部類對象會暗暗地連接到創建它的外部類對象上; 補充2)如果創建的是靜態內部類, 則靜態內部類不需要對外部類對象的引用;
【荔枝】.new 應用于 Parcel public class Parcel3 {class Contents { // 內部類private int i = 11;public int value() {return i;}}class Destination { // 內部類private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}public static void main(String[] args) {Parcel3 p = new Parcel3();// Must use instance of outer class// to create an instance of the inner class:// 必須使用外部類對象實例 創建 內部類實例Parcel3.Contents c = p.new Contents();Parcel3.Destination d = p.new Destination("Tasmania");} } // /:~ 【10.4】內部類與向上轉型 1)當將內部類向上轉型為其基類, 尤其轉型為一個接口時,內部類就有了用武之地;
【荔枝】內部類向上轉型 // 荔枝:內部類向上轉型 class Parcel4 {// private 訪問修飾符 的內部類 隱藏子類的實現細節.private class PContents implements Contents {private int i = 11;public int value() { return i; }}protected class PDestination implements Destination {private String label;private PDestination(String whereTo) { label = whereTo; }public String readLabel() { return label; }}public Destination destination(String s) {return new PDestination(s);}public Contents contents() {return new PContents();} } public class TestParcel {public static void main(String[] args) {Parcel4 p = new Parcel4();Contents c = p.contents(); // 內部類向上轉型.Destination d = p.destination("Tasmania");} } 【補充】 補充1)注意private內部類的訪問權限;
【10.5】在方法和作用域內的內部類 1)可以在一個方法里面或在任意的作用域內定義內部類;
【荔枝】局部內部類:在方法作用域內創建一個完整的類 // 局部內部類的經典荔枝 public class Parcel5 {public Destination destination(String s) {class PDestination implements Destination { // 局部內部類,在方法中進行定義private String label;private PDestination(String whereTo) {label = whereTo;}public String readLabel() {return label;}}return new PDestination(s); // 在同一個方法中 返回 局部內部類的實例}public static void main(String[] args) {Parcel5 p = new Parcel5();Destination d = p.destination("Tasmania");} } // 【荔枝】如何在任意的作用域內嵌入一個內部類 // 荔枝:在任意作用域中嵌入一個內部類. public class Parcel6 {private void internalTracking(boolean b) {if (b) { class TrackingSlip { // if 條件語句中 定義 局部內部類private String id;TrackingSlip(String s) {id = s;}String getSlip() { return id; }}TrackingSlip ts = new TrackingSlip("slip");String s = ts.getSlip();System.out.println(s);} // 在 定義 TrackingSlip 的作用域之外 創建 TrackingSlip 實例是不可行的. // TrackingSlip instance = new TrackingSlip("str"); // syntax error.}public void track() {internalTracking(true);}public static void main(String[] args) {Parcel6 p = new Parcel6();p.track();} } 【10.6】匿名內部類 1)匿名內部類荔枝: public interface Contents {int value(); } // 荔枝-匿名內部類 public class Parcel7 {public Contents contents() {return new Contents() { // 插入一個類的定義 == 匿名內部類private int i = 11;public int value() {return i;}}; // 匿名內部類需要分號.}public static void main(String[] args) {Parcel7 p = new Parcel7();Contents c = p.contents();} } 【補充】 補充1)contents方法將返回值的生成與表示這個返回值的類的定義結合在一起; 補充2)匿名內部類語法說明: 創建一個繼承自Contents的匿名類的對象; 通過new表達式返回的引用被自動向上轉型為 對 Contents的引用;
【荔枝】上述匿名內部類(Parcel7.java)的語法是以下代碼的簡化版本,如下: // 匿名內部類的等同版本(不過本版本要比匿名內部類復雜得多) public class Parcel7b {// 創建一個繼承自 Contents 的 匿名類的對象class MyContents implements Contents {private int i = 11;public int value() {return i;}}// 創建方法返回的引用被自動 向上轉型為 對 Contents 的 引用public Contents contents() {return new MyContents();}public static void main(String[] args) {Parcel7b p = new Parcel7b();Contents c = p.contents();} } 以上代碼使用了默認構造器來生成 Contents對象, 如果構造器是有參數的, 怎么辦?
【荔枝】基于有參構造器定義匿名內部類 public class Wrapping {private int i;public Wrapping(int x) { i = x; }public int value() { return i; } } // 荔枝-基于有參構造器 定義匿名內部類 public class Parcel8 {public Wrapping wrapping(int x) {return new Wrapping(x) { // 傳遞給有參構造器. public int value() {return super.value() * 47; // super.value() 是基類方法返回值}}; // 匿名內部類需要分號}public static void main(String[] args) {Parcel8 p = new Parcel8();Wrapping w = p.wrapping(10);System.out.println(w.value());} } 【荔枝】在匿名內部類中定義字段時, 可以對其執行初始化操作 // 荔枝-在匿名內部類中定義字段時, 可以對其執行初始化操作 public class Parcel9 {// 希望 匿名內部類 使用一個 在其外部定義的 對象,其參數引用必須為finalpublic Destination destination(final String dest) { return new Destination() {private String label = dest;public String readLabel() { return label; }}; // 需要分號}public static void main(String[] args) {Parcel9 p = new Parcel9();Destination d = p.destination("Tasmania");System.out.println(d.readLabel());}public static void f1() {}public void f2(){ f1(); } } /* Tasmania */ 【補充】希望 匿名內部類 使用一個 在其外部定義的 對象,其參數引用必須為final
在匿名內部類中不可能有命名構造器(因為它根本沒名字)。但通過實例初始化, 就能夠達到為匿名內部類創建一個構造器的效果,就像這樣: 【荔枝】通過實例初始化為匿名內部類創建一個構造器 // 荔枝-通過實例初始化為匿名內部類創建一個構造器 abstract class Base {public Base(int i) { print("Base constructor, i = " + i); }public abstract void f(); } public class AnonymousConstructor {public static Base getBase(int i) {// 通過實例初始化, 就能夠達到為 匿名內部類創建一個構造器的效果.return new Base(i) {{print("Inside instance initializer");}public void f() {print("In anonymous f(), and i = " + i);}};}public static void main(String[] args) {Base base = getBase(47);base.f();} } /* Base constructor, i = 47 Inside instance initializer In anonymous f(), and i = 47 */ 【補充】在上述荔枝中, 不要求變量i一定是final的。因為 i 被傳遞給匿名類的基類構造器, 他并不會在匿名內部類的內部被直接使用;
【荔枝】為內部類字段進行賦值,則方法參數必須是 final // 荔枝-為內部類字段進行賦值, 則方法參數 必須是 final public class Parcel10 { // 為內部類字段進行賦值, 則方法參數 必須是 finalpublic Destination destination(final String dest, final float price) {return new Destination() {private int cost;// 對每個對象進行初始化{cost = Math.round(price); // 四舍五入if (cost > 100)System.out.println("Over budget!");}private String label = dest;public String readLabel() {return label;}};}public static void main(String[] args) {Parcel10 p = new Parcel10();Destination d = p.destination("Tasmania", 101.395F);System.out.println(d.readLabel());} } /* Over budget! Tasmania */ 【補充】匿名內部類與正規的繼承相比有些受限:因為匿名內部類既可以擴展類,也可以實現接口,但不能兩者兼備。如果實現接口,也只能實現一個接口;
【10.6.1】在訪工廠方法 【荔枝】基于匿名內部類的工廠方法 interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } // 荔枝-通過匿名內部類 實現工廠方法模式(經典荔枝) class Implementation1 implements Service {private Implementation1() { }public void method1() { print("Implementation1 method1"); }public void method2() { print("Implementation1 method2"); }public static ServiceFactory factory = new ServiceFactory() { // 靜態匿名內部類public Service getService() { return new Implementation1(); }}; } class Implementation2 implements Service {private Implementation2() { }public void method1() { print("Implementation2 method1"); }public void method2() { print("Implementation2 method2"); }public static ServiceFactory factory = new ServiceFactory() { // 靜態匿名內部類public Service getService() { return new Implementation2(); }}; } public class Factories {public static void serviceConsumer(ServiceFactory fact) {Service s = fact.getService();s.method1();s.method2();}public static void main(String[] args) {serviceConsumer(Implementation1.factory);serviceConsumer(Implementation2.factory);} } /** Output: * Implementation1 method1 * Implementation1 method2 * Implementation2 method1* Implementation2 method2*/// :~ 【荔枝】通過匿名內部類 實現工廠方法模式(經典荔枝) interface Game { boolean move(); } interface GameFactory { Game getGame(); } //通過匿名內部類 實現工廠方法模式(經典荔枝) class Checkers implements Game {private Checkers() {}private int moves = 0;private static final int MOVES = 3;public boolean move() {print("Checkers move " + moves);return ++moves != MOVES;}public static GameFactory factory = new GameFactory() { // 匿名內部類.public Game getGame() { return new Checkers(); }}; } class Chess implements Game {private Chess() {}private int moves = 0;private static final int MOVES = 4;public boolean move() {print("Chess move " + moves);return ++moves != MOVES;}public static GameFactory factory = new GameFactory() { // 匿名內部類.public Game getGame() { return new Chess(); }}; } public class Games {public static void playGame(GameFactory factory) {Game s = factory.getGame();while (s.move()) ;}public static void main(String[] args) {playGame(Checkers.factory);playGame(Chess.factory);} } /* Checkers move 0 Checkers move 1 Checkers move 2 Chess move 0 Chess move 1 Chess move 2 Chess move 3 */ 【10.7】嵌套類(靜態內部類) 1)靜態內部類意味著: 1.1)要創建匿名內部類的對象,并不需要其外圍類對象; 1.2)不能從匿名內部類的對象中訪問非靜態的外圍類對象; 2)靜態內部類與普通內部類的區別:普通內部類的字段與方法, 只能放在類的外部層次上, 所以普通內部類不能有static數據和static字段,也不能包含 靜態內部類。 但是靜態內部類里是可以包含所有這些東西的;
【荔枝】靜態內部類可以包含static數據,static字段和方法,也可以包含普通的字段和方法 // 嵌套類(靜態內部類)的荔枝 public class Parcel11 {// 靜態內部類private static class ParcelContents implements Contents {private int i = 11;public int value() { return i; }}// 靜態內部類protected static class ParcelDestination implements Destination {private String label; // 普通變量private ParcelDestination(String whereTo) {label = whereTo;}public String readLabel() { // 普通方法return label;}public static void f() { } // 靜態方法static int x = 10; // 靜態變量static class AnotherLevel { // 靜態內部類( 嵌套類 )public static void f() { } // 靜態方法static int x = 10; // 靜態變量int y = 10; // 普通變量}class A { // 普通內部類class B {class C { }}}}public static Destination destination(String s) {return new ParcelDestination(s);}public static Contents contents() {return new ParcelContents();}public static void main(String[] args) {Contents c = contents();Destination d = destination("Tasmania");} } 【10.7.1】接口內部的類 1)正常情況下, 不能在接口內部放置任何代碼,但靜態內部類可以作為接口的一部分; 2)放置到接口中的任何類都默認是 public static;
【荔枝】在接口內部定義靜態內部類 // 在接口作用域內放置 嵌套類(靜態內部類) // 接口中的類 自動是 public 和 static 的. interface ClassInInterface{void howdy();class Test implements ClassInInterface { // 默認是 public staticpublic void howdy() {System.out.println("Howdy!");}public static void test() {System.out.println("my name is test.");}public static void main(String[] args) {new Test().howdy(); }} } /* 錯誤: 找不到或無法加載主類 chapter10.ClassInInterfaceTest$Test */ 【補充】在每個類中都寫main方法來測試。這樣做有一個缺點: 那就是必須帶著那些已經編譯過的額外代碼。如果這對你是個麻煩,那就可以使用匿名內部類來放置測試代碼; //荔枝-使用匿名內部類來放置測試代碼 public class TestBed {public void f() {System.out.println("f()");}public static class Tester {public static void main(String[] args) {TestBed t = new TestBed();t.f();}} } /** Output: f()*/ 【說明】這生成了一個獨立的類 TestBed$Tester(要運行這個程序, 執行 java TestBed$Tester 即可);可以使用這個類來做測試, 但不必再發布的產品中包含它, 在將產品打包前可以簡單地刪除 TestBed$Tester.class; 【10.7.2】從多層嵌套類中訪問外部類的成員 1)一個內部類被嵌套多少層并不重要:它能透明地訪問所有它所嵌入的外圍類的所有成員;
【荔枝】從多層嵌套類中訪問外部類的成員 // 荔枝-從多層嵌套類中訪問外部類的成員 class MNA {private void f() {}class A {private void g() {}public class B {// 從多層嵌套類中 訪問外部類的成員.void h() {g(); // 調用 A.g()f(); // 調用 MNA.f()}}} }public class MultiNestingAccess {public static void main(String[] args) {MNA mna = new MNA();MNA.A mnaa = mna.new A(); // .new 表達式提供對其他外部類對象的引用.MNA.A.B mnaab = mnaa.new B();mnaab.h();} } // /:~ 【10.8】為什么需要內部類? 1)內部類最吸引人的原因: 每個內部類都能獨立繼承自一個(接口的)實現, 所以無論外圍類是否已經繼承了某個(接口的)實現, 對于內部類都沒有影響; 2)內部類使得多重繼承的解決方案變得完整。接口解決了多重繼承的部分問題, 內部類有效地實現了 多重繼承。也就是說, 內部類允許繼承多個非接口類型(譯注:類或抽象類);
3)考慮以下情形:必須在一個類中以某種方式實現兩個接口。 有兩種實現方式:使用單一類, 或者使用內部類; // 荔枝-實現多重接口的荔枝 // 方式1-使用單一類 class X implements A, B {}// 方式2-使用內部類, 如下: // 外部類本身實現一個接口; // 外部類的方法返回一個匿名內部類(匿名內部類就是一個接口類型), 以達到實現兩個接口的目的; class Y implements A {B makeB() {// 匿名內部類return new B() {};} } public class MultiInterfaces {static void takesA(A a) {}static void takesB(B b) {}public static void main(String[] args) {X x = new X();Y y = new Y();takesA(x);takesA(y);takesB(x);takesB(y.makeB()); // this line.(bingo)} } 4)如果擁有的是抽象類或具體類,而不是接口, 那就只能使用內部類才能實現多重繼承; (干貨——使用內部類才能實現多重繼承)
【荔枝】使用內部類才能實現多重繼承 class D {} // 具體類 abstract class E {} // 抽象類// 荔枝-使用內部類才能實現多重繼承 class Z extends D {E makeE() {return new E() {};} }public class MultiImplementation {static void takesD(D d) {}static void takesE(E e) {}public static void main(String[] args) {Z z = new Z();takesD(z);takesE(z.makeE());} } 5)內部類有以下特性: 特性1)內部類可以有多個實例, 每個實例都有自己的狀態信息,并且與其外圍類對象的信息相互獨立; 特性2)在單個外圍類中, 可以讓多個內部類以不同方式實現同一個接口,或繼承同一個類; 特性3)創建內部類對象的時刻并不依賴于外圍類對象的創建; 特性4)內部類并沒有 is-a 關系, 他就是一個獨立實體;
【10.8.1】閉包與回調 1)閉包是一個可調用的對象,他記錄了一些信息,這些信息來自于創建它的作用域; 2)內部類是面向對象的閉包: 因為內部類不僅包含外圍類對象(創建內部類的作用域)的信息,還自動擁有一個指向指向此外圍類對象的引用,在此作用域內,內部類有權操作所有成員,包括private成員;
【荔枝】通過內部類提供閉包功能 // 荔枝-通過內部類提供閉包功能 interface Incrementable { void increment(); } // 類本身實現接口 class Callee1 implements Incrementable {private int i = 0;public void increment() { i++; print(i); } }class MyIncrement {public void increment() { print("Other operation"); }static void f(MyIncrement mi) { mi.increment(); } } // 類的內部類實現接口 class Callee2 extends MyIncrement {private int i = 0;@Overridepublic void increment() {super.increment(); i++; print(i);}// 閉包內部類private class Closure implements Incrementable {public void increment() {// 指定外部類方法,否則你將得到一個無限循環.Callee2.this.increment(); // 返回外部類對象的引用(鉤子),利用鉤子調用外部類的方法,稱為回調}}// 返回回調引用Incrementable getCallbackReference() { return new Closure(); } } class Caller {private Incrementable callbackReference;Caller(Incrementable cbh) { callbackReference = cbh; }void go() { callbackReference.increment(); } } public class Callbacks {public static void main(String[] args) {Callee1 c1 = new Callee1();Callee2 c2 = new Callee2();MyIncrement.f(c2); // Other operation 1Caller caller1 = new Caller(c1);Caller caller2 = new Caller(c2.getCallbackReference()); // 獲得回調引用caller1.go(); // 1 caller1.go(); // 2caller2.go(); // Other operation 2caller2.go(); // Other operation 3} } /* Other operation 1 1 2 Other operation 2 Other operation 3 */ 分析1)內部類Closure 實現了 Incrementable接口,以提供返回 Callee2的鉤子; 分析2)Caller的構造器需要一個 Incrementable 的引用作為參數(雖然可以在任意時刻捕獲回調引用),然后在以后的某個時刻,Caller對象可以使用此引用回調Callee類;
【10.9】內部類的繼承 // 荔枝-內部類的繼承 class WithInner {class Inner {} }public class InheritInner extends WithInner.Inner {// InheritInner() {} // 這個無參構造器 無法編譯,Won't compileInheritInner(WithInner wi) {wi.super();}public static void main(String[] args) {WithInner wi = new WithInner();InheritInner ii = new InheritInner(wi);} } // /:~ 分析1)InheritInner只繼承自內部類,而不是外圍類; 分析2)當要生成一個構造器時,默認的構造器并不算好,而且不能只是傳遞一個指向外圍類對象的引用。 分析3)必須在構造器內使用如下語法: enclosingClassReference.super(); 這樣才提供了必要的引用, 然后程序才能通過編譯;
【10.10】內部類可以被覆蓋嗎? 1)內部類覆蓋:如果創建一個內部類,然后繼承其外圍類并重新定義此內部類時,會發生什么呢? 2)覆蓋內部類并不起什么作用; // 荔枝-覆蓋內部類(不起任何作用) class Egg {private Yolk y;protected class Yolk {public Yolk() { print("Egg.Yolk()"); } // 2, 而是調用這個 Yolk 構造方法。}public Egg() {print("New Egg()"); // 1y = new Yolk();} }public class BigEgg extends Egg {// 內部類 BigEgg.Yolk 沒有覆蓋 內部類 Egg.Yolk public class Yolk {public Yolk() { print("BigEgg.Yolk()"); } // not this one. 并沒有調用這個 Yolk 構造方法。}public static void main(String[] args) {new BigEgg();} } /* New Egg() Egg.Yolk() */ 3)明確地繼承某個內部類是奏效的, 如下: // 荔枝-明確地繼承某個內部類是奏效的 class Egg2 {protected class Yolk {public Yolk() { print("Egg2.Yolk()"); } // 1, 3public void f() { print("Egg2.Yolk.f()"); }}private Yolk y = new Yolk();public Egg2() { print("New Egg2()"); } // 2public void insertYolk(Yolk yy) { y = yy; }public void g() { y.f(); } }public class BigEgg2 extends Egg2 {// 內部類 BigEgg2.Yolk 明確繼承繼承 另一個外部類的內部類 Egg2.Yolkpublic class Yolk extends Egg2.Yolk {public Yolk() { print("BigEgg2.Yolk()"); } // 4public void f() { print("BigEgg2.Yolk.f()"); } // 5}public BigEgg2() { insertYolk(new Yolk()); }public static void main(String[] args) {Egg2 e2 = new BigEgg2();e2.g();} } /* Egg2.Yolk() New Egg2() Egg2.Yolk() BigEgg2.Yolk() BigEgg2.Yolk.f() */ 【10.11】局部內部類 1)介紹: 局部內部類不能有訪問說明符,因為它不是外圍類的一部分;但是他可以訪問當前代碼塊內的常量,以及此外圍類的所有成員; 2)對局部內部類與匿名內部類的創建進行了比較,荔枝如下: // 荔枝-局部內部類 // 荔枝-對局部內部類與匿名內部類的創建進行了比較 interface Counter { int next(); } public class LocalInnerClass {private int count = 0;Counter getCounter(final String name) {class LocalCounter implements Counter { // 方法域中聲明 局部內部類public LocalCounter() { print("LocalCounter()"); }public int next() {printnb(name); return count++; // 共同操作 外部類的字段}}return new LocalCounter();}Counter getCounter2(final String name) {return new Counter() { // 匿名內部類完成 與 局部內部類相同的工作{ print("Counter()"); }public int next() {printnb(name); return count++; // 共同操作 外部類的字段}};}public static void main(String[] args) {LocalInnerClass lic = new LocalInnerClass();Counter c1 = lic.getCounter("Local inner "), c2 = lic.getCounter2("Anonymous inner ");for (int i = 0; i < 5; i++)print(c1.next());for (int i = 0; i < 5; i++)print(c2.next());} } /* LocalCounter() Counter() Local inner 0 Local inner 1 Local inner 2 Local inner 3 Local inner 4 Anonymous inner 5 Anonymous inner 6 Anonymous inner 7 Anonymous inner 8 Anonymous inner 9 */ 【補充】為什么有些時候仍然使用局部內部類,不是已經有匿名內部類了嗎? 理由1)需要一個已命名的構造器,或者需要重載構造器,而匿名內部類只能用于實例化; 理由2)需要不止一個該內部類的對象;
【10.12】內部類標識符 1)內部類生成一個 .class文件以包含他們的 Class 對象信息; 2)這些類文件的命名有嚴格的規則: 外圍類的名字, 加上 $ , 再加上內部類的名字; 3)荔枝: LocalInnerClass.java 生成的 .class 文件包括: Counter.class // 接口 LocalInnerClass$1.class // 匿名內部類 LocalInnerClass$1LocalCounter.class // 局部內部類 LocalInnerClass.class // LocalInnerClass類
總結
以上是生活随笔為你收集整理的thinking-in-java(10)内部类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 帝姬和公主有什么区别 帝姬和公主有区别吗
- 下一篇: Multi-catch paramete