CodeGym /Java Blog /Toto sisi /嵌套內部類
John Squirrels
等級 41
San Francisco

嵌套內部類

在 Toto sisi 群組發布
你好!今天我們將討論一個重要的話題——嵌套類在 Java 中是如何工作的。Java 允許您在另一個類中創建類:

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
這些內部類被稱為嵌套。它們分為兩種類型:
  1. 非靜態嵌套類。這些也稱為內部類。
  2. 靜態嵌套類。
反過來,內部類有兩個不同的子類別。內部類除了簡單的內部類之外,還可以是:
  • 本地班級
  • 匿名類
使困惑?:) 沒關係。為了清楚起見,這裡有一個圖表。如果您突然發現自己感到困惑,請在上課時回過頭來看看! 嵌套內部類 - 2在今天的課程中,我們將討論內部類(也稱為非靜態嵌套類)。它們在整體圖中特別突出顯示,因此您不會迷路:) 讓我們從一個明顯的問題開始:為什麼將它們稱為“內部”類?答案很簡單:因為它們是在其他類中創建的。這是一個例子:

public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }
  
   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {
      
       public void up() {

           System.out.println("Seat up!");
       }
      
       public void down() {

           System.out.println("Seat down!");
       }
   }
}
我們在這裡上課Bicycle。它有 2 個字段和 1 個方法:start(). 嵌套內部類 - 3它與普通類的不同之處在於它包含兩個類:HandlebarSeat。他們的代碼寫在Bicycle類裡面。這些是成熟的類:如您所見,每個類都有自己的方法。說到這裡,您可能會有一個疑問:為什麼我們要把一個類放在另一個類中?為什麼要讓它們成為內部類?好吧,假設我們需要單獨的類來處理程序中的把手和座椅的概念。當然,我們沒有必要讓它們嵌套!我們可以做普通班。例如,像這樣:

public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
很好的問題!當然,我們不受技術的限制。這樣做當然是一種選擇。在這裡,更重要的是從特定程序及其目的的角度正確設計類。內部類用於分離出與另一個實體密不可分的實體。車把、車座和踏板是自行車的組成部分。與自行車分開,它們沒有多大意義。如果我們將所有這些概念單獨的公共類,我們的程序中就會有這樣的代碼:

public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
嗯……這段代碼的意思就更難解釋了。我們有一些模糊的車把(為什麼有必要?老實說,不知道)。這個把手向右轉……完全靠自己,沒有自行車……出於某種原因。通過將車把的概念與自行車的概念分開,我們在程序中丟失了一些邏輯。使用內部類,代碼看起來非常不同:

public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
控制台輸出:

Seat up! 
Let's go! 
Steer left! 
Steer right!
現在我們所看到的突然變得有道理了!:) 我們創建了一個自行車對象。我們創建了兩個自行車“子對象”——一個把手和一個座椅。為了舒適起見,我們將座椅抬高,然後出發:根據需要踩踏板和轉向!:) 我們需要的方法在適當的對像上被調用。一切都簡單方便。在此示例中,將車把和座椅分開可增強封裝(我們將有關自行車零件的數據隱藏在相關類中)並讓我們創建更詳細的抽象。現在讓我們看看不同的情況。假設我們要創建一個模擬自行車商店和自行車備件的程序。 嵌套內部類 - 4在這種情況下,我們之前的解決方案將行不通。在自行車商店,每個單獨的自行車零件即使與自行車分開也是有意義的。例如,我們需要“向客戶出售踏板”、“購買新座椅”等方法。在這裡使用內部類是錯誤的——我們新程序中的每個自行車零件都具有代表意義其自身:可以脫離自行車的概念。如果您想知道是應該使用內部類還是將所有實體組織為單獨的類,這正是您需要注意的。面向對象編程的好處在於它可以輕鬆地對真實世界的實體進行建模。在決定是否使用內部類時,這可能是您的指導原則。在實體店裡,備件與自行車分開——這沒關係。也就是說在設計程序的時候也是可以的。好的,我們已經了解了“哲學”:) 現在讓我們熟悉內部類的重要“技術”特性。以下是您絕對需要記住和理解的內容:
  1. 沒有外部類的對象,內部類的對象就不能存在。

    這是有道理的:這就是我們在程序中創建內部類SeatHandlebar內部類的原因——這樣我們就不會得到孤立的車把和座椅。

    此代碼無法編譯:

    
    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }
    

    另一個重要特徵由此而來:

  2. 內部類的對象可以訪問外部類的變量。

    例如,讓我們int seatPostDiameter在我們的類中添加一個變量(代表座桿的直徑)Bicycle

    然後在Seat內部類中,我們可以創建一個displaySeatProperties()顯示座位屬性的方法:

    
    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    

    現在我們可以在我們的程序中顯示這些信息:

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }
    

    控制台輸出:

    
    Seat properties: seatpost diameter = 40
    

    筆記:新變量使用最嚴格的訪問修飾符 ( ) 聲明private。而且內部類仍然可以訪問!

  3. 不能在外部類的靜態方法中創建內部類的對象。

    這可以通過內部類的組織方式的具體特徵來解釋。內部類可以有帶參數的構造函數,或者只有默認構造函數。但無論如何,當我們創建一個內部類的對象時,外部類對象的引用無形中傳遞給了內部類創建的對象。畢竟,存在這樣一個對象引用是絕對必要的。否則,我們將無法創建內部類的對象。

    但是如果外部類的方法是靜態的,那麼我們可能沒有外部類的對象!這將違反內部類如何工作的邏輯。在這種情況下,編譯器會產生一個錯誤:

    
    public static Seat createSeat() {
      
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
    
  4. 內部類不能包含靜態變量和方法。

    邏輯是一樣的:即使沒有對象,靜態方法和變量也可以存在並被調用或引用。

    但是如果沒有外部類的對象,我們將無法訪問內部類。

    明顯的矛盾!這就是內部類中不允許使用靜態變量和方法的原因。

    如果您嘗試創建它們,編譯器將生成錯誤:

    
    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
          
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    
  5. 創建內部類的對象時,其訪問修飾符很重要。

    可以使用標準訪問修飾符標記內部類:publicprivateprotectedpackage private

    為什麼這很重要?

    這會影響我們可以在程序中創建內部類實例的位置。

    如果我們的Seat類聲明為public,我們可以在任何其他類中創建Seat對象。唯一的要求是外部類的對像也必須存在。

    順便說一句,我們已經在這裡做了:

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }
    

    我們很容易HandlebarMain類中獲得了對內部類的訪問權。

    如果我們將內部類聲明為private,我們將只能在外部類中創建對象。

    我們不能再Seat“在外面”創建一個對象:

    
    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }
    

    您可能已經理解其中的邏輯 :)

  6. 內部類的訪問修飾符與普通變量的工作方式相同。

    修飾符protected提供對同一包中的子類和類中的實例變量的訪問。

    protected也適用於內部類。我們可以創建protected內部類的對象:

    • 在外部類;
    • 在其子類中;
    • 在同一個包中的類中。

    如果內部類沒有訪問修飾符(package private),則可以創建內部類的對象:

    • 在外部類;
    • 在同一個包中的類中。

    您已經熟悉修飾符很長時間了,所以這裡沒有問題。

現在就這些了:) 但是不要懈怠!內部類是一個相當廣泛的主題,我們將在下一課中繼續探討。現在你可以重溫一下我們課程關於內部類的課了。下一次,讓我們談談靜態嵌套類。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION