Java 內部類詳解(成員內部類、靜態(tài)內部類攒至、局部內部類厚者、匿名內部類)

內部類是一種嵌套在另一個類(稱為外部類)內部的類定義。

  • 內部類可以訪問外部類的所有成員(包括私有成員)迫吐,同時也可以擁有自己的成員變量库菲、方法、構造器以及嵌套類志膀。
  • 內部類的使用增強了封裝性熙宇,簡化了代碼組織,尤其在處理事件監(jiān)聽溉浙、回調函數烫止、模塊化設計等方面非常有用。

內部類的種類

  • 成員內部類(Member Inner Class):
    • 定義在外部類的成員位置戳稽,可以具有任意訪問修飾符(public馆蠕、protectedprivate 或默認訪問權限)惊奇。
    • 可以直接訪問外部類的所有成員(包括私有成員)荆几。
    • 外部類要訪問內部類的成員,需要通過內部類的實例來訪問赊时。
  • 靜態(tài)內部類(Static Nested Class):
    • 使用 static 關鍵字修飾,不依賴于外部類的實例行拢,可以直接使用外部類的靜態(tài)成員祖秒,無需創(chuàng)建外部類對象。
    • 不能直接訪問外部類的非靜態(tài)成員舟奠,但可以通過外部類實例來訪問竭缝。
  • 局部內部類(Local Inner Class):
    • 定義在外部類的方法或代碼塊內部。
    • 只能在定義它的方法或代碼塊中被訪問沼瘫。
    • 可以訪問外部類的所有成員抬纸,以及定義它的方法或代碼塊內的局部變量(前提是這些局部變量必須被聲明為 final)。
  • 匿名內部類(Anonymous Inner Class):
    • 將內部類的概念進一步簡化耿戚,沒有類名湿故,直接在創(chuàng)建對象時定義并實例化阿趁。
    • 由于沒有類名,它不能獨立存在坛猪,只能與創(chuàng)建它的實例關聯(lián)脖阵。

內部類的特點

  • 封裝性:內部類增強了面向對象的封裝性,可以把一些實現細節(jié)封裝在內部類中墅茉,隱藏在外部類之內命黔,限制了其他對象的直接訪問。
  • 代碼組織:內部類使得相關的類可以緊密地封裝在一起就斤,提高了代碼的可讀性和可維護性悍募。
  • 訪問特權:內部類可以直接訪問外部類的所有成員,包括私有成員洋机,這在設計回調機制坠宴、事件監(jiān)聽器等場景下非常有用。
  • 生命周期相關性:某些內部類(非靜態(tài)內部類)的實例與外部類的實例之間存在緊密的生命周期聯(lián)系槐秧,它們共享同一個外部類實例啄踊。

成員內部類

成員內部類(Member Inner Class)是Java中內部類的一種,它定義在另一個類(外部類)的成員位置刁标,可以具有任意訪問修飾符(public颠通、protectedprivate 或默認訪問權限)膀懈。

特點

  • 訪問外部類成員
    • 成員內部類可以直接訪問外部類的所有成員顿锰,包括私有成員(字段、方法和嵌套類)启搂。這意味著內部類可以訪問外部類的私有數據和受保護的功能硼控,這有助于實現更緊密的封裝和更復雜的邏輯。
    • 外部類訪問內部類的成員則需要通過內部類的實例來訪問胳赌,如同訪問其他類的對象一樣牢撼。
  • 生命周期依賴
    • 成員內部類的實例與外部類的實例之間存在依賴關系。創(chuàng)建一個成員內部類的實例時疑苫,必須先有一個外部類的實例存在熏版。也就是說,不能獨立創(chuàng)建一個成員內部類的對象捍掺,除非它被嵌套在外部類的一個方法或代碼塊中撼短,且該方法或代碼塊正在被外部類的一個實例調用。
  • 訪問修飾符
    • 成員內部類可以有任意訪問修飾符挺勿,如 public曲横、protectedprivate 或默認(包訪問權限)不瓶。訪問修飾符決定了其他類能否訪問這個內部類禾嫉。
  • 不能有靜態(tài)成員
    • 成員內部類不能有靜態(tài)字段灾杰、靜態(tài)方法或靜態(tài)初始化塊,因為它本身依賴于外部類的實例夭织。然而吭露,它仍然可以使用外部類的靜態(tài)成員。
  • 編譯后的類文件
    • 編譯成員內部類時尊惰,編譯器會生成兩個獨立的類文件:一個是外部類的 .class 文件讲竿,另一個是內部類的 .class 文件,其名稱格式通常是 外部類名$內部類名.class弄屡。

