Java™ 教程(嵌套類)

嵌套類

Java編程語言容許你在另外一個類中定義類,這樣的類稱爲嵌套類,以下所示:html

class OuterClass {
    ...
    class NestedClass {
        ...
    }
}
術語:嵌套類分爲兩類:靜態和非靜態,聲明爲 static的嵌套類稱爲靜態嵌套類,非靜態嵌套類稱爲內部類。
class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}

嵌套類是其封閉類的成員,非靜態嵌套類(內部類)能夠訪問封閉類的其餘成員,即便它們被聲明爲private,靜態嵌套類無權訪問封閉類的其餘成員。做爲OuterClass的成員,能夠將嵌套類聲明爲privatepublicprotected或包私有(回想一下,外部類只能聲明爲public或包私有)。java

爲何使用嵌套類?

使用嵌套類的使人信服的理由包括:編程

  • 這是一種邏輯分組僅在一個地方使用的類的方法:若是一個類只對另外一個類有用,那麼將它嵌入該類並將二者保持在一塊兒是合乎邏輯的,嵌套這樣的「幫助類」使得它們的包更加簡化。
  • 它增長了封裝:考慮兩個頂級類A和B,其中B須要訪問A的成員,不然這些成員將被聲明爲private,經過將類B隱藏在類A中,能夠將A的成員聲明爲私有,而且B能夠訪問它們,另外,B自己也能夠不被外界發現。
  • 它可使代碼更具可讀性和可維護性:在頂級類中嵌套小類會使代碼更接近於使用它的位置。

靜態嵌套類

與類方法和變量同樣,靜態嵌套類與其外部類相關聯,和靜態類方法同樣,靜態嵌套類不能直接引用其封閉類中定義的實例變量或方法:它只能經過對象引用來使用它們。segmentfault

注意:靜態嵌套類與其外部類(和其餘類)的實例成員交互,就像任何其餘頂級類同樣,實際上,靜態嵌套類在行爲上是一個頂級類,它已嵌套在另外一個頂級類中以方便打包。

使用封閉的類名訪問靜態嵌套類:api

OuterClass.StaticNestedClass

例如,要爲靜態嵌套類建立對象,請使用如下語法:數組

OuterClass.StaticNestedClass nestedObject =
     new OuterClass.StaticNestedClass();

內部類

與實例方法和變量同樣,內部類與其封閉類的實例相關聯,而且能夠直接訪問該對象的方法和字段,此外,因爲內部類與實例相關聯,所以自己沒法定義任何靜態成員。數據結構

做爲內部類的實例的對象存在於外部類的實例中,考慮如下類:oracle

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

InnerClass的實例只能存在於OuterClass的實例中,而且能夠直接訪問其封閉實例的方法和字段。編程語言

要實例化內部類,必須首先實例化外部類,而後,使用如下語法在外部對象中建立內部對象:函數

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

有兩種特殊的內部類:局部類和匿名類。

遮蔽

若是特定範圍(例如內部類或方法定義)中的類型聲明(例如成員變量或參數名稱)與封閉範圍中的另外一個聲明具備相同的名稱,而後聲明會影響封閉範圍的聲明,你不能僅經過其名稱引用遮蔽的聲明,如下示例ShadowTest演示了這一點:

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

如下是此示例的輸出:

x = 23
this.x = 1
ShadowTest.this.x = 0

此示例定義了三個名爲x的變量:ShadowTest類的成員變量、內部類FirstLevel的成員變量、以及方法methodInFirstLevel中的參數。變量x定義爲方法methodInFirstLevel的參數遮蔽內部類FirstLevel的變量,所以,當你在方法methodInFirstLevel中使用變量x時,它引用方法參數,要引用內部類FirstLevel的成員變量,請使用關鍵字this來表示封閉範圍:

System.out.println("this.x = " + this.x);

引用成員變量,這些成員變量根據所屬的類名包含更大的做用域,例如,如下語句從方法methodInFirstLevel訪問類ShadowTest的成員變量:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

序列化

強烈建議不要對內部類(包括局部類和匿名類)進行序列化,當Java編譯器編譯某些構造(如內部類)時,它會建立合成構造,這些是類、方法、字段和其餘在源代碼中沒有相應構造的構造。合成構造使Java編譯器可以在不更改JVM的狀況下實現new的Java語言功能,可是,合成構造能夠在不一樣的Java編譯器實現之間變化,這意味着.class文件也能夠在不一樣的實現之間變化。所以,若是序列化內部類,而後使用其餘JRE實現反序列化,則可能存在兼容性問題,有關在編譯內部類時生成的合成構造的更多信息,請參閱獲取方法參數名稱一節中的隱式和合成參數部分。

內部類示例

要看內部類的使用,首先要考慮一個數組,在下面的示例中,你將建立一個數組,用整數值填充它,而後僅按升序輸出數組的偶數索引值。

下面的DataStructure.java示例包括:

  • DataStructure外部類,它包含一個構造函數,用於建立DataStructure的實例,該實例包含一個數組,該數組填充了連續的整數值(0、一、二、3等等),以及一個方法,該方法打印具備偶數索引值的數組元素。
  • EvenIterator內部類,它實現了繼承了Iterator< Integer>接口的DataStructureIterator接口,迭代器用於逐步執行數據結構,一般有方法來測試最後一個元素,檢索當前元素,而後移動到下一個元素。
  • 實例化DataStructure對象(ds)的main方法,而後調用printEven方法來打印具備偶數索引值的數組arrayOfInts的元素。
public class DataStructure {
    
    // Create an array
    private final static int SIZE = 15;
    private int[] arrayOfInts = new int[SIZE];
    
    public DataStructure() {
        // fill the array with ascending integer values
        for (int i = 0; i < SIZE; i++) {
            arrayOfInts[i] = i;
        }
    }
    
    public void printEven() {
        
        // Print out values of even indices of the array
        DataStructureIterator iterator = this.new EvenIterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
        System.out.println();
    }
    
    interface DataStructureIterator extends java.util.Iterator<Integer> { } 

    // Inner class implements the DataStructureIterator interface,
    // which extends the Iterator<Integer> interface
    
    private class EvenIterator implements DataStructureIterator {
        
        // Start stepping through the array from the beginning
        private int nextIndex = 0;
        
        public boolean hasNext() {
            
            // Check if the current element is the last in the array
            return (nextIndex <= SIZE - 1);
        }        
        
        public Integer next() {
            
            // Record a value of an even index of the array
            Integer retValue = Integer.valueOf(arrayOfInts[nextIndex]);
            
            // Get the next even element
            nextIndex += 2;
            return retValue;
        }
    }
    
    public static void main(String s[]) {
        
        // Fill the array with integer values and print out only
        // values of even indices
        DataStructure ds = new DataStructure();
        ds.printEven();
    }
}

輸出是:

0 2 4 6 8 10 12 14

請注意,EvenIterator類直接引用DataStructure對象的arrayOfInts實例變量。

你可使用內部類來實現輔助類,例如本示例中所示的類,要處理用戶界面事件,你必須知道如何使用內部類,由於事件處理機制會普遍使用它們。

局部和匿名類

還有兩種類型的內部類,你能夠在方法體內聲明內部類,這些類稱爲局部類,你還能夠在方法體內聲明內部類,而無需命名該類,這些類稱爲匿名類。

修飾符

你能夠對用於外部類的其餘成員的內部類使用相同的修飾符,例如,你可使用訪問修飾符privatepublicprotected來限制對內部類的訪問,就像你使用它們來限制對其餘類成員的訪問同樣。


上一篇:類的更多方面

下一篇:局部類

相關文章
相關標籤/搜索