1. 程式人生 > >《java程式設計思想——第十章(內部類)》

《java程式設計思想——第十章(內部類)》

內部類

可以將一個類的定義放在另一個類的定義內部,這就是內部類。

10.1 建立內部類

把類的定義置於外圍類的裡面。

/**
 * 建立內部類
 * @author Administrator
 */
public class Parcel1 {
    class Contents{
        private int i = 11;
        public int value() {
            return i;
        }
    }
    class Destination{
        private String label;
        public
Destination(String whereTo) { label = whereTo; } String readLabel(){ return label; } } 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("哈哈"); } }

10.2 連結到外部類

內部類自動擁有外部類所有成員的訪問權。構建內部類物件時,需要一個指向其外圍類物件的引用。

10.3 使用.this和.new

如果想要生成對外部物件的引用,可以用外部類名後跟.this的形式。

public class DotThis {

    void f(){
        System.out.println("DotThis.f()");
    }
    public
class Inner { public DotThis outer(){ return DotThis.this; } } public Inner inner() { return new Inner(); } public static void main(String[] args) { DotThis dt = new DotThis(); DotThis.Inner dti = dt.inner(); dti.outer().f(); } }

如果要建立內部類的物件,必須在new表示式時提供其外部類的引用。形式是外部類引用.new形式。

public class DotNew {

    public class Inner {}
    public static void main(String[] args) {
        DotNew dn = new DotNew();
        DotNew.Inner dni = dn.new Inner();
    }
}

10.4 內部類與向上轉型

內部類向上轉型為其基類,或者介面時,所得到的只是指向基類或介面的引用,能夠很方便的隱藏細節。

/**
 * 內部類向上轉型
 * @author Administrator
 */
class Parcel4{
    private class pContents implements Content{
        private int i = 11;
        @Override
        public int value() {
            return i;
        }

    }
    protected class PDestination implements Destinations{

        private String label;
        public PDestination(String whereTo) {
            label = whereTo;
        }
        public String readLabel(){
            return label;
        }

    }

    public Destinations destinations(String s) {
        return new PDestination(s);
    }
    public Content contents() {
        return new pContents();
    }
}
public class TestParcel {
    public static void main(String[] args) {
        Parcel4 p = new Parcel4();
        Content c = p.contents();
        Destinations d = p.destinations("哈哈");
    }
}

10.5 在方法和作用域的內部類

/**
 * 方法內部類
 * @author Administrator
 *
 */
public class Parcel5 {

    public Destinations destination(String s) {

         class pDestination implements Destinations{

            private String label;
            public pDestination(String whereTo) {
                label = whereTo;
            }
            public String readLabel(){
                return label;
            }

        }
        return new pDestination(s);
    }

}

/**
 * 作用域內部類
 * @author Administrator
 *
 */
public class Parcel6 {

    private void internalTracking(boolean b) {
        if(b){
            class TrackingSlip{
                private String id;
                 TrackingSlip(String s) {
                    id = s;
                }
                 String getSlip(){
                     return id;
                 }
            }

            TrackingSlip ts = new TrackingSlip("slip");
            String s= ts.getSlip();
        }
    }
    public void track() {
        internalTracking(true);
    }
    public static void main(String[] args) {
        Parcel6 p = new Parcel6();
        p.track();
    }
}

10.6 匿名內部類

預設構造匿名內部類

/**
 * 匿名內部類
 * @author Administrator
 *
 */
public class Parcel7 {

    public Content  content() {
        return new Content() {
            private int i = 11;
            @Override
            public int value() {
                return i;
            }
        };
    }
    public static void main(String[] args) {
        Parcel7 p = new Parcel7();
        Content c = p.content();
    }
}

帶引數匿名內部類

public class Parcel8 {
    public Wrapping  wrapping(int x) {
        return new Wrapping(x) {
            private int i = 11;
            @Override
            public int value() {
                return super.value() * 47;
            }
        };
    }
    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Wrapping w = p.wrapping(8) ;
    }
}

匿名內部類如果使用一個在其外部定義的物件,其引數的引用必須是final 的。

10.7 巢狀類

如何不需要內部類和外部類有聯絡,可以將內部類宣告為static,通常稱為巢狀類

特性:建立巢狀類的物件,並不需要其外圍物件。
不能從巢狀類的物件中訪問非靜態的外圍類物件

  1. 介面中的內部類
    正常情況下介面中不能放置任何程式碼,但巢狀類可以作為介面中國的一部分。
