class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
Các lớp nội bộ này được gọi là lồng nhau. Chúng được chia thành 2 loại:
- Các lớp lồng nhau không tĩnh. Chúng còn được gọi là các lớp bên trong.
- Các lớp lồng nhau tĩnh.
- một lớp địa phương
- một lớp ẩn danh

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!");
}
}
}
Ở đây chúng ta có Bicycle
lớp học. Nó có 2 trường và 1 phương thức: start()
. 
Handlebar
và Seat
. Mã của họ được viết bên trong Bicycle
lớp. Đây là các lớp chính thức: như bạn có thể thấy, mỗi lớp có các phương thức riêng. Tại thời điểm này, bạn có thể có một câu hỏi: tại sao trên thế giới chúng ta lại đặt một lớp bên trong một lớp khác? Tại sao làm cho chúng lớp bên trong? Chà, giả sử chúng ta cần các lớp riêng biệt cho các khái niệm về tay lái và chỗ ngồi trong chương trình của mình. Tất nhiên, chúng ta không cần thiết phải lồng chúng vào nhau! Chúng ta có thể làm các lớp bình thường. Ví dụ, như thế này:
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!");
}
}
Câu hỏi rất hay! Tất nhiên, chúng tôi không bị giới hạn bởi công nghệ. Làm điều đó chắc chắn là một lựa chọn. Ở đây, điều quan trọng hơn là thiết kế chính xác của các lớp từ quan điểm của một chương trình cụ thể và mục đích của nó. Các lớp bên trong là để tách ra một thực thể được kết nối chặt chẽ với một thực thể khác. Tay lái, ghế ngồi và bàn đạp là các bộ phận của xe đạp. Tách khỏi xe đạp, chúng không có nhiều ý nghĩa. Nếu chúng ta tách tất cả các khái niệm này thành các lớp công khai, chúng ta sẽ có mã như thế này trong chương trình của mình:
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
Hmm... Ý nghĩa của mã này thậm chí còn khó giải thích. Chúng tôi có một số tay lái mơ hồ (Tại sao nó lại cần thiết? Thành thật mà nói, tôi không biết). Và cái tay cầm này rẽ phải... tự nó, không có xe đạp... vì lý do nào đó. Bằng cách tách biệt khái niệm tay lái khỏi khái niệm xe đạp, chúng ta đã làm mất đi một số logic trong chương trình của mình. Sử dụng một lớp bên trong, mã trông rất khác:
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();
}
}
Đầu ra bảng điều khiển:
Seat up!
Let's go!
Steer left!
Steer right!
Bây giờ những gì chúng ta thấy đột nhiên có ý nghĩa! :) Chúng tôi đã tạo một đối tượng xe đạp. Chúng tôi đã tạo ra hai "đối tượng phụ" của xe đạp — tay lái và yên xe. Chúng tôi nâng ghế lên để tạo sự thoải mái và bắt đầu: đạp và lái khi cần thiết! :) Các phương thức chúng ta cần được gọi trên các đối tượng thích hợp. Tất cả đều đơn giản và thuận tiện. Trong ví dụ này, việc tách riêng tay lái và yên xe giúp tăng cường khả năng đóng gói (chúng tôi ẩn dữ liệu về các bộ phận xe đạp bên trong lớp có liên quan) và cho phép chúng tôi tạo một bản tóm tắt chi tiết hơn. Bây giờ chúng ta hãy xem xét một tình huống khác. Giả sử chúng ta muốn tạo một chương trình mô phỏng một cửa hàng bán xe đạp và phụ tùng cho xe đạp. 
-
Một đối tượng của lớp trong không thể tồn tại nếu không có đối tượng của lớp ngoài.
Điều này có ý nghĩa: đây là lý do tại sao chúng tôi tạo
Seat
vàHandlebar
các lớp bên trong chương trình của mình — để chúng tôi không kết thúc với tay lái và ghế ngồi mồ côi.Mã này không biên dịch:
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
Một tính năng quan trọng khác sau đây:
-
Một đối tượng của lớp bên trong có quyền truy cập vào các biến của lớp bên ngoài.
Ví dụ: hãy thêm một
int seatPostDiameter
biến (đại diện cho đường kính của cột an toàn) vàoBicycle
lớp của chúng ta.Sau đó, trong
Seat
lớp bên trong, chúng ta có thể tạo mộtdisplaySeatProperties()
phương thức hiển thị các thuộc tính chỗ ngồi: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); } } }
Và bây giờ chúng ta có thể hiển thị thông tin này trong chương trình của mình:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.displaySeatProperties(); } }
Đầu ra bảng điều khiển:
Seat properties: seatpost diameter = 40
Ghi chú:biến mới được khai báo với công cụ sửa đổi truy cập nghiêm ngặt nhất (
private
). Và lớp bên trong vẫn có quyền truy cập! -
Một đối tượng của lớp bên trong không thể được tạo trong phương thức tĩnh của lớp bên ngoài.
Điều này được giải thích bởi các tính năng cụ thể về cách tổ chức các lớp bên trong. Một lớp bên trong có thể có các hàm tạo có tham số hoặc chỉ hàm tạo mặc định. Nhưng bất chấp điều đó, khi chúng ta tạo một đối tượng của lớp bên trong, một tham chiếu đến đối tượng của lớp bên ngoài được chuyển một cách vô hình tới đối tượng đã tạo của lớp bên trong. Rốt cuộc, sự hiện diện của một tham chiếu đối tượng như vậy là một yêu cầu tuyệt đối. Nếu không, chúng ta sẽ không thể tạo các đối tượng của lớp bên trong.
Nhưng nếu một phương thức của lớp bên ngoài là tĩnh, thì chúng ta có thể không có đối tượng của lớp bên ngoài! Và điều này sẽ vi phạm logic về cách thức hoạt động của một lớp bên trong. Trong trường hợp này, trình biên dịch sẽ báo lỗi:
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
Một lớp bên trong không thể chứa các biến và phương thức tĩnh.
Logic là như nhau: các phương thức và biến tĩnh có thể tồn tại và được gọi hoặc tham chiếu ngay cả khi không có đối tượng.
Nhưng không có đối tượng của lớp bên ngoài, chúng ta sẽ không truy cập được vào lớp bên trong.
Một sự mâu thuẫn rõ ràng! Đây là lý do tại sao các biến và phương thức tĩnh không được phép sử dụng trong các lớp bên trong.
Trình biên dịch sẽ báo lỗi nếu bạn cố tạo chúng:
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); } } }
-
Khi tạo một đối tượng của lớp bên trong, công cụ sửa đổi truy cập của nó rất quan trọng.
Một lớp bên trong có thể được đánh dấu bằng các công cụ sửa đổi truy cập tiêu chuẩn:
public
,private
,protected
vàpackage private
.Vì sao vấn đề này?
Điều này ảnh hưởng đến nơi chúng ta có thể tạo các thể hiện của lớp bên trong chương trình của mình.
Nếu
Seat
lớp của chúng ta được khai báo làpublic
, chúng ta có thể tạoSeat
các đối tượng trong bất kỳ lớp nào khác. Yêu cầu duy nhất là một đối tượng của lớp bên ngoài cũng phải tồn tại.Nhân tiện, chúng tôi đã làm điều này ở đây:
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(); } }
Chúng tôi dễ dàng có được quyền truy cập vào
Handlebar
lớp bên trong từMain
lớp.Nếu chúng ta khai báo lớp bên trong là
private
, chúng ta sẽ chỉ có thể tạo các đối tượng bên trong lớp bên ngoài.Chúng ta không còn có thể tạo một
Seat
đối tượng "ở bên ngoài":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(); } }
Chắc bạn cũng đã hiểu logic rồi :)
-
Công cụ sửa đổi truy cập cho các lớp bên trong hoạt động giống như đối với các biến thông thường.
Công
protected
cụ sửa đổi cung cấp quyền truy cập vào một biến thể hiện trong các lớp con và các lớp trong cùng một gói.protected
cũng hoạt động cho các lớp bên trong. Chúng ta có thể tạoprotected
các đối tượng của lớp bên trong:- ở lớp ngoài;
- trong các lớp con của nó;
- trong các lớp nằm trong cùng một gói.
Nếu lớp bên trong không có công cụ sửa đổi truy cập (
package private
), các đối tượng của lớp bên trong có thể được tạo:- ở lớp ngoài;
- trong các lớp nằm trong cùng một gói.
Bạn đã quen thuộc với các công cụ sửa đổi trong một thời gian dài, vì vậy không có vấn đề gì ở đây.
GO TO FULL VERSION