搞懂Java內(nèi)部類

Java內(nèi)部類一直是我感覺(jué)很生疏的地方,但是這個(gè)知識(shí)點(diǎn)在面試中被問(wèn)到的幾率還是很高,而且在最近的需求中也用到了靜態(tài)內(nèi)部類,索性就把這一塊又好好學(xué)習(xí)了下,趁熱打鐵,寫(xiě)一篇博客鞏固下

為什么會(huì)有內(nèi)部類

開(kāi)始學(xué)習(xí)內(nèi)部類的時(shí)候,最讓我懵比的就是為啥要設(shè)計(jì)一個(gè)內(nèi)部類,直接就新建一個(gè)普通的類豈不是更簡(jiǎn)單粗暴,但是大神既然設(shè)計(jì),那肯定存在即有理。

在我看來(lái)內(nèi)部類的作用主要包括以下幾點(diǎn):

  1. 更好的封裝性,因?yàn)閮?nèi)部類可以被訪問(wèn)限定符修飾,所以可以完全對(duì)外部其他類隱藏;
  2. 彌補(bǔ)Java單繼承的問(wèn)題,利用內(nèi)部類,可以在類里利用內(nèi)部類再繼承其他的抽象類或?qū)嶓w類,提高代碼的復(fù)用率;
  3. 內(nèi)部類可以直接訪問(wèn)所在作用域中的其他數(shù)據(jù),包括私有數(shù)據(jù),這是直接新建一個(gè)普通類做不到的;
  4. 內(nèi)部類可以簡(jiǎn)化代碼,在Android中最常用的就是匿名內(nèi)部類,例如注冊(cè)Button的Onclick時(shí),使用匿名內(nèi)部類簡(jiǎn)化代碼,不用再單獨(dú)建一個(gè)類來(lái)實(shí)現(xiàn)抽象類或接口

內(nèi)部類的分類

內(nèi)部類主要可以分為成員內(nèi)部類,局部?jī)?nèi)部類以及匿名內(nèi)部類。其中成員內(nèi)部類又包括靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類。

靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類的使用和區(qū)別

  1. 使用非靜態(tài)內(nèi)部類之前必須先實(shí)例化外部類,而靜態(tài)類不需要。這個(gè)很好理解,內(nèi)部類也是外部類的成員,靜態(tài)成員可以直接通過(guò)類來(lái)調(diào)用,而非靜態(tài)的必須通過(guò)實(shí)例來(lái)調(diào)用;
  2. 非靜態(tài)內(nèi)部類不能有靜態(tài)成員,而靜態(tài)內(nèi)部類可以有。因?yàn)槿缟弦稽c(diǎn)所說(shuō)非靜態(tài)類的使用必須依賴于外部類的實(shí)例,而靜態(tài)成員是類成員,不屬于任何實(shí)例,所以如果在非靜態(tài)內(nèi)部類聲明靜態(tài)成員就存在了矛盾,因此在非靜態(tài)內(nèi)部類里不能有靜態(tài)成員;
  3. 靜態(tài)內(nèi)部類可以訪問(wèn)外部類的靜態(tài)成員,但不能訪問(wèn)外部類的非靜態(tài)成員。這個(gè)也很好理解,因?yàn)殪o態(tài)內(nèi)部類不依賴外部類的實(shí)例化,所以不能訪問(wèn)外部類的非靜態(tài)成員;而非靜態(tài)內(nèi)部類可以訪問(wèn)外部類的所有成員;

還是用代碼說(shuō)最直白

public class ClassOuter {

    private int a = 1;
    private static int static_b = 2;
    public int c =3;
    public static int static_d = 4;

    public class ClassInner {

        private int x;

        public void innerFun(){
            System.out.println("內(nèi)部類方法");
            System.out.println(a);
            System.out.println(static_b);
            System.out.println(c);
            System.out.println(static_d);

        }
    }

    public static class ClassStaticInner{

