天天看點

Java程式設計思想學習筆記1 - 内部類1. 内部類的基礎結構2. 内部類的優點和使用場景3. 内部類的分類4. 内部類的繼承參考資料

内部類簡單來說就是定義在一個類内部的類。一直很難了解為什麼要使用内部類,對内部類的了解始終停留在表明。今天詳細學習了Java内部類的機制,總結下内部類的使用。歸納大綱如下:

1. 内部類的基礎結構

2. 内部類的優點和使用場景

3. 内部類的分類

4. 内部類的繼承

若有不正之處,請批評指教,共同成長!請尊重作者勞動成果,轉載請标明原文連結

  • 1. 内部類的基礎結構
  • 2. 内部類的優點和使用場景
  • 3. 内部類的分類
    • 成員内部類
    • 局部内部類
    • 匿名内部類
    • 嵌套類
  • 4. 内部類的繼承
  • 參考資料

1. 内部類的基礎結構

package c10;

public class Parcel1 {
    class Contents {
        private int i = ;
        public int value(){
            return i;
        }
    }

    class Destination {
        private String label;
        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();
        Parcel1.Contents contents = p.new Contents();
        Parcel1.Destination dest = p.new Destination("Tasmania");
    }   
}

輸出結果:Tasmania
           

内部類其實是一個編譯時的概念(内部類與普通類的不同展現在編譯上),如上例所示,編譯完成後,分别生成三個class檔案:

Parcel1.class, Parcel1$Contents.class, Destination$Contents.class。

.class

檔案包含了如何建立該類型對象的全部資訊。内部類生成的

.class

檔案有嚴格的規則:

外部類名稱+$+内部類名稱

。如果是匿名内部類,編譯器會簡單地産生一個數字作為其表示符,如

Parcel1$1.class

.

2. 内部類的優點和使用場景

Thinking in Java中通過一個章節詳細讨論了為什麼需要内部類,可能是因為筆者的了解能力有限,直到今天也無法明确體會作者的意思,總結起來有以下這些:

1. 内部類最吸引人的原因是:每個内部類都能獨立繼承一個(接口或類)的實作,是以無論外圍類是否已經繼承了某個(接口或類)實作,對于内部類都沒有影響

2. 内部類可以有多個執行個體,每個執行個體都有自己的狀态資訊,并且與外圍類對象的資訊互相獨立

3. 單個外圍類中,可以讓多個内部類以不同的方式實作同一個接口,或內建同一個類

4. 内部類并沒有令人迷惑的”is-a”關系,是一個獨立的實體

也就是說通過内部類可以變相的實作類的多重繼承(我們知道Java中隻能引用多個接口,而不能繼承多個類)。比如這樣:

//基類A、B、C
class A {}
abstract class B {}
class C {}

//派生類通過内部類同時繼承ABC
class Z extends A {
    class Z1 extends B{
        C makeC() { return new C(){}; }
    }
}
           

也看了網上各位大神的文章,總結歸納,自己對為什麼使用内部類的了解是這樣的:

使用内部類會破壞良好的代碼結構(第一次看到會覺得怪怪的),但為類的設計者提供了一種途徑來隐藏類的實作細節(這些往往是用戶端程式員所不關注的),同時也是代碼變的更加靈活。

3. 内部類的分類

筆者認為内部類之是以很難了解,正是因為文法覆寫了大量難以了解的技術(如果都像基礎内部類那樣,就沒有多少意思了)。内部類可以分為四種:成員内部類,局部内部類,嵌套類,匿名内部類。

  • 靜态内部類的應用場景是:隻可以通路外部類的靜态成員變量和靜态成員方法。
  • 成員内部類的應用場景是:它可以通路它的外部類的所有成員變量和方法,不管是靜态的還是非靜态的都可以。
  • 局部内部類:像局部變量一樣,不能被public, protected,

    private和static修飾。隻能通路方法中定義的final類型的局部變量。

  • 匿名内部類:匿名内部類就是沒有名字的局部内部類,不使用關鍵字class, extends, implements,沒有構造方法。匿名内部類隐式地繼承了一個父類或者實作了一個接口。匿名内部類使用得比較多,通常是作為一個方法參數。

成員内部類

成員内部類擁有對外部類所有元素的通路權。

在成員内部類要引用外部類對象時,使用

outer.this

來表示外部類對象;

而需要建立内部類對象,可以使用

outer.inner obj = outer.new inner();

(注意,在擁有外部類對象之前是不可能建立内部類對象的,除非你建立的是嵌套類)

舉個例子:

package c10;

public class Parcel {
    private int num = ;

    class Contents {
        private int num = ;
        public void print() {
            int num = ;
            System.out.println("局部變量:" + num);
            System.out.println("内部局部變量:" + this.num);
            System.out.println("外部局部變量:" + Parcel.this.num);
        }

    }

    public static void main(String[] args) {
        Parcel p = new Parcel();
        Parcel.Contents c = p.new Contents();
        c.print();
    }
}

