1. 程式人生 > >(Thinking in Java)內部類的簡單使用

(Thinking in Java)內部類的簡單使用

1. 成員內部類

1. 最基本使用

public class Demo {
    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 void ship(String dest){ Contents c=new Contents(); Destination d=new Destination(dest); System.out.println(d.readLabel()); } public static void main(String[] args) { Demo d=new Demo(); d.ship("Tasmania"); } }

2.內部類可以訪問外部類的成員

內部類可以訪問外部類的成員變數。如下:

public class Demo {
    private Object[] items;
    private int next = 0;

    public Demo(int size) {
        items = new Object[size];
    }

    public void add(Object x) {
        if (next < items.length) {
            items[next++] = x;
        }
    }

    private class
SequenceSelector implements Selector {
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 Selector selector() { return new SequenceSelector(); } public static void main(String[] args) { Demo d = new Demo(10); for (int i = 0; i < 10; i++) { d.add(Integer.toString(i)); } Selector selector = d.selector(); while (!selector.end()) { System.out.print(selector.current() + " "); selector.next(); } } } interface Selector { boolean end(); Object current(); void next(); }

因為在建立內部類物件的時候,內部類物件會捕獲一個指向外部類物件的引用。訪問外部類成員的時候,就是用這個引用來獲取外部類成員的。內部類中也可以取得這個外部類物件引用。舉例如下:

public class DotThis{
    void f(){
        System.out.println("DotThis.f()");
    }
    class Inner{
        public DotThis outer(){
            return DotThis.this;
            //A plain "this" would be Inner's 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語法。

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

當創造內部類物件的時候,如果這個內部類不是巢狀類(靜態內部類),那麼就必須要通過外部類物件,才能建立這個內部類物件,為什麼呢,之前說過,因為這個內部類物件要獲取外部類的引用啊。
並且在存在多個內部類的時候,多個內部類之間可以互相建立物件。例子就不舉了。

小結:現在所說的都是成員內部類,其實內部類沒那麼複雜,既然叫做成員內部類了,他就只是類的成員罷了。他也可以帶修飾符,他的修飾符和其他普通的成員變數的修飾符的意義也沒有什麼不同。

3. 內部類許可權修飾符

當內部類被private修飾的時候,該類只能被外部類內的方法使用,其他類不能獲取該內部類的引用,因為是private的,所以其他類根本不知道存在一個這樣的類。當然也可以作為一個成員變數使用,但是如果作為成員變數,則其他類並不能直接建立內部類的引用,需要用其他手段獲取該引用,如下:

class Outer{
    Inner in;
    private class Inner implements a_interface{
        void show(){
            System.out.println("123");
        }
    }
}
interface a_interface{
    void show();
}

class test{
    //Inner in=new Outer().in;這是錯誤的,因為test並不知道Outer類有一個Inner內部類,因為是私有的
    a_interface in=new Outer().in;//可以運用向上轉型的方法獲取private修飾的內部類。
}

小結:其實這也很好記,無論是public還是private,修飾到內部類上的時候,和他們修飾普通的成員變數(如string,int之類)的時候沒什麼不同,規則都一樣,public就都能使用,private就類內可以用。所以規則就記住三條就好:1.先考慮外部類的許可權,是否可以獲取一個外部類物件。2.建立成員內部類物件的時候需要外部類物件。3.考慮內部類的許可權,是否可以獲取這樣的一個內部類物件(或者說,在外部知不知道有這樣一個內部類)。

2. 方法和作用域內的內部類

當我們需要解決一個複雜的問題,想建立一個類來輔助解決問題,但是不希望這個類是公共可用的,甚至不希望在外部類之內的其他地方可以訪問到這個輔助類。我們可以運用方法內的內部類

public class Outer {
    public InterfaceDemo get_InterfaceDemo(String s) {
         class InterfaceDemoTool implements InterfaceDemo {
            private String label;

            private InterfaceDemoTool(String label) {
                this.label = label;
            }

            public String readLabel() {
                return label;
            }
        }

        return new InterfaceDemoTool(s);
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        InterfaceDemo i = o.get_InterfaceDemo("123");
    }
}

interface InterfaceDemo {
    String readLabel();
}

當然在方法中還可以定義多個內部類,並且這些內部類之間的關係和普通一個java檔案中多個類之間的關係好像沒什麼不同。也可以相互繼承和建立物件。另外在方法中的內部類不能加private等許可權修飾符,只能加abstract和final修飾符。
另外也可以在某個作用域內建立內部類物件

if(a==b){
    class inner{
    }
    new inner();
}

上面的例子中,在if外就不能知道有這麼個inner類了,他的作用域只在{…}之中,同理,在方法內定義的內部類,在方法外也不能知道存在這麼個類,因為這個內部類的作用域只在這個方法內。

3.匿名內部類

下面這塊程式碼中get_inner()的意思是,建立一個繼承自InnerFather的匿名類物件,並且自動向上轉型為InnerFather後返回。

public class Outer {
    public InnerFather get_inner() {
        return new InnerFather() {
            void print(){
                System.out.println("Inner_Override");
            }
        };
    }

    class InnerFather {
        InnerFather() {

        }
        void print(){
            System.out.println("InnerFather");
        }
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        InnerFather i = o.get_inner();
        i.print();
    }
}

當然這只是有無參建構函式,當父類只有一個含參建構函式的時候,我們可以這樣向匿名內部類傳入一個建構函式引數。

public class Outer {
    public InnerFather get_inner(int i) {
        return new InnerFather(i) {
            void print(){
                System.out.println("Inner_Override");
            }
        };
    }

    class InnerFather {
        InnerFather(int i) {

        }
        void print(){
            System.out.println("InnerFather");
        }
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        InnerFather i = o.get_inner(10);
        i.print();
    }
}

可以通過構造程式碼塊來實現匿名內部類的自定義的建構函式

abstract class Base {
    public Base(int i) {
        System.out.println("Base constructor");
    }

    public abstract void f();
}

public class AnonymousConstructor {
    public static Base getBase(int i){
        return new Base(i){
            {System.out.println("AnonymousConstructor constructor");}
            public void f(){
            }
        };
    }

    public static void main(String[] args) {
        Base base = getBase(47);
    }
}

(書上說,如果傳入了新的物件,就比如下面例子中的s_in,這個s_in就必須是final的,但是我實驗了一下發現並不用啊,我也沒太搞懂。)

abstract class Base {
    public Base(int i) {
        System.out.println("Base constructor");
    }

    public abstract void f();
}

public class AnonymousConstructor {
    public static Base getBase(int i,String s_in){
        return new Base(i){
            {System.out.println("AnonymousConstructor constructor");}
            String s=s_in;
            public void f(){
            }
        };
    }

    public static void main(String[] args) {
        Base base = getBase(47,"hello");
    }
}

4.巢狀類

巢狀類指的是被static修飾的內部類。這意味著:1.建立巢狀類物件不需要外部類物件。2.不能再巢狀類物件之中訪問非靜態的外圍類物件。普通內部類的成員和方法只能放在類的外部層次上(這句話我沒搞懂= =),所以普通內部類不能有static的成員和方法。但是巢狀類可以有。

public class Outer {
    static class Inner{
        static int i=5;
    }
    public static void main(String[] args) {
        Inner i=new Outer.Inner();
    }
}

可以從上面的例子看到,在建立這個巢狀類物件的時候,並沒有像最開始那樣,用一個外部類物件來建立這個內部類物件。其實這和靜態方法差不多。

可以在介面內部定義內部類,而且他們即使沒有static修飾,也會自動變成public static的。

public interface ClassInInterface {
    void howdy();
    class Test implements ClassInInterface{
        public void howdy(){
            System.out.println("howdy!");
        }
        public static void main(String[] args) {
            new Test().howdy();
        }
    }
}

書上有句話說的很好,在開發的時候建議在每個類中都寫一個main方法測試,但是這又必須帶著那些已經編譯過的額外程式碼,所以我們可以用巢狀類放置測試程式碼。

public class Outer {
    public void f(){
        System.out.println("I need to be tested");
    }

    public static class Tester{
        public static void main(String[] args) {
            Outer o=new Outer();
            o.f();
        }
    }
}

當然以上兩段程式碼如果是在eclipse上執行的話,需要設定一下執行的main函式在哪,否則會報錯

eclipse設定runconfigurations

5.多層內部類巢狀

紙老虎,愛有幾層有幾層,反正只要是外部類的東西,不管哪層外部類,都能訪問到。

public class Outer {
    void f(){
        System.out.println("hello");
    }
    class Inner1{
        void g(){
            System.out.println("java");
        }
        class Inner2{
            void h(){
                f();
                g();
            }
        }
    }
    public static void main(String[] args) {
        Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
        in2.h();
    }
}

下面是個好玩的

public class Outer {
    void f(){
        System.out.println("hello");
    }
    class Inner1{
        void f(){
            System.out.println("java");
        }
        class Inner2{
            void h(){
                f();
            }
        }
    }
    public static void main(String[] args) {
        Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
        in2.h();
    }
}

最後列印結果是java。

大概就這麼多吧,以後如果還有新東西再補。