        private int y;
        private static int static_z;

        public void innerFun() {
            System.out.println("靜態(tài)類方法");
            System.out.println(static_b);
            System.out.println(static_d);
        }
    }

}
public class TestInnerClass {

    public static void main(String[] args){

        ClassOuter.ClassInner classInner = new ClassOuter(). new ClassInner();
        ClassOuter.ClassStaticInner classStaticInner = new ClassOuter.ClassStaticInner();
        classInner.innerFun();
        classStaticInner.innerFun();
    }

}

從demo中可以看出

  1. 代碼中非靜態(tài)內(nèi)部類ClassInner內(nèi)部只定義了一個(gè)非靜態(tài)的成員變量,若定義靜態(tài)的成員變量IDE會(huì)報(bào)錯(cuò);而在靜態(tài)內(nèi)部類ClassStaticInner 中既能定義靜態(tài)成員,也能定義非靜態(tài)成員;
  2. 非靜態(tài)內(nèi)部類ClassInner里既能訪問(wèn)外部類的靜態(tài)變量,也能訪問(wèn)非靜態(tài)變量,而靜態(tài)內(nèi)部類只能訪問(wèn)外部類的靜態(tài)變量;
  3. 使用非靜態(tài)內(nèi)部類時(shí)必須先new一個(gè)外部類,再通過(guò)外部類的實(shí)例去new內(nèi)部類,而靜態(tài)內(nèi)部類不需要外部類的實(shí)例;

局部?jī)?nèi)部類

局部?jī)?nèi)部類相對(duì)于上面所說(shuō)的兩種內(nèi)部類的區(qū)別就在于作用域發(fā)生了變化,它定義在方法中,或者某個(gè)作用域中,這個(gè)類的使用僅限于這個(gè)方法內(nèi)或作用域內(nèi)。
使用局部?jī)?nèi)部類的作用在于,當(dāng)我們?cè)谝粋€(gè)方法的內(nèi)部想要實(shí)現(xiàn)一個(gè)較為復(fù)雜的邏輯時(shí),想用一個(gè)類把他封裝起來(lái),但又不想暴露在別的地方,這時(shí)就可以使用局部?jī)?nèi)部類。
使用局部?jī)?nèi)部類時(shí)需要注意的是:

  1. 局部?jī)?nèi)部類不能使用訪問(wèn)修飾符修飾,它對(duì)外是完全隱藏的,只有在定義該局部?jī)?nèi)部類的方法里或作用域內(nèi)才能訪問(wèn);
  2. 局部?jī)?nèi)部類可以使用該類外部方法的局部變量,但是該局部變量必須被聲明為final
public class ClassOuter {

   public void testInnerClass(final int a){
       final int b = 1;
       class LocalInnerClass{
           int c;
           public void test(){
               System.out.println(a);
               System.out.println(b);
               System.out.print(c);
           }
       }
       LocalInnerClass localInnerClass = new LocalInnerClass();
       localInnerClass.test();
   }

}

如demo中代碼所示,局部?jī)?nèi)部類LocalInnerClass聲明在testInnerClass方法內(nèi)部,則只能在該方法內(nèi)部使用,對(duì)方法以外的作用域是隱藏的;

在局部?jī)?nèi)部類中使用了方法中的參數(shù)a以及方法內(nèi)定義的局部變量b,這兩個(gè)變量在該局部?jī)?nèi)部類使用時(shí)都需要被聲明為final,否則IDE會(huì)報(bào)錯(cuò);而局部?jī)?nèi)部類自己的成員變量c的使用和普通類的成員變量使用一致,不需要聲明為final。

匿名內(nèi)部類