使用方式

  1. 定義

    • 在外部類的成員位置(不在任何方法或代碼塊內部)使用 class 關鍵字定義內部類题禀,可以加上訪問修飾符。
    public class OuterClass {
        private String outerData;
     // 定義一個成員內部類
        class InnerClass {
            // ...
        }
    }
    
  2. 創(chuàng)建實例

    • 在外部類的方法或代碼塊中膀捷,通過 new 關鍵字創(chuàng)建內部類的實例迈嘹,需要先有一個外部類的實例。
    OuterClass outer = new OuterClass();
    OuterClass.InnerClass inner = outer.new InnerClass();
    
  3. 訪問內部類成員

    • 外部類通過內部類實例訪問其成員全庸,如同訪問其他類的對象一樣秀仲。
    inner.someMethod();
    
  4. 內部類訪問外部類成員

    • 內部類可以直接訪問外部類的所有成員,包括私有成員壶笼。
    class InnerClass {
        void accessOuter() {
            System.out.println("外部類的成員變量outerData: " + outerData);
        }
    }
    

應用場景

  • 封裝復雜邏輯:當某個類的實現細節(jié)過于復雜神僵,或者需要對外部類進行深度定制時,可以使用成員內部類來封裝這部分邏輯覆劈,保持外部類的簡潔性保礼。
  • 事件監(jiān)聽器:在處理GUI事件、網絡事件等場景中责语,經常需要創(chuàng)建事件監(jiān)聽器類炮障。將監(jiān)聽器類定義為成員內部類,可以方便地訪問外部類的狀態(tài)和方法坤候。
  • 回調函數:在多線程胁赢、異步處理等需要回調的場景中,成員內部類可以作為回調接口的實現類白筹,直接訪問外部類的狀態(tài)徘键,簡化代碼。
  • 模塊化設計:當某個類的功能可以自然地劃分為幾個邏輯相關的部分時遍蟋,可以使用成員內部類來組織這些部分,增強代碼的模塊化和可讀性螟凭。

代碼示例

public class MemberInternalClass {
    private String outerData;
    
    public MemberInternalClass(String outerData) {
        this.outerData = outerData;
    }
    
    // 創(chuàng)建一個成員內部類
    class InnerClass {
        void accessOuter() {
            System.out.println("外部成員變量outerData: " + outerData);
        }
    }

    public void demonstrateInnerClass() {
        // 調用成員內部類
        InnerClass inner = new InnerClass();
        inner.accessOuter();
    }
}

public static void main(String[] args) {
    MemberInternalClass memberInternalClass = new MemberInternalClass("Hello Java!");
    // 通過成員方法調用成員內部類
    memberInternalClass.demonstrateInnerClass();// 輸出: 外部成員變量outerData: Hello Java!
    // 直接訪問成員內部類
    memberInternalClass.new InnerClass().accessOuter();// 輸出: 外部成員變量outerData: Hello Java!
}

靜態(tài)內部類

靜態(tài)內部類(Static Nested Class)虚青,也被稱為靜態(tài)嵌套類,是一種特殊的內部類螺男,其聲明時使用了 static 關鍵字棒厘。

