淺談Java中的四種內部類

概述

如果你看過一些JDK和框架源碼的話,就經常會發現一般在類的定義中,都會再定義一些其他的類,這些類也同樣會被編譯成字節碼文件,這樣的類就被叫做內部類,按照一般的分法,大致可以分爲以下四類:

  • 成員內部類
  • 局部內部類
  • 匿名內部類
  • 靜態內部類

接下來會針對這四種內部類進行詳細講解,旨在解釋這些類的特點和應用場景,如果你懶得看的話,可以直接翻到最底下看總結

我們預先定義好一個類,接來下的所有操作都會在這個類中執行:

class Demo {
    
    public int pubVal = 1;
    
    private int priVal = 1;
    
    public static void staticMethod() {}
    
    public void publicMethod() {}
    
    private void privateMethod() {}
    
}

成員內部類

概念

從名字就可以看出,這種內部類是作爲類的成員而存在的,其定義位於類的內部,可以類比爲成員變量來理解

實現
class Demo {

	// ...	
	
    class InternalClass {
        
    }
    
    // ...
    
}

這樣就是一個成員內部類了

約束

首先是類定義的約束,實際上,成員內部類的類定義沒有任何約束(不涉及static,因爲這屬於靜態內部類的範疇),不僅可以將內部類聲明爲public、private的,甚至可以將其聲明爲一個abstract抽象類和interface接口,以及繼承外部類也是允許的,其定義是十分寬鬆的

接着是內部屬性和方法,這裏就有一些約束了:

  • 不能使用static來修飾任何成員內部類中的屬性和方法,但可以使用private、final等其餘任意修飾符
  • 可以用static final來修飾成員
  • 可以允許與外部類字段和方法重名

也就是說,成員內部類不能存在靜態屬性和方法,這麼做也是符合成員變量的含義

最後是訪問上的約束,這裏十分重要:

  • 成員內部類也存在this指針,但是這個指針指向的是自己的引用,如果想訪問外部類,需要使用外部類名.this這個指針
  • 可以通過外部類名.靜態字段來訪問外部類的靜態屬性或方法字段

這裏的限制就和成員方法是類似的,在理解時可以進行類比

使用

先來說聲明,以本文中程序爲例,我們可以通過下面的方式來聲明一個內部類:

        Demo.InternalClass demo

注意,如果你將這個內部類聲明爲private的,外部類依然可以訪問,但是外部類之外的其他類是無法訪問到的

接着就是通過new來創建,我們在外部類的任何非靜態方法中,都可以通過new來進行創建,具體格式如下:

        Demo.InternalClass demo = new Demo.InternalClass();

同樣地,我們可以在外部類中,通過內部類的實例來訪問其內部的私有變量和方法

局部內部類

我們剛花了大力氣來講解成員內部類,而局部內部類和成員內部類十分類似,可能一些相同的地方我就一筆帶過了

概念

局部內部類位於外部類成員方法的內部,可以類比局部變量

實現
    public void publicMethod() {
        class InternalClass {
            
        }
    }
約束

這裏的約束基本和成員內部類類似,我就單獨說一些不同的地方:

  • 其類上不允許有任何修飾符,但是可以使用abstract將類聲明爲抽象類
  • 不允許將局部內部類聲明爲接口
  • 不允許使用static來聲明成員變量和方法
  • 可以將局部內部類聲明在靜態方法中
  • 任意兩個方法中的局部內部類可以重名

其餘的基本和成員內部類類似,把局部內部類當作成員內部類的局部變量版本就好理解了,比如也擁有外部類的指針,使用方法和成員內部類一致

使用

既然是局部內部類,就只能在聲明類的方法處來使用,聲明和使用方式如下:

    public void publicMethod() {
        class InternalClass {

        }

        InternalClass test = new InternalClass();
    }

同樣地,我們依然可以無條件訪問內部類中定義的私有屬性

匿名內部類

這種內部類應該是我們使用的最多的一種,有時候甚至我們已經使用過了卻沒有發現

概念

匿名內部類沒有類的聲明,會隱式地繼承一個類或實現一個接口

實現

概念比較抽象,我們直接看是如何定義的,這裏我們有一個接受一個對象參數的方法:

    private void privateMethod() {
        new Demo() {
            public int newVal = 20;

            @Override
            public void publicMethod() {
                super.publicMethod();
            }
        };
    }

這裏的Demo可以換成任意一個類或者接口,你會發現這個類沒有名字,所以被叫做匿名內部類

如果Demo是一個普通類,則匿名內部類相當於這個類的子類;如果Demo是一個接口或者抽象類,則這個匿名內部類相當於接口或抽象類的實現

約束

想要理解匿名內部類的約束,就需要將整個匿名內部類不要當成一塊程序邏輯來看,而應該當成一個對象來處理,整塊匿名內部類完全可以當成一個對象,可以調用對象的方法、屬性等等

其主要的約束有以下這些:

  • 不能使用static來修飾方法和屬性,但是可以有static final的屬性
  • 可以使用this指針來訪問本身定義的變量和繼承得到的變量,也可以使用外部類名.this指針來訪問外部類中的所有屬性
  • 無法在類上進行任何修飾,因爲沒有class定義符和類名
  • 其中定義的私有字段對外是完全可見的
使用

這裏就不進行過多講解了,完全和普通對象的用法一致,這裏就舉兩個簡單的例子:

        int val = new Demo() {
            private int newVal = 20;

            @Override
            public void publicMethod() {
                super.publicMethod();
            }
        }.newVal;
        
        Demo demo = new Demo() {
            private int newVal = 20;

            @Override
            public void publicMethod() {
                super.publicMethod();
            }
        };

靜態內部類

概念

靜態內部類相當於static修飾的成員內部類,可以當作靜態變量來理解

實現
class Demo {

	// ...	
	
    static class InternalClass {
        
    }
    
    // ...
    
}
約束

這裏的約束就和之前的有很大不同了,如下:

  • 可以使用任意的修飾符來修飾類、方法和屬性
  • 可以訪問外部類的靜態方法和屬性

沒錯,廣義上,靜態內部類根本沒有約束

使用

使用有以下兩種情況:

在外部類的方法中使用時,通過如下語句來創建內部類對象:

		InternalClass test = new InternalClass();

在外部類之外的其他類可以通過下面的語句來創建內部類對象:

        Demo.InternalClass test = new Demo.InternalClass();

總結

最後,爲大家總結了一張表,基本上內部類的知識點都在這裏了:

成員內部類 局部內部類 匿名內部類 靜態內部類
類比 成員變量 局部變量 子類或接口的實現類 靜態變量
聲明位置 類的內部 作用域內部 任意對象可能出現的位置 與成員內部類一致
類修飾符 除static外任意 只允許用abstract修飾 無法修飾 任意的修飾符均可
變量和方法修飾符 除static外任意(但是可以用static final修飾變量) 與成員內部類一致 與成員內部類一致 任意修飾符均可
外部類的訪問權限 均可 均可 均可 均可
聲明位置 只能在外部類的成員方法中,和外部類之外的其餘類中聲明(如果在外部類之外,要看類訪問控制是否允許) 只能在作用域中聲明 無需聲明 任意位置均可 (如果在外部類之外,要看類訪問控制是否允許)
創建位置 只能在外部類的成員方法中創建 只能在作用域中創建 立即創建 任意位置均可 (如果在外部類之外,要看類訪問控制是否允許)
是否允許有內部類的內部類 可以,但是不允許有靜態內部類 與成員內部類一致 與成員內部類一致 可以
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章