1. 程式人生 > 其它 >Java內部類-成員內部類、區域性內部類、匿名內部類、靜態內部類

Java內部類-成員內部類、區域性內部類、匿名內部類、靜態內部類

在 Java 中,可以將一個類定義在另一個類裡面或者一個方法裡面,這樣的類稱為內部類。廣泛意義上的內部類一般來說包括這四種:成員內部類、區域性內部類、匿名內部類和靜態內部類。我們知道類的五大成員:屬性、方法、構造器、程式碼塊、內部類。內部類最大的特點就是可以直接訪問私有屬性,並且可以體現類與類之間的包含關係。

1.成員內部類

成員內部類是最普通的內部類,它的定義為位於另一個類的內部

成員內部類語法格式

/**
 * @author joshua317
 */
public class Outer {//外部類

    class Inner {//內部類

    }
}

類Inner像是類Outer的一個成員,Outer稱為外部類。成員內部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態成員)

package com.joshua317;

/**
 * @author joshua317
 */
public class Outer {//外部類
    private String name;
    public static void main(String[] args) {
        Outer outerObj = new Outer("joshua317");
        Outer.Inner inner = outerObj.new Inner();
        inner.InnerFun1();
    }

    public Outer(String name) {
        this.name = name;
    }

    public void OuterFun1() {
        System.out.println("外部類成員方法");
    }

    class Inner {//內部類
        public void InnerFun1() {
            System.out.println(name);
        }
    }
}

雖然成員內部類可以無條件地訪問外部類的成員,而外部類想訪問成員內部類的成員卻不是這麼隨心所欲了。在外部類中如果要訪問成員內部類的成員,必須先建立一個成員內部類的物件,再通過指向這個物件的引用來訪問。

當成員內部類擁有和外部類同名的成員變數或者方法時,會根據就近原則,即預設情況下訪問的是成員內部類的成員。如果要訪問外部類的同名成員,需要以下面的形式進行訪問:

外部類.this.成員變數
外部類.this.成員方法
package com.joshua317;

/**
 * @author joshua317
 */
public class Outer {//外部類
    private String name;
    public static void main(String[] args) {
        Outer outerObj = new Outer("joshua317");
        Outer.Inner inner = outerObj.new Inner();
        inner.InnerFun1();
    }

    public Outer(String name) {
        this.name = name;
    }

    public void OuterFun1() {
        System.out.println("外部類成員方法");
    }

    class Inner {//內部類
        String name;
        public void InnerFun1() {
            //訪問外部類的同名成員屬性
            System.out.println("我是內部類的成員屬性name:"+ name + " 我是外部類的成員屬性name:"+Outer.this.name);
        }
    }
}

成員內部類是依附外部類而存在的,也就是說,如果要建立成員內部類的物件,前提是必須存在一個外部類的物件。建立成員內部類物件的一般方式如下:

package com.joshua317;
public class TestInner {
    public static void main(String[] args)  {
        //第一種方式:
        Outter outter = new Outter();
        Outter.Inner inner = outter.new Inner();  //必須通過Outter物件來建立
         
        //第二種方式:
        Outter.Inner inner1 = outter.getInnerInstance();
    }
}
 
class Outter {
    private Inner inner = null;
    public Outter() {
         
    }
     
    public Inner getInnerInstance() {
        if(inner == null)
            inner = new Inner();
        return inner;
    }
      
    class Inner {
        public Inner() {
             
        }
    }
}

成員內部類可以擁有 private 訪問許可權、protected 訪問許可權、public 訪問許可權及包訪問許可權。比如上面的例子,如果成員內部類 Inner 用 private 修飾,則只能在外部類的內部訪問,如果用 public 修飾,則任何地方都能訪問;如果用 protected 修飾,則只能在同一個包下或者繼承外部類的情況下訪問;如果是預設訪問許可權,則只能在同一個包下訪問。這一點和外部類有一點不一樣,外部類只能被 public 和包訪問兩種許可權修飾。由於成員內部類看起來像是外部類的一個成員,所以可以像類的成員一樣擁有多種許可權修飾。

2.區域性內部類

區域性內部類是定義在一個方法或者一個作用域裡面的類,它和成員內部類的區別在於區域性內部類的訪問僅限於方法內或者該作用域內。

區域性內部類語法格式:

/**
 * @author joshua317
 */
public class Outer {//外部類

    public void OuterFun1() {
        System.out.println("外部類成員方法");
        class Inner {//區域性內部類
            
        }
    }
}

**區域性內部類就像是方法裡面的一個區域性變數一樣,是不能有 public、protected、private 以及 static 修飾符的。**總結下來有這幾點:

(1)區域性內部類可以直接訪問外部類的所有成員,包含私有的;

(2)不能新增修飾符,因為它是一個區域性變數,區域性變數是不能使用修飾符的,但是可以用final修飾,因為區域性變數是可以使用final修飾的。

(3)作用域僅僅在定義它的方法或者程式碼塊中。

