最全面Java內部類總結(面試&查漏補缺必備)

前言

這幾天趁着時間多多,回顧並總結出來超全面的Java內部類知識;Java內部類老實說我們在開發的時候用的不多,然而正是因爲用的不多,久而久之我們就忘了Java內部類,所以纔想寫這一篇博客,相信看了這篇博客之後,你絕對敢說學會了Java內部類,如果遇到面試的時候,吹給面試官聽,很可能面試官就會對你刮目相看(面試官內心獨白:這個小夥子不錯喲,Java內部類用的不多都這麼熟悉,那麼經常使用的技術是不是非常熟練?)。

下面先貼出一張超全的Java內部類知識圖譜
在這裏插入圖片描述
上面思維導圖已經大部分說明了Java內部類,但是還是想把這篇博客的大綱列出來,這樣會更清晰一些。

Java內部類知識大綱

  1. 什麼是內部類
  2. 內部類間接體現Java多繼承?
  3. 四大內部類
  4. 成員內部類
  5. 靜態內部類
  6. 局部(方法)內部類
  7. 匿名內部類

什麼是內部類

將一個類定義在另一個類的內部,這就是內部類。內部類和普通類一樣,都是類,都可以定義屬性、方法 (包括構造方法,靜態方法等等)。通常將內部類分爲4種:成員內部類、靜態內部類、局部(方法)內部類、匿名內部類。在這四種內部類之中,有的內部類可以定義靜態成員,有些內部類就不能定義靜態成員,再下面將會一一說明。

內部類間接體現Java多繼承?

我們都知道在Java中,一個類繼承另一個類,就會繼承那個類(父類、基類)公有成員(屬性、方法),如果外部類繼承一個類,內部類也繼承一個類(內部類也是類,可以繼承類,或者實現接口),又因爲內部類可以直接訪問外部類的成員(屬性、方法),所以內部類也可以訪問外部類繼承父類的成員,所以說內部類的出現間接體現Java多繼承(雖然最多是繼承兩個類,外部類一個,內部類一個)。這個是我個人的見解,如果有不同的想法看法,可以評論交流一下下。
在這裏插入圖片描述

四種內部類

雖然內部類和普通的類一樣,都可以繼承類,實現接口,而且都可以定義成員(屬性,方法),但是它們之間還是有區別的;
比如成員內部類就不能定義靜態成員(靜態變量,靜態方法),而靜態內部類就可以定義靜態成員,下面將會一一介紹這四大內部類,而且都會附帶完整的代碼說明,只講理論不給代碼都是不貼心的。

成員內部類

我們在學面向對象的時候,應該都知道static這個關鍵字,被static修飾(屬性、方法)就是類級別的了,也就是類成員(不依賴於對象,被該類所有對象共享),那麼反過來不被static修飾就是對象成員了,所以成員內部類就是不能被static修飾的,但是可以被四大權限修飾符修飾(public、private、…)。

外部類與成員內部類

下面給出成員內部類代碼,下面還會給出成員內部類總結,而這個總結就是從這個代碼裏濃縮出來的(因爲語法錯誤編譯器會報紅)。
代碼可能看起來有點長,可以複製到IDEA中慢慢細讀,相信會有收穫的。

/**
 *  外部類內部使用成員內部類
 *      1.成員內部類可以繼承類,實現接口
 *      2.成員內部類不能創建靜態成員(靜態變量,靜態方法)
 *      3.不能在外部類靜態方法內部創建成員內部類對象
 *      4.如果外部類屬性與內部類屬性同名時,
 *          直接調用是訪問內部類屬性,通過外部類名.this.屬性名訪問的是外部類屬性
 *  其他類內部使用成員內部類
 *      第一種方式:
 *          //創建外部類對象
 *          MemberInnerClass outer = new MemberInnerClass();
 *          //創建內部類對象
 *          MemberInnerClass.InnerClass inner = outer.new InnerClass();
 *      第二種方式:
 *          //鏈式創建內部類對象
 *          MemberInnerClass.InnerClass innerClass = new MemberInnerClass().new InnerClass();
 */
@Data
public class MemberInnerClass {

    private Integer age = 22;
    private String name = "xq";
    private static String country = "中國";

    public void outerMethod() {
        System.out.println("我是外部類的成員方法!");
    }

    public static void outerStaticMethod() {
        System.out.println("我是外部類的靜態方法!");
    }

    public class InnerClass {

        /**
         * 成員變量
         */
        private Integer age = 18;
        /**
         * 成員內部類不允許定義靜態變量
         */
//        public static String name;    //報錯

        /**
         * 構造器
         */
        public InnerClass() {
        }