特點

  • 獨立于外部類實例
    • 靜態(tài)內部類不依賴于外部類的實例纵穿,可以獨立創(chuàng)建其對象。這意味著無需先創(chuàng)建外部類實例就能創(chuàng)建靜態(tài)內部類的實例奢人。
  • 訪問外部類成員
    • 靜態(tài)內部類只能訪問外部類的靜態(tài)成員(包括靜態(tài)字段谓媒、靜態(tài)方法和嵌套靜態(tài)類),不能直接訪問外部類的實例成員(非靜態(tài)字段和非靜態(tài)方法)何乎。若需要訪問實例成員句惯,通常需要通過傳遞外部類實例作為參數或在內部類中持有外部類實例引用。
  • 靜態(tài)成員
    • 靜態(tài)內部類可以擁有自己的靜態(tài)成員(靜態(tài)字段支救、靜態(tài)方法和靜態(tài)初始化塊)抢野,這些靜態(tài)成員遵循普通類中靜態(tài)成員的規(guī)則。
  • 編譯后的類文件
    • 同成員內部類一樣各墨,靜態(tài)內部類也會生成單獨的 .class 文件指孤,文件名通常為 外部類名$靜態(tài)內部類名.class
  • 訪問修飾符
    • 靜態(tài)內部類可以使用任意訪問修飾符(public贬堵、protected恃轩、private 或默認包訪問權限),控制其他類對其的訪問黎做。

使用方式

  1. 定義

    • 在外部類的成員位置叉跛,使用 static class 關鍵字定義靜態(tài)內部類峭弟。
    public class OuterClass {
        // 創(chuàng)建靜態(tài)內部類
        static class StaticInnerClass {
            // ...
        }
    }
    
  2. 創(chuàng)建實例

    • 與創(chuàng)建普通類對象類似媚创,直接使用 new 關鍵字創(chuàng)建靜態(tài)內部類的實例,無需外部類實例薪介。
    OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
    
  3. 訪問內部類成員

    • 通過靜態(tài)內部類的實例訪問其成員伟桅。
    inner.someMethod();
    
  4. 內部類訪問外部類成員

    • 靜態(tài)內部類通過外部類的類名訪問其靜態(tài)成員敞掘。
    class StaticInnerClass {
        void accessOuter() {
            System.out.println("外部靜態(tài)常量: " + OuterClass.outerStaticData);
        }
    }
    

應用場景

  • 工具類或輔助類:當需要定義一組僅與某個外部類相關的工具方法或常量時,可以使用靜態(tài)內部類來封裝這些功能楣铁,避免污染外部類或創(chuàng)建不必要的頂級類玖雁。
  • 單例模式:靜態(tài)內部類可以用于實現線程安全的懶漢式單例模式,利用類加載機制確保單例的唯一性盖腕。
  • 封閉數據結構:靜態(tài)內部類可以用來封裝外部類的數據結構赫冬,提供安全的訪問接口,防止外部直接修改數據溃列。
  • 枚舉類的替代:在不支持枚舉的早期Java版本中劲厌,靜態(tài)內部類常被用來模擬枚舉的行為,每個內部類實例代表一個枚舉值听隐。

代碼示例

public class OuterClass {
    private static String staticData = "Static Data";

    // 定義靜態(tài)內部類
    static class StaticNestedClass {
        void accessStatic() {
            System.out.println("靜態(tài)成員變量: " + staticData);
        }
    }
}

public static void main(String[] args) {
    // 調用靜態(tài)內部類
    OuterClass.StaticNestedClass inner = new OuterClass.StaticNestedClass();
    inner.accessStatic();
}

局部內部類

局部內部類是一種特殊的內部類补鼻,它被定義在一個方法、代碼塊或其他局部作用域內。這種設計允許將類的定義限制在非常特定的范圍中风范,只在需要的地方創(chuàng)建和使用咨跌。

特點

  • 訪問權限與可見性: 局部內部類對外部不可見,即不能從外部類的其他方法或外部其他類中直接訪問硼婿。它只能在其定義的局部作用域內被創(chuàng)建和使用锌半。由于其局限性,局部內部類沒有訪問修飾符(如 public寇漫、private 等)刊殉。
  • 訪問外部資源: 盡管作用域有限,局部內部類卻可以訪問其所在作用域內的所有局部變量猪腕,前提是這些變量必須是 final 或實際上 final(即在局部內部類的生命周期內其值不會改變)冗澈。此外,局部內部類還可以訪問外部類的所有成員(包括私有成員)陋葡,這一點與非局部內部類一致亚亲。
  • 生命周期與垃圾回收: 局部內部類對象的生命周期與其依賴的局部變量密切相關。如果一個局部內部類對象引用了其外部方法的局部變量腐缤,那么只要這個局部內部類對象是可達的捌归,所引用的局部變量就不會被垃圾回收,即使該方法已經執(zhí)行完畢岭粤。這是為了確保內部類對象能夠正確訪問到這些變量的值惜索。