(4)區域性內部類訪問外部類的成員屬性或者方法時,直接訪問;

(5)外部類訪問區域性內部類的成員屬性或者方法時,通過先建立物件,再訪問,且必須要在作用域內。

package com.joshua317;

/**
 * @author joshua317
 */
public class Outer {//外部類
    private String name;
    public static void main(String[] args) {
        Outer outerObj = new Outer("joshua317");
        outerObj.OuterFun1();
    }

    public Outer(String name) {
        this.name = name;
    }

    public void OuterFun1() {
        System.out.println("外部類成員方法");
        class Inner {//內部類
            String name;
            public void InnerFun1() {
                //訪問外部類的同名成員屬性
                System.out.println("區域性內部類的成員屬性name:"+ name + " 我是外部類的成員屬性name:"+Outer.this.name);
            }
            public void setName(String name) {
                this.name = name;
            }

        }
        Inner inner = new Inner();
        inner.setName("innerName");
        inner.InnerFun1();
        System.out.println("區域性內部類的成員屬性name:" + inner.name);
    }
}

3.匿名內部類

Java 中可以實現一個類中包含另外一個類,且不需要提供任何的類名直接例項化。主要是用於在我們需要的時候建立一個物件來執行特定的任務,可以使程式碼更加簡潔。匿名類是不能有名字的類,它們不能被引用,只能在建立時用new語句來宣告它們。

匿名內部類語法格式:

package com.joshua317;

/**
 * @author joshua317
 */
public class Outer {//外部類
    Object obj = new Inner(){
        @Override
        public void innerFun1() {
            
        }
    };
}

interface Inner {
    public void innerFun1();
}

匿名內部類也是不能有訪問修飾符和 static 修飾符的。

匿名內部類是唯一一種沒有構造器的類。正因為其沒有構造器,所以匿名內部類的使用範圍非常有限,大部分匿名內部類用於介面回撥。

匿名內部類在編譯的時候由系統自動起名為 Outer$1.class。

匿名內部類主要用於繼承其他類或是實現介面,並不需要增加額外的方法,只是對繼承方法的實現或是重寫。

匿名內部類使用一次,就不再使用。

package com.joshua317;

/**
 * @author joshua317
 */
public class Outer {//外部類

    public static void main(String[] args) {

        /**
         * 底層會分配一個類名Outer$1,去實現Inner介面
         * class Outer$1 implements Inner {
         *             @Override
         *             public void innerFun1() {
         *                 System.out.println("實現介面Inner,匿名內部類方法innerFun1()");
         *             }
         *         }
         */
        Inner inner = new Inner(){
            @Override
            public void innerFun1() {
                System.out.println("實現介面Inner,匿名內部類方法innerFun1()");
            }
        };
        System.out.println(inner);
        inner.innerFun1();

        /**
         * 底層會分配一個類名Outer$2,去繼承InnerClass
         * class Outer$1 extends InnerClass {
         *             @Override
         *             public void innerFun1() {
         *                 System.out.println("匿名類繼承了 InnerClass 類,匿名內部類方法innerFun1()");
         *             }
         *         }
         */
        Inner inner2 = new InnerClass(){
            @Override
            public void innerFun1() {
                System.out.println("匿名類繼承了 InnerClass 類,匿名內部類方法innerFun1()");
            }
        };
        System.out.println(inner2);
        inner2.innerFun1();
    }
}

class InnerClass implements Inner {
    @Override
    public void innerFun1() {

    }
}

interface Inner {
    public void innerFun1();
}

4.靜態內部類

靜態內部類也是定義在另一個類裡面的類,只不過在類的前面多了一個關鍵字static。靜態內部類是不需要依賴於外部類的,這點和類的靜態成員屬性有點類似,並且它不能使用外部類的非static成員變數或者方法,這點很好理解,因為在沒有外部類的物件的情況下,可以建立靜態內部類的物件,如果允許訪問外部類的非static成員就會產生矛盾,因為外部類的非static成員必須依附於具體的物件。可以新增任意的訪問修飾符public、protected、private 以及預設,因為它就是類的成員。作用域和其他類成員一樣,為整個類體。

靜態內部類語法格式:

/**
 * @author joshua317
 */
public class Outer {//外部類

    static class Inner {//內部類

    }
}
package com.joshua317;


import javax.swing.*;

/**
 * @author joshua317
 */
public class Outer {//外部類
    static String name="joshua317";
    public Outer() {
    }
    public static void main(String[] args) {
        Outer.Inner.innerFun();
    }
    static class Inner {//內部類
        public static void innerFun () {
            System.out.println(name);
        }
    }
}

5.內部類的使用好處

  • 1.每個內部類都能獨立的繼承一個介面的實現,所以無論外部類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響。內部類使得多繼承的解決方案變得完整;
  • 2.方便將存在一定邏輯關係的類組織在一起,又可以對外界隱藏;
  • 3.方便編寫事件驅動程式;
  • 4.方便編寫執行緒程式碼。