第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

Java 內(nèi)部類

本節(jié)我們將介紹 Java 中的內(nèi)部類。通過本節(jié)的學習,我們將了解到什么是內(nèi)部類,內(nèi)部類的分類作用。在內(nèi)部類的分類部分,我們將逐一學習各個類型的內(nèi)部類如何定義,如何實例化以及各自的特點,要注意區(qū)分不同類型內(nèi)部類的異同。有了這些基礎知識之后,我們也會結合示例介紹為什么需要內(nèi)部類

1. 概念

在 Java 語言中,可以將一個類定義在另一個類里面或者一個方法里面,我們把這樣的類稱為內(nèi)部類。

與之對應的,包含內(nèi)部類的類被稱為外部類。請閱讀下面的代碼:

// 外部類 Car
public class Car {
    // 內(nèi)部類 Engine
    class Engine {
        private String innerName = "發(fā)動機內(nèi)部類";
    }
}

代碼中,Engine 就是內(nèi)部類,而 Car 就是外部類。

2. 分類

Java 中的內(nèi)部類可以分為 4 種:成員內(nèi)部類、靜態(tài)內(nèi)部類、方法內(nèi)部類和匿名內(nèi)部類。接下來我們按照分類一一介紹。

2.1 成員內(nèi)部類

2.1.1 定義

成員內(nèi)部類也稱為普通內(nèi)部類,它是最常見的內(nèi)部類??梢詫⑵淇醋魍獠款惖囊粋€成員。在成員內(nèi)部類中無法聲明靜態(tài)成員。

如下代碼中聲明了一個成員內(nèi)部類:

// 外部類 Car
public class Car {
    // 內(nèi)部類 Engine
    private class Engine {
        private void run() {
            System.out.println("發(fā)動機啟動了!");
        }
    }
}

我們在外部類 Car 的內(nèi)部定義了一個成員內(nèi)部類 Engine,在 Engine 下面有一個 run() 方法,其功能是打印輸出一行字符串:“發(fā)動機啟動了!”。

另外,需要注意的是,與普通的 Java 類不同,含有內(nèi)部類的類被編譯器編譯后,會生成兩個獨立的字節(jié)碼文件:

Car$Engine.class
Car.class

內(nèi)部類 Engine 會另外生成一個字節(jié)碼文件,其文件名為:外部類類名 $ 內(nèi)部類類名.class

2.1.2 實例化

內(nèi)部類在外部使用時,無法直接實例化,需要借助外部類才能完成實例化操作。關于成員內(nèi)部類的實例化,有 3 種方法:

  1. 我們可以通過 new 外部類().new 內(nèi)部類() 的方式獲取內(nèi)部類的實例對象:
實例演示
預覽 復制
復制成功!
// 外部類 Car
public class Car {

    // 內(nèi)部類 Engine
    private class Engine {
        private void run() {
            System.out.println("發(fā)動機啟動了!");
        }
    }

