class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
這些類別稱為嵌套類別。它們分為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("Go!");
}
public class HandleBar {
public void right() {
System.out.println("Steering wheel to the right!");
}
public void left() {
System.out.println("Steering wheel to the left!");
}
}
public class Seat {
public void up() {
System.out.println("The seat is up!");
}
public void down() {
System.out.println("The seat is down!");
}
}
}
這裡我們有一門課Bicycle
──自行車。它有 2 個字段和 1 個方法 - start()
。 它與普通類的區別在於它有兩個類,其代碼寫在裡面——它們是(方向盤)Bicycle
類和(座椅)類。這些是成熟的類別:如您所見,每個類別都有自己的方法。此時,您可能會有一個問題:為什麼我們要把一個類別放在另一個類別中?為什麼要讓它們成為內在的?好吧,假設我們需要在程式中為方向盤和座椅單獨提供類別。但你不必嵌套它們!你可以定期上課。例如,像這樣: HandleBar
Seat
public class HandleBar {
public void right() {
System.out.println("Steering wheel to the right!");
}
public void left() {
System.out.println("Steering wheel left");
}
}
public class Seat {
public void up() {
System.out.println("The seat is up!");
}
public void down() {
System.out.println("The seat is 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
內部HandleBar
類,這樣無主的方向盤和座椅就不會出現在我們的程序中。此程式碼將無法編譯:
public static void main(String[] args) { HandleBar handleBar = new HandleBar(); }
由此得出以下重要特徵:
-
內部類別的物件可以存取「外部」類別的變數。
例如,讓我們在類別中新增
Bicycle
一個變數int seatPostDiameter
- 座桿的直徑。然後在內部類別中
Seat
我們可以建立一個方法getSeatParam()
來告訴我們座位參數: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("Go!"); } public class Seat { public void up() { System.out.println("The seat is up!"); } public void down() { System.out.println("The seat is down!"); } public void getSeatParam() { System.out.println("Seat parameter: 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.getSeatParam(); } }
控制台輸出:
Параметр сиденья: диаметр подседельного штыря = 40
注意:新變數是用最嚴格的修飾符 - 宣告的
private
。並且內部類別仍然可以存取! -
不能在「外部」類別的靜態方法中建立內部類別物件。
這是透過內部類別的設計特徵來解釋的。內部類別可以有參數的建構函數,也可以只有預設建構子。但不管怎樣,當我們建立內部類別的物件時,對「外部」類別的物件的引用會悄悄地傳遞給它。畢竟,這樣物體的存在才是前提。否則我們將無法建立內部類別的物件。
但如果外部類別方法是靜態的,那麼外部類別物件可能根本不存在!這意味著內部類別的邏輯將被打破。在這種情況下,編譯器會拋出錯誤:
public static Seat createSeat() { //Bicycle.this cannot be referenced from a static context return new Seat(); }
-
內部類別不能包含靜態變數和方法。
這裡的邏輯是一樣的:靜態方法和變數即使沒有物件也可以存在並被呼叫。
但是如果沒有「外部」類別的對象,我們將無法存取內部類別。
明顯的矛盾啊!因此,內部類別中禁止存在靜態變數和方法。
編譯器在嘗試建立它們時會拋出錯誤:
public class Bicycle { private int weight; public class Seat { //inner class cannot have static declarations public static void getSeatParam() { System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
在建立內部類別的物件時,其存取修飾符起著重要作用。
內部類別可以用標準存取修飾符 -
public
、private
和protected
來表示package private
。為什麼它如此重要?
這會影響我們程式中可以實例化內部類別的位置。
如果我們的類別
Seat
被聲明為public
,我們可以在任何其他類別中創建它的物件。唯一的要求是「外部」類別的物件也必須存在。順便說一下,我們已經在這裡完成了這個:
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(); } }
我們可以輕鬆地
HandleBar
從Main
.如果我們將內部類別聲明為
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 a private access in 'Bicycle' Bicycle.Seat seat = bicycle.new Seat(); } }
你可能已經明白其中的邏輯了:)
-
內部類別的存取修飾符的工作方式與常規變數相同。
修飾符
protected
提供對其後代類別和同一包中的類別中的類別變數的存取。protected
對於內部類別也是如此。protected
可以建立內部類別物件:- 在「外部」類別內部;
- 在其後代階級中;
- 在同一包中的那些類別中。
如果內部類別沒有存取修飾符(
package private
),則可以建立內部類別的對象- 在「外部」類別內部;
- 在同一包中的類別中。
你已經熟悉修飾符很久了,所以這裡不會有任何問題。
GO TO FULL VERSION