class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
Chính những lớp 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 - 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 theo một cách khác.
- Các lớp lồng nhau tĩnh - các lớp lồng nhau tĩnh.
- lớp học địa phương
- 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("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!");
}
}
}
Ở đây chúng tôi có một lớp học Bicycle
- xe đạp. Nó có 2 trường và 1 phương thức - start()
. Sự khác biệt của nó so với một lớp thông thường là nó có hai lớp, mã được viết bên trong Bicycle
- đây là các lớp HandleBar
(vô lăng) và Seat
(ghế ngồi). Đây là các lớp chính thức: như bạn có thể thấy, mỗi lớp đều có các phương thức riêng. Tại thời điểm này, bạn có thể thắc mắc: tại sao chúng ta lại đặt một lớp này vào một lớp khác? Tại sao làm cho chúng nội bộ? Được rồi, giả sử chúng ta cần các lớp riêng biệt cho vô lăng và ghế ngồi trong chương trình. Nhưng bạn không cần phải lồng chúng! Bạn có thể tổ chức các lớp học thông thường. Ví dụ như thế này:
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!");
}
}
Câu hỏi rất hay! Tất nhiên, chúng tôi không có giới hạn kỹ thuật - chúng tôi có thể làm theo cách này. Đó là việc thiết kế các lớp học một cách chính xác theo quan điểm của một chương trình cụ thể và theo ý nghĩa của chương trình đó. Các lớp bên trong là các lớp dùng để làm nổi bật một thực thể nhất định trong một chương trình được liên kết chặt chẽ với một thực thể khác. Vô lăng, ghế ngồi, bàn đạp là những bộ phận của một chiếc xe đạp. Tách khỏi xe đạp, chúng không có ý nghĩa gì. Nếu chúng ta tách tất cả các lớp này thành các lớp công khai riêng biệt, thì chương trình của chúng ta có thể có đoạn mã sau:
public class Main {
public static void main(String[] args) {
HandleBar handleBar = new HandleBar();
handleBar.right();
}
}
Ừm... Ý nghĩa của đoạn mã này thậm chí còn khó giải thích hơn. Chúng tôi có một số tay lái xe đạp kỳ lạ (tại sao lại cần nó? Thành thật mà nói, tôi không biết). Và cái vô lăng này quay sang phải... tự nó, không có xe đạp... vì lý do nào đó. Bằng cách tách rời bản chất của vô lăng khỏi bản chất của chiếc xe đạp, chúng ta đã đánh mất tính logic của chương trình. Sử dụng lớp bên trong, mã trông hoàn toàn 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 của bảng điều khiển:
Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
Những gì đang xảy ra đột nhiên có ý nghĩa! :) Chúng tôi đã tạo ra một đối tượng xe đạp. Chúng tôi đã tạo ra hai “đối tượng phụ” của nó - vô lăng và ghế ngồi. Chúng tôi đã nâng ghế lên cao hơn để thuận tiện - và chúng tôi khởi hành: chúng tôi lăn bánh và lái đến nơi chúng tôi cần đến! :) Các phương thức chúng ta cần được gọi trên các đối tượng cần thiết. Mọi thứ đều đơn giản và thuận tiện. Trong ví dụ này, việc làm nổi bật tay lái và ghế ngồi sẽ tăng cường khả năng đóng gói (chúng tôi đang ẩn dữ liệu về các bộ phận của xe đạp trong lớp tương ứng) và cho phép chúng tôi tạo ra 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ô hình một cửa hàng xe đạp và phụ tùng. Trong tình huống này, giải pháp trước đó của chúng tôi sẽ thất bại. Trong giới hạn của một cửa hàng phụ tùng, mỗi bộ phận riêng lẻ của một chiếc xe đạp đều có ý nghĩa thậm chí ngoài bản chất của chiếc xe đạp. Ví dụ: chúng ta sẽ cần các phương thức như “bán bàn đạp cho người mua”, “mua ghế mới”, v.v. Sẽ là một sai lầm nếu sử dụng các lớp nội bộ ở đây - mỗi bộ phận riêng lẻ của chiếc xe đạp trong chương trình mới của chúng tôi đều có ý nghĩa riêng: nó tách biệt với bản chất của chiếc xe đạp và không hề gắn liền với nó. Đây là điều bạn nên chú ý nếu bạn đang thắc mắc liệu mình có cần sử dụng các lớp bên trong hay tách tất cả các thực thể thành các lớp riêng biệt hay không. Lập trình hướng đối tượng rất tuyệt vời vì nó giúp dễ dàng mô hình hóa các thực thể trong thế giới thực. Đây là những gì bạn có thể sử dụng làm hướng dẫn khi quyết định có nên sử dụng các lớp bên trong hay không. Trong cửa hàng thực, các bộ phận được tách biệt khỏi xe đạp - điều này là bình thường. Điều này có nghĩa là điều này sẽ đúng khi thiết kế một chương trình. Được rồi, chúng ta đã sắp xếp xong “triết học” :) Bây giờ hãy làm quen với các tính năng “kỹ thuật” quan trọng của các lớp bên trong. Đây là những gì bạn chắc chắn cần phải nhớ và hiểu:
-
Một đối tượng của lớp bên trong không thể tồn tại nếu không có đối tượng của lớp “bên ngoài”.
Điều này rất hợp lý: đó là lý do tại sao chúng tôi tạo nó thành các lớp
Seat
nội bộHandleBar
, để vô lăng và ghế ngồi không có chủ sở hữu sẽ không xuất hiện chỗ này chỗ kia trong chương trình của chúng tôi.Mã này sẽ không biên dịch:
public static void main(String[] args) { HandleBar handleBar = new HandleBar(); }
Tính năng quan trọng sau đây xuất phát từ điều nà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
Bicycle
một biến vào lớp của chúng taint seatPostDiameter
- đường kính của cột an toàn.Sau đó, trong lớp bên trong,
Seat
chúng ta có thể tạo một phương thứcgetSeatParam()
cho chúng ta biết tham số 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("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); } } }
Và bây giờ chúng ta có thể lấy 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.getSeatParam(); } }
Đầu ra của bảng điều khiển:
Параметр сиденья: диаметр подседельного штыря = 40
Chú ý:biến mới được khai báo với công cụ sửa đổi chặt chẽ nhất -
private
. Và lớp bên trong vẫn có quyền truy cập! -
Không thể tạo đối tượng lớp bên trong theo 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 thiết kế của các lớp nội bộ. Một lớp bên trong có thể có các hàm tạo với các tham số hoặc chỉ là một hàm tạo mặc định. Nhưng bất chấp điều này, khi chúng ta tạo một đối tượng của lớp bên trong, một tham chiếu đến một đối tượng của lớp “bên ngoài” sẽ được truyền vào nó một cách lặng lẽ. Suy cho cùng, sự hiện diện của một đối tượng như vậy là điều kiện tiên quyết. 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 phương thức của lớp bên ngoài là tĩnh thì đối tượng của lớp bên ngoài có thể không tồn tại! Điều này có nghĩa là logic của lớp bên trong sẽ bị phá vỡ. Trong tình huống như vậy, trình biên dịch sẽ đưa ra lỗi:
public static Seat createSeat() { //Bicycle.this cannot be referenced from a static context return new Seat(); }
-
Lớp bên trong không thể chứa các biến và phương thức tĩnh.
Logic ở đây giống nhau: các phương thức và biến tĩnh có thể tồn tại và được gọi ngay cả khi không có đối tượng.
Nhưng nếu không có đối tượng của lớp “bên ngoài”, chúng ta sẽ không thể truy cập vào lớp bên trong.
Một sự mâu thuẫn rõ ràng! Vì vậy, sự hiện diện của các biến và phương thức tĩnh trong các lớp bên trong đều bị cấm.
Trình biên dịch sẽ báo lỗi khi cố gắng tạo chúng:
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); } } }
-
Khi tạo một đối tượng lớp bên trong, công cụ sửa đổi truy cập của nó đóng một vai trò quan trọng.
Một lớp bên trong có thể được biểu thị bằng các công cụ sửa đổi truy cập tiêu chuẩn -
public
,private
,protected
vàpackage private
.Tại sao nó lại quan trọng?
Điều này ảnh hưởng đến vị trí trong chương trình của chúng ta, chúng ta có thể khởi tạo lớp bên trong.
Nếu lớp của chúng ta
Seat
được khai báo làpublic
, chúng ta có thể tạo các đối tượng của nó 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 đã thực hiện việc 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 truy cập lớp bên trong
HandleBar
từ tệpMain
.Nếu chúng ta khai báo lớp bên trong là
private
, chúng ta sẽ chỉ có quyền truy cập để tạo các đối tượng bên trong lớp “bên ngoài”.Seat
Chúng ta sẽ không thể tạo một đối tượng từ bên ngoài nữa: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(); } }
Có lẽ bạn đã 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 cụ sửa đổi
protected
cung cấp quyền truy cập vào một biến lớp trong các lớp con của nó và trong các lớp nằm trong cùng một gói.Điều tương tự cũng
protected
xảy ra với các lớp bên trong. Các đối tượngprotected
lớp bên trong có thể được tạo:- bên trong lớp "bên ngoài";
- trong các lớp con cháu 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- bên trong lớp "bên ngoài";
- trong các lớp nằm trong cùng một gói.
Bạn đã làm quen với các sửa đổi từ lâu nên sẽ không có vấn đề gì ở đây.
GO TO FULL VERSION