局部變量:
内部局部變量:
外部局部變量:
           

局部内部類

當你要解決一個複雜的問題,想建立一個類來輔助你的解決方案,但又不希望這個類是公共可用的時,可以通過以下方式實作:

  • 一個定義在方法中的類
  • 一個定義在作用域内的類
  • 一個實作了接口的匿名類
  • 一個擴充了非預設構造器的匿名類
  • 執行字段初始化的匿名類

定義在方法中的内部類:

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) { 
        Parcel4 p = new Parcel4(); 
        Destination d = p.destination("Tasmania"); 
    } 
} 

Tasmania
           

PDestination

類是

destination()

方法的一部分,在之外不能被通路。注意

return

語句中的向上轉型,傳回的是

Destination

的引用,它是

PDestination

的基類。

定義在作用域中的内部類:

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) { 
        Parcel5 p = new Parcel5(); 
        p.track(); 
    } 
} 
           

匿名内部類

一個匿名内部類的例子如下,匿名類是内部類比較常用的方式,簡化了代碼,更加靈活:

package c10;

//注釋後,編譯報錯:Contents cannot be resolved to a type
//interface Contents { }

public class Parcel7 {
    public Contents contents() {
        return new Contents() {
            private int i = ;
            public int value(){ return i; }
        };
    }

    public static void main(String[] args) {
        Parcel7 p = new Parcel7();
        Contents c = p.contents();
    }

}
           

需要注意:

1. new匿名類前,這個類需要定義,否則編譯報錯;

2. 當所在的方法的形參需要被内部類裡面使用時,該形參必須為final,否則編譯報錯,如下例所示:

package c10;

interface Destination {}

public class Parcel10 {
    public Destination destination(
            final String dest,final float price) {
        return new Destination() {
            private int cost;
            {
                cost = Math.round(price);
                if( cost >  ) {
                    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("Nanjing", F);
    }
}
           

通過匿名内部類,可以寫出一個完美的工廠模式:

package c10;

interface Service {
    void method1();
    void method2();
}

interface ServiceFacotry {
    Service getService();
}

class Implementation1 implements Service {
    private Implementation1() {}

    @Override
    public void method1() {
        System.out.println("Implementation1 method1");      
    }
    @Override
    public void method2() {
        System.out.println("Implementation1 method2");          
    }
    public static ServiceFacotry factory = 
            new ServiceFacotry() {              
                @Override
                public Service getService() {
                    return new Implementation1();
                }
            };
}

class Implementation2 implements Service {
    private Implementation2() {}

    @Override
    public void method1() {
        System.out.println("Implementation2 method1");      
    }
    @Override
    public void method2() {
        System.out.println("Implementation2 method2");          
    }
    public static ServiceFacotry factory = 
            new ServiceFacotry() {              
                @Override
                public Service getService() {
                    return new Implementation2();
                }
            };
}

public class Factories {
    public static void serviceConsumer(ServiceFacotry fact) {
        Service s = fact.getService();
        s.method1();
        s.method2();
    }
    public static void main(String[] args) {
        serviceConsumer(Implementation1.factory);
        serviceConsumer(Implementation2.factory);
    }
}

Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
           

嵌套類

如果不需要内部類對象與其外部類對象之間有聯系,那麼可以将内部類聲明為static。嵌套類意味着:

1. 要建立嵌套類的對象,并不需要先建立外部類的對象

2. 不能從嵌套類的對象中通路非靜态的外部類對象

3. 嵌套類和普通的内部類還有一個差別:普通内部類不能有

static

資料和

static

屬性,也不能包含嵌套類,但嵌套類可以。而嵌套類不能聲明為

private

,一般聲明為

public

,友善調用。

package c10;

public class Parcel11 {
    private static int age = ;

    static class Contents {
        public void print() {
            System.out.println(age);
        }
    }

    public static void main(String[] args) {
        Contents c = new Contents();
        c.print();
    }
}


           

4. 内部類的繼承

内部類的繼承,是指内部類被繼承,普通類

extends

内部類。而這時候代碼上要有點特别處理,具體看以下例子:

public class InheritInner extends WithInner.Inner { 

    // InheritInner() 是不能通過編譯的,一定要加上形參 
    InheritInner(WithInner wi) { 
        wi.super(); 
    } 

    public static void main(String[] args) { 
        WithInner wi = new WithInner(); 
        InheritInner obj = new InheritInner(wi); 
    } 
} 

class WithInner { 
    class Inner {  
    } 
} 
           

可以看到子類的構造函數裡面要使用父類的外部類對象

.super()

;而這個對象需要從外面建立并傳給形參。

參考資料

  • 《Thinking in Java》第4版
  • http://www.cnblogs.com/dolphin0520/ 作者:海子
  • http://www.cnblogs.com/nerxious/archive/2013/01/24/2875649.htm作者:Nerxious
  • http://android.blog.51cto.com/268543/384844/ 作者:Icansoft