        /**
         * 內部類成員方法,訪問外部類信息(屬性、方法)
         */
        public void innerCallOuter() {
            //當內部類屬性和外部類屬性不同名時,直接調用即可
            System.out.println(name);
            //當內部類屬性和外部類屬性同名時,訪問的是內部類屬性
            System.out.println("內部類age屬性:" + age);
            //當內部類屬性和外部類屬性同名時,可通過外部類名.this.屬性名
            System.out.println("外部類age屬性:" + MemberInnerClass.this.age);
            System.out.println("外部類靜態變量:" + country);
            //訪問外部類的方法
            outerMethod();
            outerStaticMethod();
        }

        /**
         * 成員內部類不允許定義靜態方法,報錯
         */
        /*public static void innerStaticMethod(){}*/
    }

    /**
     * 外部類靜態方法不能創建內部類對象,
     * 也就是在外部類靜態方法內訪問不了內部類信息
     * @param args
     */
    /*public static void main(String[] args) {
        InnerClass innerClass = new InnerClass();
    }*/

    /**
     * 外部類非靜態方法創建內部類對象,訪問內部類信息
     */
    public void outerCallInner() {
        InnerClass innerClass = new InnerClass();
        innerClass.innerCallOuter();
    }

    /**
     * 外部類靜態方法,創建外部類對象,調用外部類成員方法(內部訪問內部類信息)
     *
     * @param args
     */
    public static void main(String[] args) {
        MemberInnerClass memberInnerClass = new MemberInnerClass();
        memberInnerClass.outerCallInner();
    }

}

其他類使用成員內部類

public class MemberInnerClassTest {

    public static void main(String[] args) {
        //創建外部類對象
        MemberInnerClass outer = new MemberInnerClass();
        outer.outerCallInner();
        System.out.println("=========================");
        //創建內部類對象
        MemberInnerClass.InnerClass inner = outer.new InnerClass();
        inner.innerCallOuter();

        System.out.println("========================");
        //鏈式創建內部類對象
        MemberInnerClass.InnerClass innerClass = new MemberInnerClass().new InnerClass();
        innerClass.innerCallOuter();

    }
}

成員內部類總結

  1. 成員內部類可以被任何的訪問修飾符修飾。
  2. 成員內部類的內部不能定義靜態成員。
  3. 成員內部類也是類,可以繼承類,可以實現接口,方法也可以重寫,重載,this和super隨便使用。
  4. 成員內部類可以直接使用外部類的任何信息,如果屬性或者方法發生衝突,調用外部類.this.屬性或者方法
  5. 其它類如何訪問成員內部類,被public修飾的成員內部類,可以被不同包的其他類訪問;其他情況和普通類一樣…

靜態內部類

靜態內部類就是static修飾的內部類,也可以被四大權限修飾符修飾。

外部類定義以及使用靜態內部類

下面代碼以及註釋非常清晰說明了靜態內部類的特性。

/**
 * 外部類&靜態內部類
 */
public class StaticInnerClass {

    //和內部類屬性同名
    private int age = 22;

    private String outer = "outerClass";
    private static String country = "china";

    static {
        System.out.println("外部類靜態代碼塊...");
    }

    public void outerMethod() {
        System.out.println("我是外部類的成員方法!");
    }

    public static void outerStaticMethod() {
        System.out.println("我是外部類的靜態方法!");
    }

    /**
     * 靜態內部類,需要使用static修飾
     */
    public static class InnerClass {

        private int age = 18;
        private String inner = "innerClass";

        //靜態內部類可以定義靜態變量
        private static String country = "中國";

        static {
            System.out.println("內部類靜態代碼塊...");
        }

        public void innerMethod() {
            //靜態內部類不能訪問外部類非靜態成員屬性
//            System.out.println("outer:"+outer); 報錯

            System.out.println("inner:" + inner);

            System.out.println("靜態內部類age屬性:" + age);
            //靜態類內部不能通過這種方式訪問外部類的同名屬性
//            System.out.println("外部類age屬性:"+StaticOuterClass.this.age);

            System.out.println("靜態內部類static屬性:" + country);
            System.out.println("外部類static屬性:" + cn.zwq.innerclass.StaticInnerClass.country);

            //靜態內部類不能調用外部類成員方法
//            outerMethod(); 報錯
            //靜態內部類可以調用外部類靜態方法
            outerStaticMethod();
        }

        /**
         * 靜態內部類可以定義靜態方法
         */
        public static void innerStaticMethod() {
//            outerMethod(); 報錯
            outerStaticMethod();
        }

        public static void main(String[] args) {
            //訪問靜態內部類靜態屬性
            System.out.println(cn.zwq.innerclass.StaticInnerClass.InnerClass.country);
            //訪問靜態內部類靜態方法
            cn.zwq.innerclass.StaticInnerClass.InnerClass.innerStaticMethod();
        }

    }
}

其他類使用靜態內部類

創建靜態內部類對象和創建成員內部類對象稍微不同,可以和上面稍微對比一下就清晰了。