    public static void main(String[] args) {
        // 1.實例化外部類后緊接著實例化內(nèi)部類
        Engine engine = new Car().new Engine();
        // 2.調(diào)用內(nèi)部類的方法
        engine.run();
    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

發(fā)動機啟動了!
  1. 我們可通過先實例化外部類、再實例化內(nèi)部類的方法獲取內(nèi)部類的對象實例:
public static void main(String[] args) {
    // 1.實例化外部類
    Car car = new Car();
    // 2.通過外部類實例對象再實例化內(nèi)部類
    Engine engine = car.new Engine();
    // 3.調(diào)用內(nèi)部類的方法
    engine.run();
}

編譯執(zhí)行,成功調(diào)用了內(nèi)部類的 run () 方法:

$javac Car.java
java Car
發(fā)動機啟動了!
  1. 我們也可以在外部類中定義一個獲取內(nèi)部類的方法 getEngine(),然后通過外部類的實例對象調(diào)用這個方法來獲取內(nèi)部類的實例:
實例演示
預覽 復制
復制成功!
// 外部類 Car
public class Car {

    // 獲取內(nèi)部類實例的方法
    public Engine getEngine() {
        return new Engine();
    }

    // 內(nèi)部類 Engine
    private class Engine {
        private void run() {
            System.out.println("發(fā)動機啟動了!");
        }
    }

    public static void main(String[] args) {
		// 1.實例化外部類
    	Car car = new Car();
    	// 2.調(diào)用實例方法getEngine(),獲取內(nèi)部類實例
    	Engine engine = car.getEngine();
    	// 3.調(diào)用內(nèi)部類的方法
    	engine.run();
    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

發(fā)動機啟動了!

這種設計在是非常常見的,同樣可以成功調(diào)用執(zhí)行 run() 方法。

2.1.2 成員的訪問

成員內(nèi)部類可以直接訪問外部類的成員,例如,可以在內(nèi)部類的中訪問外部類的成員屬性:

實例演示
預覽 復制
復制成功!
// 外部類 Car
public class Car {

    String name;

    public Engine getEngine() {
        return new Engine();
    }

    // 內(nèi)部類 Engine
    private class Engine {
        // 發(fā)動機的起動方法
        private void run() {
            System.out.println(name + "的發(fā)動機啟動了!");
        }
    }

    public static void main(String[] args) {
        // 實例化外部類
        Car car = new Car();
        // 為實例屬性賦值
        car.name = "大奔奔";
        // 獲取內(nèi)部類實例
        Engine engine = car.getEngine();
        // 調(diào)用內(nèi)部類的方法
        engine.run();
    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

觀察 Enginerun() 方法,調(diào)用了外部類的成員屬性 name,我們在主方法實例化 Car 后,已經(jīng)為屬性 name 賦值。

運行結果:

大奔奔的發(fā)動機啟動了!

相同的,除了成員屬性,成員方法也可以自由訪問。這里不再贅述。

還存在一個同名成員的問題:如果內(nèi)部類中也存在一個同名成員,那么優(yōu)先訪問內(nèi)部類的成員。可理解為就近原則。

這種情況下如果依然希望訪問外部類的屬性,可以使用外部類名.this.成員的方式,例如:

實例演示
預覽 復制
復制成功!
// 外部類 Car
public class Car {

    String name;

    public Engine getEngine() {
        return new Engine();
    }
    // 汽車的跑動方法
    public void run(String name) {
        System.out.println(name + "跑起來了!");
    }

    // 內(nèi)部類 Engine
    private class Engine {
        private String name = "引擎";
        // 發(fā)動機的起動方法
        private void run() {
            System.out.println("Engine中的成員屬性name=" + name);
            System.out.println(Car.this.name + "的發(fā)動機啟動了!");
            Car.this.run(Car.this.name);
        }
    }

    public static void main(String[] args) {
        // 實例化外部類
        Car car = new Car();
        // 為實例屬性賦值
        car.name = "大奔奔";
        // 獲取內(nèi)部類實例
        Engine engine = car.getEngine();
        // 調(diào)用內(nèi)部類的方法
        engine.run();
    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

Engine中的成員屬性name=引擎
大奔奔的發(fā)動機啟動了!
大奔奔跑起來了!

請觀察內(nèi)部類 run() 方法中的語句:第一行語句調(diào)用了內(nèi)部類自己的屬性 name,而第二行調(diào)用了外部類 Car 的屬性 name,第三行調(diào)用了外部類的方法 run(),并將外部類的屬性 name 作為方法的參數(shù)。

2.2 靜態(tài)內(nèi)部類

2.2.1 定義

靜態(tài)內(nèi)部類也稱為嵌套類,是使用 static 關鍵字修飾的內(nèi)部類。如下代碼中定義了一個靜態(tài)內(nèi)部類:

public class Car1 {
    // 靜態(tài)內(nèi)部類
    static class Engine {
        public void run() {
            System.out.println("我是靜態(tài)內(nèi)部類的run()方法");
            System.out.println("發(fā)動機啟動了");
        }
    }
}

2.2.2 實例化

靜態(tài)內(nèi)部類的實例化,可以不依賴外部類的對象直接創(chuàng)建。我們在主方法中可以這樣寫:

// 直接創(chuàng)建靜態(tài)內(nèi)部類對象
Engine engine = new Engine();
// 調(diào)用對象下run()方法
engine.run();

運行結果:

我是靜態(tài)內(nèi)部類的run()方法
發(fā)動機啟動

2.2.2 成員的訪問

在靜態(tài)內(nèi)部類中,只能直接訪問外部類的靜態(tài)成員。例如:

實例演示
預覽 復制
復制成功!
public class Car1 {

    String brand = "寶馬";

    static String name = "外部類的靜態(tài)屬性name";

    // 靜態(tài)內(nèi)部類
    static class Engine {
        public void run() {
            System.out.println(name);
        }
    }

    public static void main(String[] args) {
        Engine engine = new Engine();
        engine.run();
    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

run() 方法中,打印的 name 屬性就是外部類中所定義的靜態(tài)屬性 name。編譯執(zhí)行,將會輸出:

外部類的靜態(tài)屬性name

對于內(nèi)外部類存在同名屬性的問題,同樣遵循就近原則。這種情況下依然希望調(diào)用外部類的靜態(tài)成員,可以使用外部類名.靜態(tài)成員的方式來進行調(diào)用。這里不再一一舉例。

如果想要訪問外部類的非靜態(tài)屬性,可以通過對象的方式調(diào)用,例如在 run() 方法中調(diào)用 Car1 的實例屬性 brand

public void run() {
    // 實例化對象
    Car1 car1 = new Car1();
    System.out.println(car1.brand);
}

2.3 方法內(nèi)部類

2.3.1 定義

方法內(nèi)部類,是定義在方法中的內(nèi)部類,也稱局部內(nèi)部類。

如下是方法內(nèi)部類的代碼:

實例演示
預覽 復制
復制成功!
public class Car2 {
	
	// 外部類的run()方法
    public void run() {
        class Engine {
            public void run() {
                System.out.println("方法內(nèi)部類的run()方法");
                System.out.println("發(fā)動機啟動了");
            }
        }
        // 在Car2.run()方法的內(nèi)部實例化其方法內(nèi)部類Engine
        Engine engine = new Engine();
        // 調(diào)用Engine的run()方法
        engine.run();
    }

    public static void main(String[] args) {
        Car2 car2 = new Car2();
        car2.run();
    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

方法內(nèi)部類的run()方法
發(fā)動機啟動了

如果我們想調(diào)用方法內(nèi)部類的 run() 方法,必須在方法內(nèi)對 Engine 類進行實例化,再去調(diào)用其 run() 方法,然后通過外部類調(diào)用自身方法的方式讓內(nèi)部類方法執(zhí)行。

2.3.2 特點

與局部變量相同,局部內(nèi)部類也有以下特點:

  • 方法內(nèi)定義的局部內(nèi)部類只能在方法內(nèi)部使用;
  • 方法內(nèi)不能定義靜態(tài)成員;
  • 不能使用訪問修飾符。

也就是說,Car2.getEngine() 方法中的 Engine 內(nèi)部類只能在其方法內(nèi)部使用;并且不能出現(xiàn) static 關鍵字;也不能出現(xiàn)任何的訪問修飾符,例如把方法內(nèi)部類 Engine 聲明為 public 是不合法的。

2.4 匿名內(nèi)部類

2.4.1 定義

匿名內(nèi)部類就是沒有名字的內(nèi)部類。使用匿名內(nèi)部類,通常令其實現(xiàn)一個抽象類或接口。請閱讀如下代碼:

實例演示
預覽 復制
復制成功!
// 定義一個交通工具抽象父類,里面只有一個run()方法
public abstract class Transport {
    public void run() {
        System.out.println("交通工具run()方法");
    }

    public static void main(String[] args) {
        // 此處為匿名內(nèi)部類,將對象的定義和實例化放到了一起
        Transport car = new Transport() {
            // 實現(xiàn)抽象父類的run()方法
            @Override
            public void run() {
                System.out.println("汽車跑");
            }
        };
        // 調(diào)用其方法
        car.run();

        Transport airPlain = new Transport() {
            // 實現(xiàn)抽象父類的run()方法
            @Override
            public void run() {
                System.out.println("飛機飛");
            }
        };
        airPlain.run();

    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

運行結果:

汽車跑
飛機飛

上述代碼中的抽象父類中有一個方法 run(),其子類必須實現(xiàn),我們使用匿名內(nèi)部類的方式將子類的定義和對象的實例化放到了一起,通過觀察我們可以看出,代碼中定義了兩個匿名內(nèi)部類,并且分別進行了對象的實例化,分別為 carairPlain,然后成功調(diào)用了其實現(xiàn)的成員方法 run()

2.4.2 特點

  • 含有匿名內(nèi)部類的類被編譯之后,匿名內(nèi)部類會單獨生成一個字節(jié)碼文件,文件名的命名方式為:外部類名稱$數(shù)字.class。例如,我們將上面含有兩個匿名內(nèi)部類的 Transport.java 編譯,目錄下將會生成三個字節(jié)碼文件:
Transport$1.class
Transport$2.class
Transport.class
  • 匿名內(nèi)部類沒有類型名稱和實例對象名稱;
  • 匿名內(nèi)部類可以繼承父類也可以實現(xiàn)接口,但二者不可兼得;
  • 匿名內(nèi)部類無法使用訪問修飾符、staticabstract 關鍵字修飾;
  • 匿名內(nèi)部類無法編寫構造方法,因為它沒有類名;
  • 匿名內(nèi)部類中不能出現(xiàn)靜態(tài)成員。

2.4.2 使用場景

由于匿名內(nèi)部類沒有名稱,類的定義可實例化都放到了一起,這樣可以簡化代碼的編寫,但同時也讓代碼變得不易閱讀。當我們在代碼中只用到類的一個實例、方法只調(diào)用一次,可以使用匿名內(nèi)部類。

3. 作用

3.1 封裝性

內(nèi)部類的成員通過外部類才能訪問,對成員信息有更好的隱藏,因此內(nèi)部類實現(xiàn)了更好的封裝。

3.2 實現(xiàn)多繼承

我們知道 Java 不支持多繼承,而接口可以實現(xiàn)多繼承的效果,但實現(xiàn)接口就必須實現(xiàn)里面所有的方法,有時候我們的需求只是實現(xiàn)其中某個方法,內(nèi)部類就可以解決這些問題。

下面示例中的 SubClass,通過兩個成員內(nèi)部類分別繼承 SuperClass1SuperClass2,并重寫了方法,實現(xiàn)了多繼承:

// SuperClass1.java
public class SuperClass1 {
    public void method1() {
        System.out.println("The SuperClass1.method1");
    }
}

// SuperClass2.java
public class SuperClass2 {
    public void method2() {
        System.out.println("The SuperClass2.method2");
    }
}

// SubClass.java
public class SubClass {
	// 定義內(nèi)部類1
    class InnerClass1 extends SuperClass1 {
        // 重寫父類1方法
        @Override
        public void method1() {
            super.method1();
        }
    }
	
    // 定義內(nèi)部類2
    class InnerClass2 extends SuperClass2 {
        // 重寫父類2方法
        @Override
        public void method2() {
            super.method2();
        }
    }

    public static void main(String[] args) {
        // 實例化內(nèi)部類1
        InnerClass1 innerClass1 = new SubClass().new InnerClass1();
        // 實例化內(nèi)部類2
        InnerClass2 innerClass2 = new SubClass().new InnerClass2();
        // 分別調(diào)用內(nèi)部類1、內(nèi)部類2的方法
        innerClass1.method1();
        innerClass2.method2();
    }
}

編譯執(zhí)行 SubClass.java,屏幕將會打印:

$ javac SubClass.java
$ java SubClass
The SuperClass1.method1
The SuperClass1.method2

3.3 解決繼承或?qū)崿F(xiàn)接口時的方法同名問題

請閱讀如下代碼:

// One.java
public class One {
    public void test() {
    }
}
// Two.java
public interface Two {
    void test();
}
// Demo.java
public class Demo1 extends One implements Two {
    public void test() {
        
    }
}

此時,我們無法確定 Demo1 類中的 test() 方法是父類 One 中的 test 還是接口 Two 中的 test。這時我們可以使用內(nèi)部類解決這個問題:

public class Demo2 extends One {
    
    // 重寫父類方法
    @Override
    public void test() {
        System.out.println("在外部類實現(xiàn)了父類的test()方法");
    }
    
    // 定義內(nèi)部類
    class InnerClass implements Two {
        // 重寫接口方法
        @Override
        public void test() {
            System.out.println("在內(nèi)部類實現(xiàn)了接口的test()方法");
        }
    }
    
    public static void main(String[] args) {
        // 實例化子類Demo2
        Demo2 demo2 = new Demo2();
        // 調(diào)用子類方法
        demo2.test();
        // 實例化子類Demo2的內(nèi)部類
        InnerClass innerClass = demo2.new InnerClass();
        // 調(diào)用內(nèi)部類方法
		innerClass.test();
    }
}

運行結果:

在外部類實現(xiàn)了父類的test()方法
在內(nèi)部類實現(xiàn)了接口的test()方法

4. 小結

本小節(jié),我們知道了什么是內(nèi)部類,也知道了在 Java 中有四種內(nèi)部類:成員內(nèi)部類、靜態(tài)內(nèi)部類、方法內(nèi)部類和匿名內(nèi)部類。對于它們的定義和調(diào)用也做了詳細講解,理解內(nèi)部類的作用是使用好內(nèi)部類的關鍵。