/**
 * 介面中的內部類
 * @author Administrator
 *
 */
public interface ClassInInterface {
    void howdy();
    class Test implements ClassInInterface{
        @Override
        public void howdy() {
            System.out.println("hai");
        }
        public static void main(String[] args) {
            new Test().howdy();
        }

    }
}

10.8 為什麼需要內部類

每個內部類都能獨立地繼承自一個實現,所以無論外圍類是否已經繼承了某個實現,對應內部類都沒有影響。

  1. 內部類可以有多個例項,每個例項都有自己的狀態資訊,並且與外部類資訊相獨立。
  2. 在單個外圍類中,可以讓多個內部類以不同的方式實現同一個介面,或繼承同一個類。
  3. 建立內部類物件的時刻並不依賴於外圍類物件的建立。

閉包與回撥:閉包是一個可呼叫物件,它記錄了一些資訊,這些資訊來自於建立它的域。
回撥的價值在於可以在執行時動態地決定呼叫什麼方法。

內部類與控制框架:
框架的完整實現是由單個類建立,從而使得實現的細節被封裝了起來。
內部類能夠很容易的訪問外圍類的任意成員。
(這部分demo無法執行)

10.9 內部類的繼承

因為內部類的構造器必須連線到指向其外圍類物件的引用,所以在繼承內部類的時候,必須用特殊的語法。

/**
 * 內部類的繼承
 * @author Administrator
 *
 */
class WithInner{
    class Inner{}
}
public class InheritInner  extends WithInner.Inner{
    public InheritInner(WithInner wi) {
        wi.super(); //初始化
    }
    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
    }
}

10.10 內部類可以別覆蓋嗎

不能被覆蓋。繼承了外圍類並覆蓋了內部類時,兩個內部類是是完全獨立的實體,各自在自己的名稱空間裡。
可以明確的繼承某一個內部類。

/**
 * 明確繼承內部類,覆蓋方法
 * @author Administrator
 *
 */
 class Egg2 {

    protected class Yolk{
        public Yolk() {
            System.out.println("Egg2.Yolk()");
        }
        public void f() {
            System.out.println("Egg2.Yolk.f()");
        }
    }
    private Yolk y = new Yolk();
    public Egg2(){
        System.out.println("new Egg2");
    }
    public void insertYolk(Yolk yy) {
        y = yy;
    }
    public void g() {
        y.f();
    }

}
public class BigEgg2 extends Egg2{
    public class Yolk extends Egg2.Yolk{
        public Yolk() {
            System.out.println("BigEgg2.Yolk()");
        }
        public void f() {
            System.out.println("BigEgg2.Yolk.f()");
        }
    }
    public BigEgg2() {
        insertYolk(new Yolk());
    }
    public static void main(String[] args) {
        Egg2 e2 =  new BigEgg2();
        e2.g();
    }
}

10.11 區域性內部類

區域性內部類可以定義在方法體內,不能有訪問說明符,因為它不是外圍類的一部分;但是可以訪問外圍類成員。

/**
 * 區域性內部類
 * @author Administrator
 */
interface Counter{
    int next();
}
public class LocalInnerClass {

    private int count = 0;
    Counter getCounter (final String name){
        class LocalCounter implements Counter{
            public LocalCounter() {
                System.out.println("LocalCounter()");
            }
            @Override
            public int next() {
                System.out.println(name +" "+count);
                return count++;
            }

        }
        return new LocalCounter();

    }

    Counter getCounter2 (final String name){
        return new Counter() {
            {
                System.out.println("Counter()");
            }
            @Override
            public int next() {
                System.out.println(name +" "+count);
                return count++;
            }
        };

    }
    public static void main(String[] args) {
        LocalInnerClass lic = new LocalInnerClass();
        Counter c1 = lic.getCounter("Local Inner");
        Counter c2 = lic.getCounter2("Anonymous Inner");

        for (int i = 0; i < 5; i++) {
            c1.next();
        }
        for (int i = 0; i < 5; i++) {
            c2.next();
        }
    }
}

與匿名內部類比較:可以有一個命名的構造器,可以建立不止一個區域性內部類物件。

10.12 內部類識別符號

每個類都會產生.class檔案,內部類也會產生一個.class檔案,命名格式:外圍類名字+$+內部類名字。

10.13 總結

介面和內部類結合起來就能解決多重繼承的問題。