public class StaticInnerClassTest {

    public static void main(String[] args) {
        //創建靜態內部類對象,和創建成員內部類稍微不同
        StaticInnerClass.InnerClass innerClass = new StaticInnerClass.InnerClass();
        //訪問靜態內部類方法(靜態、非靜態)
        innerClass.innerMethod();
        innerClass.innerStaticMethod();

        //直接調用靜態內部類靜態屬性:外部類.靜態內部類.靜態屬性(非私有的)
        StaticInnerClass.InnerClass.innerStaticMethod();
    }
}

在這裏插入圖片描述

靜態內部類總結

  1. 靜態內部類使用static修飾,可以定義非靜態成員,也可以定義靜態成員。
  2. 靜態內部類中的方法(成員方法、靜態方法)只能訪問外部類的靜態成員,不能訪問外部類的非靜態成員。
  3. 靜態內部類可以被4大權限修飾符修飾,被public修飾而任意位置的其他類都可以訪問,被private修飾只能被外部類內部訪問。
  4. 靜態內部類內部的靜態成員,可以直接使用外部類.靜態內部類.靜態成員訪問。

局部內部類

局部內部類是定義在方法內部的,我們可以想一下,以前定義方法的時候,有哪些限制?

  1. 首先呢,是變量不能使用權限修飾符修飾,而局部內部類就是方法內部定義的變量,所以局部內部類也不能使用權限修飾符修飾。
  2. 這裏先列舉一條限制,下面還會給出更加詳細的總結。
/**
 * 局部內部類
 */
public class LocalInnerClass {

    //和局部內部類屬性同名
    private int age = 22;

    private String outer = "outerClass";
    private static String country = "china";

    public void outerMethod() {
        System.out.println("我是外部類的成員方法!");
    }

    public static void outerStaticMethod() {
        System.out.println("我是外部類的靜態方法!");
    }

    /**
     * 外部類成員方法,內部定義局部內部類
     */
    public void localInnerClass() {

        String name = "java";
        name = "javaEE";

        //報錯,局部內部類不能被權限修飾符修飾
        /*public class InnerClass{

        }*/

        class InnerClass {

            private String inner = "inner";
            private int age = 18;

            //報錯,局部內部類不能定義靜態成員(屬性、方法)
//            private static String country = "中國";
            /*public static void innerStaticMethod(){

            }*/

            public void innerMethod() {
                //報錯,因爲局部內部類訪問方法定義的變量,該變量必須是final修飾的
//                System.out.println(name);報錯
                System.out.println("局部內部類inner屬性:" + inner);

                //訪問外部類信息
                System.out.println("外部類outer屬性:"+outer);
                System.out.println("局部內部類age屬性:"+age);
                System.out.println("外部類age屬性:"+LocalInnerClass.this.age);
                System.out.println("外部類靜態屬性country:"+country);
                outerMethod();
                outerStaticMethod();
            }
        }

        /*
            局部內部類只能在聲明的方法內部使用
         */
        InnerClass innerClass = new InnerClass();
        innerClass.innerMethod();
        System.out.println(innerClass.age);
        System.out.println(innerClass.inner);
    }

}

局部內部類總結

  1. 局部內部類不能被權限修飾符修飾。
  2. 局部內部類只能在方法內部使用。
  3. 局部內部類不能定義靜態成員。
  4. 局部內部類可以直接訪問方法內部的局部變量和方法參數。
  5. 局部內部類可以訪問外部類的靜態成員、非靜態成員。

局部內部類注意點(重點)

如果局部內部類訪問方法內部的局部變量、方法形參,那麼就要求這些局部變量、方法形參被final修飾,否則會報錯。
下圖很好的說明了這個問題:
在這裏插入圖片描述

匿名內部類

  1. 首先匿名內部類也是內部類的一種,只不過它沒名字。
  2. 匿名內部類最常用的使用場景就是快速創建抽象類或接口的實例。
  3. 如果某個類判定只使用一次,那麼就不要通過new關鍵字創建那個類的對象,而是使用匿名內部類的方式。
  4. 匿名內部類的格式:
    new 實現接口() | 父類構造器(實參列表){
     //匿名內部類類體部分     
    };
    

5.接下來使用匿名內部類創建Runnable接口的實例,創建Thread類的實例。

public static void main(String[] args) {
    //使用匿名內部類創建接口實例
    Runnable runnable = new Runnable() {
        @Override
        public void run() {

        }
    };
    //使用匿名內部類創建Thread類實例
    Thread thread = new Thread(runnable,"小小線程"){
          
    };
}

好了,到這裏Java內部類已經說完了,相信你看了這篇之後,之後面試被問到,或者筆試題考到Java內部類題目,都可以輕鬆解決了。
如果感覺OK的話,可以關注或者點贊博主我一下下,感謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章