使用方式

局部內部類定義在外部類的一個方法、循環(huán)剃浇、條件語句等局部作用域內巾兆。它的聲明和實現與其他內部類一樣,包含類的屬性虎囚、方法角塑、構造器等組件,但其作用域僅限于定義它的局部區(qū)域淘讥。

public class OuterClass {
    public void someMethod() {
        // 局部內部類定義在此處
        class LocalInnerClass {
            // 屬性圃伶、方法、構造器等...
        }
        
        // 在此作用域內可以創(chuàng)建和使用 LocalInnerClass 的實例
        LocalInnerClass localInstance = new LocalInnerClass();
    }
}

應用場景

局部內部類通常用于解決一些特定的編程問題蒲列,如:

  • 臨時性的類需求:當某個功能邏輯僅在特定方法中需要窒朋,并且該功能需要封裝成類,但又無需在整個類層次結構中公開時蝗岖,可以使用局部內部類侥猩。
  • 事件處理回調:在實現事件驅動模型時,有時需要為特定事件創(chuàng)建一個匿名或局部內部類的監(jiān)聽器抵赢,這些監(jiān)聽器只在注冊事件時創(chuàng)建欺劳,并在事件觸發(fā)時調用其方法洛退。
  • 異常處理:有時為了簡化異常處理邏輯,可以定義一個局部內部類來封裝特定類型的異常及其處理邏輯杰标。
  • 適配器模式:在需要快速創(chuàng)建一個滿足特定接口的適配器對象時,局部內部類可以提供簡潔的實現方式彩匕。

代碼示例

public class Demo03_LocalInnerClass {

    private String sharedField = "外部類定義";
    public void processData() {
        final String importantValue = "外部類方法的變量"; // 必須為 final 或 effectively final

        // 定義局部內部類
        class DataProcessor {
            void doProcessing() {
                System.out.println("外部類方法中的局部變量: " + importantValue);
                // 可以訪問外部類成員
                System.out.println("外部類成員變量: " + Demo03_LocalInnerClass.this.sharedField);
            }
        }

        // 創(chuàng)建并使用局部內部類實例
        DataProcessor processor = new DataProcessor();
        processor.doProcessing();
    }
    public static void main(String[] args) {
        // 調用非靜態(tài)方法腔剂,間接調用局部內部類
        Demo03_LocalInnerClass method = new Demo03_LocalInnerClass();
        method.processData();
    }
}

匿名內部類

匿名內部類(Anonymous Inner Class)是Java中一種特殊的內部類,它沒有名字驼仪,即在定義時不需要使用 class 關鍵字為其命名掸犬。

  • 匿名內部類主要用于簡化代碼,特別是在只需要使用一次某個類的實例绪爸,且該類的實現相對簡單的情況下湾碎。
  • 匿名內部類通常用于實現接口或繼承抽象類,并在創(chuàng)建該類實例的同時定義其實現奠货。

特點

  • 無名稱
    • 匿名內部類沒有名稱介褥,定義時直接省略了類名。因此递惋,無法在其他地方再次引用或創(chuàng)建該類的實例柔滔。
  • 繼承或實現
    • 匿名內部類必須繼承一個父類(通常為抽象類)或實現一個接口。這是匿名內部類存在的主要目的萍虽,即在創(chuàng)建對象時同時提供具體的實現睛廊。
  • 一次性使用
    • 由于匿名內部類沒有名稱,所以它通常伴隨著 new 關鍵字一起使用杉编,創(chuàng)建并初始化一個該類的實例超全。這種“創(chuàng)建即使用”的特性使得匿名內部類適用于那些只需一次性使用的場景。
  • 訪問外部資源
    • 匿名內部類可以訪問其外部類的所有成員(包括私有成員)邓馒,以及其所在方法的 final 或 effectively final 局部變量嘶朱。

