1. 程式人生 > >Java內部類學習小記

Java內部類學習小記

這也是本人關於技術進行學習記錄的第一篇部落格。

那麼就從Java的內部類開始吧。
這篇blog主要分為三個部分的內容:
一、內部類的基本介紹
二、幾種內部類的實現形式及內部類的一些邊角使用知識
三、為什麼我們要使用內部類

一、內部類的基本介紹

  1. 內部類的基本實現

    稍微有點Java基礎的應該都對內部類這名詞有所瞭解,我們知道Java是一門純粹的面向物件的程式語言,如果一個類是在另一個類的定義中定義的,則我們將其稱之為內部類。(以上純屬個人的理解,沒有什麼查詢特別官方的說明)

public class OuterClass {

    private String outerValue;

    public
OuterClass() { System.out.print("this is OuterClass"); } private class InnerClass { private String innerValue; public InnerClass() { System.out.print("this is InnerClass"); } } }

以上是一個簡單的內部類的程式碼示例,InnerClass即是OuterClass的內部類。
2. 連結到外部類

首先,我們要明確的是對於非靜態內部類,其類例項的建立需要通過其外圍類的例項來實現,也就是說,如果你需要使用一個InnerClass類的例項,首先必須存在一個OuterClass例項。程式碼如下:

public static void main(String args[]){
    OuterClass outer = new OuterClass();
    InnerClass inner = outer.new InnerClass();
}

反過來,對於一般的內部類(非static修飾的內部類)來說,當你建立inner物件時,編譯器會祕密地捕獲一個指向outer這個例項的指標,通過這個指標使得inner能夠方便地訪問到outer的所有成員變數(注意是所有,包含private的和static修飾的),通過外部類類名+“.this”來獲得對其外部類例項物件的引用(這樣產生的引用自動地具有正確的型別,並在編譯期就被知曉並受到檢查,沒有任何執行時開銷

),例如我們修改下上面關於OuterClass和InnerClass的定義如下:

public class OuterClass {

    private String outerValue = "hello world!";

    public OuterClass() {
        System.out.println("this is OuterClass");
    }

    private class InnerClass {

        //private String innerValue;

        public InnerClass() {
            System.out.println("this is InnerClass");
        }

        public String getOuterValue(){
            return OuterClass.this.outerValue;
        }
    }

    public static void main(String args[]){
        OuterClass outer = new OuterClass();
        InnerClass inner = outer.new InnerClass();
        System.out.println(inner.getOuterValue());
    }
}

執行結果如下:

this is OuterClass
this is InnerClass
hello world!

二、幾種內部類的實現形式及內部類的一些邊角使用知識

  1. 區域性內部類
    我們會發現上面的InnerClass是跟OuterClass的成員變數定義在一樣的空間裡面的,但其實內部類的定義位置比上面的要自由得多,我們可以在一個方法或者在出了類作用域以外的作用域裡面定義一個內部類,我們把這種定義在某個方法或者作用域中的類又稱之為區域性內部類。我們再次修改下InnerClass的定義,將其放到OuterClass的一個方法裡面去定義:
public interface Count {
    public void getNumber();
}

public class OuterClass {

    private String outerValue = "hello world!";

    public OuterClass() {
        System.out.println("this is OuterClass");
    }

    public Count getInner() {
        class InnerClass implements Count {

            public InnerClass() {
                System.out.println("this is InnerClass");
            }
            @Override
            public void getNumber() {

            }
            public String getOuterValue() {
                return OuterClass.this.outerValue;
            }
        }
        return new InnerClass();
    }
}

注意到InnerClass是定義在OuterClass的getInner方法裡的,這也意味著出了getInner方法的作用域,InnerClass就變得無法使用了,這裡InnerClass通過實現Count介面向上轉型作為getInner方法的返回值返回其例項,我曾嘗試將getInner的返回值修改為InnerClass,編譯器報錯說找不到指定的InnerClass類,也進一步證明了區域性內部類的作用域範圍。
2. 匿名內部類
先貼程式碼吧:

   public class OuterClass {

    private String outerValue = "hello world!";

    public OuterClass() {
        System.out.println("this is OuterClass");
    }

    public InnerClass getInner() {

        return new InnerClass(){
           {
               System.out.println("This is initializer");
           }
           @Override
           public void writeSomething(){
               System.out.println("I'm InnerClass of InnerClass without name");
           }
        };
    }

    class InnerClass implements Count {

        //private String innerValue;
        public InnerClass() {
            System.out.println("this is InnerClass");
        }

        @Override
        public void getNumber() {

        }

        public void writeSomething(){
            System.out.println("I'm InnerClass");
        }
    }

    public static void main(String[] args){
        OuterClass outer = new OuterClass();
        outer.getInner().writeSomething();
    }

} 

執行輸出如下:

this is OuterClass
this is InnerClass
This is initializer
I'm InnerClass of InnerClass without name

我們注意到OuterClass有一個叫做getInner的方法,該方法返回一個InnerClass的物件,然而在我們的程式碼裡又重寫了InnerClass的getNumber方法。從輸出可以發現,其實我們返回的是一個繼承了InnerClass的子類的物件,並且這個類沒有他的名字。由於匿名內部類沒有其自己的名字,因此也不可能通過建構函式來進行初始化,他的例項初始化只能通過用“例項初始化”的程式碼塊來進行,就如同上文中輸出“This is initializer”的程式碼塊一樣。