匿名內(nèi)部類和局部?jī)?nèi)部類比較類似,最大的區(qū)別就在于他沒(méi)有類名,所以稱為匿名內(nèi)部類。
匿名內(nèi)部類的特點(diǎn):

  1. 沒(méi)有訪問(wèn)修飾符修飾;
  2. 匿名內(nèi)部類需繼承一個(gè)抽象類或者實(shí)現(xiàn)一個(gè)接口;
  3. 匿名內(nèi)部類中不能定義任何靜態(tài)成員;
  4. 匿名內(nèi)部類沒(méi)有構(gòu)造方法,因?yàn)樗麤](méi)有類名;
  5. 和局部?jī)?nèi)部類一樣,當(dāng)他使用該類外部方法的局部變量時(shí),必須被聲明為final;
public class ClassOuter {

   public void testInnerClass(final int a, final int c){
       final int b = 1;
       new testInterface() {
           int d = c;
           @Override
           public void onClick() {
              System.out.println(a);
              System.out.println(b);
           }
       };
   }

   public interface testInterface{
       void onClick();
   }
}

在如上的demo中,通過(guò)testInterface接口實(shí)現(xiàn)了一個(gè)匿名內(nèi)部類,在匿名內(nèi)部類中訪問(wèn)外部的局部變量時(shí),都需要該變量聲明為final,否則IDE會(huì)報(bào)錯(cuò)

為什么局部和匿名內(nèi)部類在使用局部變量時(shí),該局部變量需要被聲明為final

這個(gè)問(wèn)題實(shí)際上是由于各自的生命周期不同所引起的。
因?yàn)槟涿麅?nèi)部類被創(chuàng)建后存在于堆中,而方法的局部變量在棧中,當(dāng)方法里的代碼執(zhí)行完成后,局部變量就會(huì)出棧,而匿名內(nèi)部類存在于堆中,還可能在其他地方被引用,這樣就引起了沖突,方法的局部變量已經(jīng)消失,而內(nèi)部類還需要用到這個(gè)變量;
所以為了解決這個(gè)問(wèn)題,編譯器就幫我們?cè)谀涿麅?nèi)部類中創(chuàng)建了一個(gè)局部變量的備份,內(nèi)部類直接使用的是該備份,備份的生命周期和內(nèi)部類是一樣的。但是如果局部變量發(fā)生變化,備份也要不斷發(fā)生變化,所以索性采取了一個(gè)比較妥協(xié)的辦法,把該變量聲明為final,這樣變量是只可讀的,就保證了內(nèi)部類和外部的該變量的值是一樣。

以上就是我學(xué)習(xí)內(nèi)部類時(shí)的一些理解,能力有限,有理解不對(duì)的地方歡迎指正

2019.3.6

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 搞懂 JAVA 內(nèi)部類 前些天寫(xiě)了一篇關(guān)于 2018 年奮斗計(jì)劃的文章,其實(shí)做 Android 開(kāi)發(fā)也有一段時(shí)間了...
    醒著的碼者閱讀 647評(píng)論 0 0
  • Java 內(nèi)部類 分四種:成員內(nèi)部類、局部?jī)?nèi)部類、靜態(tài)內(nèi)部類和匿名內(nèi)部類。 1、成員內(nèi)部類: 即作為外部類的一個(gè)成...
    ikaroskun閱讀 1,349評(píng)論 0 13
  • 問(wèn):Java 常見(jiàn)的內(nèi)部類有哪幾種,簡(jiǎn)單說(shuō)說(shuō)其特征? 答:靜態(tài)內(nèi)部類、成員內(nèi)部類、方法內(nèi)部類(局部?jī)?nèi)部類)、匿名內(nèi)...
    Little丶Jerry閱讀 2,230評(píng)論 0 1
  • 目錄 第一章 不好看你還天天看我打球? “婉婉,婉婉,你醒醒,睜開(kāi)眼看看我?。 鼻袂溥o拳頭,全身上下的每一個(gè)...
    就是寧姐姐呀閱讀 911評(píng)論 4 11
  • 京東支付是什么?
    蛋黃酥沒(méi)有蛋黃閱讀 99評(píng)論 1 0

友情鏈接更多精彩內(nèi)容