使用方式

  1. 定義

    • 使用 new 關鍵字后跟父類或接口的名稱,然后直接定義類體(包含方法绒净、屬性等)见咒,不需要 class 關鍵字和類名。
    new SomeInterfaceOrAbstractClass() {
        // 類體挂疆,包含方法改览、屬性等
    };
    
  2. 實現方法

    • 如果繼承抽象類或實現接口,需要在類體中提供所繼承或實現方法的具體實現缤言。
    new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Button clicked!");
        }
    };
    
  3. 創(chuàng)建實例

    • 匿名內部類的實例是在定義時直接創(chuàng)建的宝当,無需額外的 new 操作。創(chuàng)建的實例可以直接賦值給相應類型的變量或作為參數傳遞胆萧。
    ActionListener listener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Button clicked!");
        }
    };
    
    button.addActionListener(listener); // 作為參數傳遞給方法
    

應用場景

  • 事件監(jiān)聽器:在處理GUI事件庆揩、網絡事件等場景中俐东,匿名內部類常用于快速創(chuàng)建事件監(jiān)聽器的實例,提供事件觸發(fā)時的處理邏輯订晌。
  • Lambda表達式的前身:在Java 8之前虏辫,匿名內部類是實現函數式編程風格的主要手段,如集合排序锈拨、流操作等∑鲎現在許多這樣的場景已被Lambda表達式取代。
  • 一次性使用的策略或算法:當某個策略或算法只需使用一次奕枢,且實現較為簡單時娄昆,可以用匿名內部類實現,避免創(chuàng)建多余的命名類缝彬。

代碼示例

創(chuàng)建了一個簡單的Java Swing GUI應用程序萌焰,包含一個按鈕。

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Demo04_AnonymousInnerClass {
    public static void main(String[] args) {
        // 創(chuàng)建一個JFrame實例
        JFrame frame = new JFrame("匿名內部類 - 示例");

        // 創(chuàng)建一個JButton實例
        JButton button = new JButton("快點我 !");

        // 使用匿名內部類創(chuàng)建并初始化ActionListener實例
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frame, "你點到我了!", "Message", JOptionPane.INFORMATION_MESSAGE);
            }
        });

        // 添加按鈕到frame并設置關閉操作
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(button);
        frame.pack();
        frame.setVisible(true);
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末谷浅,一起剝皮案震驚了整個濱河市扒俯,隨后出現的幾起案子,更是在濱河造成了極大的恐慌壳贪,老刑警劉巖陵珍,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異违施,居然都是意外死亡互纯,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門磕蒲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來留潦,“玉大人,你說我怎么就攤上這事辣往⊥迷海” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵站削,是天一觀的道長坊萝。 經常有香客問我,道長许起,這世上最難降的妖魔是什么十偶? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮园细,結果婚禮上惦积,老公的妹妹穿的比我還像新娘。我一直安慰自己猛频,他們只是感情好狮崩,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布蛛勉。 她就那樣靜靜地躺著,像睡著了一般睦柴。 火紅的嫁衣襯著肌膚如雪诽凌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天坦敌,我揣著相機與錄音皿淋,去河邊找鬼。 笑死恬试,一個胖子當著我的面吹牛,可吹牛的內容都是我干的疯暑。 我是一名探鬼主播训柴,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妇拯!你這毒婦竟也來了幻馁?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤越锈,失蹤者是張志新(化名)和其女友劉穎仗嗦,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體甘凭,經...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡稀拐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了丹弱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片德撬。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖躲胳,靈堂內的尸體忽然破棺而出蜓洪,到底是詐尸還是另有隱情,我是刑警寧澤坯苹,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布隆檀,位于F島的核電站,受9級特大地震影響粹湃,放射性物質發(fā)生泄漏恐仑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一再芋、第九天 我趴在偏房一處隱蔽的房頂上張望菊霜。 院中可真熱鬧,春花似錦济赎、人聲如沸鉴逞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽构捡。三九已至液南,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勾徽,已是汗流浹背滑凉。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喘帚,地道東北人畅姊。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像吹由,于是被迫代替她去往敵國和親若未。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容