Ngừng 'trừu tượng' với abstract: Hiểu ngay keyword 'nhức nhối' này trong Java OOP!
Java – OOP

Ngừng 'trừu tượng' với abstract: Hiểu ngay keyword 'nhức nhối' này trong Java OOP!

Author

Admin System

@root

Ngày xuất bản

20 Mar, 2026

Lượt xem

2 Lượt

abstract keyword

Chào các "dev non" tương lai, hôm nay anh Creyt sẽ cùng các em "phá đảo" một trong những keyword mà nhiều bạn mới học Java cứ thấy nó là… muốn "tắt app" ngay lập tức: abstract. Nghe có vẻ "trừu tượng" thật, nhưng tin anh đi, sau buổi này các em sẽ thấy nó "dễ như ăn kẹo"!

1. abstract là gì mà Gen Z hay "ngáo ngơ"?

"Trừu tượng" trong lập trình, đặc biệt là trong Java OOP, không phải là cái gì đó khó hiểu hay mơ hồ đâu mấy đứa. Hãy hình dung thế này:

abstract class (Lớp trừu tượng): Nó giống như một bản thiết kế nhà nhưng chưa hoàn thiện. Bản thiết kế đó có thể có sẵn một số phòng ốc, cửa nẻo (các phương thức và thuộc tính bình thường), nhưng lại có những phần quan trọng chưa được vẽ xong (các phương thức trừu tượng). Vì nó chưa hoàn thiện, nên em không thể xây một căn nhà từ bản thiết kế này trực tiếp được. Em phải lấy bản thiết kế này, thêm thắt chi tiết vào những chỗ còn thiếu, rồi mới xây được nhà.

abstract method (Phương thức trừu tượng): Đây chính là những phần chưa được vẽ xong trong bản thiết kế đó. Nó chỉ là một cái tên phương thức, một chữ ký, không có phần "thân" (không có code bên trong). Nó như một lời hứa vậy: "Ê, đứa nào kế thừa tao, phải tự định nghĩa cái hành động này nha!".

Tóm lại: abstract sinh ra để:

  • Định nghĩa một khuôn mẫu chung: Tạo ra một cấu trúc, một "hợp đồng" mà các lớp con phải tuân theo.
  • Ép buộc triển khai: Bắt buộc các lớp con phải "hoàn thiện" những phần còn thiếu.
  • Không cho phép tạo đối tượng trực tiếp: Vì bản thân lớp abstract chưa hoàn chỉnh, nên không thể tạo ra "thực thể" từ nó.

2. Code Ví Dụ Minh Họa: "Thực chiến" ngay!

Giờ thì mình cùng "code dạo" một chút để hiểu rõ hơn nha. Anh Creyt sẽ lấy ví dụ về một hệ thống quản lý các loại phương tiện (xe cộ).

// Bước 1: Định nghĩa một abstract class 'Vehicle'
// Đây là bản thiết kế chung cho mọi loại xe, nhưng chưa hoàn chỉnh
abstract class Vehicle {
    String brand;
    int year;

    // Constructor bình thường, abstract class vẫn có thể có constructor
    public Vehicle(String brand, int year) {
        this.brand = brand;
        this.year = year;
        System.out.println("Khởi tạo một Vehicle của hãng " + brand + " năm " + year);
    }

    // Một phương thức bình thường, có thân
    public void displayInfo() {
        System.out.println("Thương hiệu: " + brand + ", Năm sản xuất: " + year);
    }

    // Một phương thức trừu tượng: 'startEngine()'
    // Mọi loại xe đều phải có cách khởi động, nhưng mỗi xe một kiểu
    // Nên ở đây ta chỉ định nghĩa là 'phải có', chứ không nói 'làm thế nào'
    public abstract void startEngine(); 

    // Một phương thức trừu tượng khác: 'stopEngine()'
    public abstract void stopEngine();
}

// Bước 2: Tạo các lớp con kế thừa 'Vehicle'
// Các lớp con này phải 'hoàn thiện' những phần còn thiếu của 'Vehicle'

class Car extends Vehicle {
    public Car(String brand, int year) {
        super(brand, year);
        System.out.println("-> Là một chiếc Car.");
    }

    @Override
    public void startEngine() {
        System.out.println("Xe hơi " + brand + " khởi động bằng chìa khóa/nút bấm.");
    }

    @Override
    public void stopEngine() {
        System.out.println("Xe hơi " + brand + " tắt máy bằng cách xoay chìa/bấm nút.");
    }
}

class Motorcycle extends Vehicle {
    public Motorcycle(String brand, int year) {
        super(brand, year);
        System.out.println("-> Là một chiếc Motorcycle.");
    }

    @Override
    public void startEngine() {
        System.out.println("Xe máy " + brand + " khởi động bằng nút đề hoặc đạp.");
    }

    @Override
    public void stopEngine() {
        System.out.println("Xe máy " + brand + " tắt máy bằng cách gạt công tắc.");
    }
}

// Bước 3: Thử nghiệm trong lớp Main
public class AbstractDemo {
    public static void main(String[] args) {
        // KHÔNG THỂ tạo đối tượng từ abstract class trực tiếp!
        // Uncomment dòng dưới và xem lỗi:
        // Vehicle genericVehicle = new Vehicle("Generic", 2020); // Lỗi compile time!

        Car myCar = new Car("Toyota", 2022);
        myCar.displayInfo();
        myCar.startEngine();
        myCar.stopEngine();
        System.out.println("\n");

        Motorcycle myBike = new Motorcycle("Honda", 2023);
        myBike.displayInfo();
        myBike.startEngine();
        myBike.stopEngine();
        System.out.println("\n");

        // Polymorphism (tính đa hình) vẫn hoạt động ngon lành!
        Vehicle[] vehicles = new Vehicle[2];
        vehicles[0] = new Car("Ford", 2021);
        vehicles[1] = new Motorcycle("Yamaha", 2024);

        System.out.println("--- Các phương tiện trong danh sách ---");
        for (Vehicle v : vehicles) {
            v.displayInfo();
            v.startEngine();
            System.out.println("------------------");
        }
    }
}

Output khi chạy:

Khởi tạo một Vehicle của hãng Toyota năm 2022
-> Là một chiếc Car.
Thương hiệu: Toyota, Năm sản xuất: 2022
Xe hơi Toyota khởi động bằng chìa khóa/nút bấm.
Xe hơi Toyota tắt máy bằng cách xoay chìa/bấm nút.

Khởi tạo một Vehicle của hãng Honda năm 2023
-> Là một chiếc Motorcycle.
Thương hiệu: Honda, Năm sản xuất: 2023
Xe máy Honda khởi động bằng nút đề hoặc đạp.
Xe máy Honda tắt máy bằng cách gạt công tắc.

Khởi tạo một Vehicle của hãng Ford năm 2021
-> Là một chiếc Car.
Khởi tạo một Vehicle của hãng Yamaha năm 2024
-> Là một chiếc Motorcycle.
--- Các phương tiện trong danh sách ---
Thương hiệu: Ford, Năm sản xuất: 2021
Xe hơi Ford khởi động bằng chìa khóa/nút bấm.
------------------
Thương hiệu: Yamaha, Năm sản xuất: 2024
Xe máy Yamaha khởi động bằng nút đề hoặc đạp.
------------------

Thấy chưa? Lớp Vehicleabstract nên không tạo đối tượng trực tiếp được, nhưng nó lại là một "khuôn mẫu" tuyệt vời để các lớp con như CarMotorcycle kế thừa và "hoàn thiện" hành vi khởi động/tắt máy của riêng mình. Ngon lành cành đào!

Gợi Ý Đọc Tiếp
Implements trong Java: Hợp đồng code cho GenZ

1 Lượt xem

Illustration

3. Mẹo (Best Practices) để "Nhớ dai" và "Dùng đúng"

  • "Ông bố chưa sẵn sàng có con": Hãy nhớ, lớp abstract là một "ông bố" chưa hoàn chỉnh, chưa "sẵn sàng" để tự mình "sinh ra" một đứa con (object). Nó cần các "bà mẹ" (lớp con concrete) để "hoàn thiện" và sinh ra những "đứa con" thực sự.
  • "Hợp đồng bắt buộc": Khi em khai báo một phương thức là abstract, nó giống như một điều khoản bắt buộc trong hợp đồng. Đứa nào ký (kế thừa) hợp đồng này, phải thực hiện điều khoản đó. Nếu không, compiler sẽ "đấm" cho một trận.
  • abstract class vs. interface: Đừng nhầm lẫn!
    • abstract class: Có thể có cả phương thức abstractconcrete (có thân). Có thể có thuộc tính, constructor. Kế thừa một abstract class thì dùng extends.
    • interface: Từ Java 8 trở về trước, chỉ có phương thức abstract (ngầm định). Từ Java 8 trở đi có thể có defaultstatic methods. Không có constructor. Triển khai interface thì dùng implements.
    • Mẹo nhớ: abstract class là một cái gì đó nhưng chưa hoàn thiện (is-a relationship, nhưng còn thiếu). interface có thể làm cái gì đó (can-do relationship).
  • Chỉ abstract khi thực sự cần: Đừng lạm dụng. Nếu một lớp đã đủ chi tiết để tạo đối tượng, đừng biến nó thành abstract chỉ vì "nghe có vẻ pro". Mục đích chính là để tạo ra một cấu trúc chung và buộc các lớp con phải tuân thủ.

4. Ứng dụng thực tế: "Abstract" ở đâu trong cuộc sống số?

"Abstract" không chỉ nằm trong sách vở đâu các em, nó len lỏi khắp nơi trong các ứng dụng và framework mà chúng ta dùng hàng ngày:

  • Java Collections Framework: Các lớp như java.util.AbstractList, AbstractSet, AbstractMap là những ví dụ điển hình. Chúng cung cấp các triển khai cơ bản cho các phương thức chung của List, Set, Map, để các lớp con cụ thể (như ArrayList, HashSet) chỉ cần tập trung vào những logic riêng biệt của mình mà không cần viết lại từ đầu.
  • Frameworks lớn (Spring, Hibernate): Rất nhiều framework sử dụng abstract class để tạo ra các điểm mở rộng (extension points). Ví dụ, bạn muốn tạo một bộ lọc (filter) tùy chỉnh trong Spring Security, bạn có thể kế thừa từ một abstract class nào đó, và framework sẽ yêu cầu bạn triển khai một số phương thức cụ thể để nó biết cách xử lý bộ lọc của bạn.
  • Hệ thống xử lý tài liệu: Tưởng tượng bạn có một lớp AbstractDocument với phương thức abstract render(). Các lớp con như PdfDocument, WordDocument, HtmlDocument sẽ triển khai phương thức render() theo cách riêng của từng định dạng.

5. Thử nghiệm đã từng và hướng dẫn nên dùng cho case nào

Anh Creyt đã từng "nghiên cứu" và áp dụng abstract trong rất nhiều dự án, và đây là kinh nghiệm xương máu:

  • Nên dùng khi:

    • Bạn muốn định nghĩa một "kiểu" đối tượng chung nhưng không muốn tạo đối tượng trực tiếp từ nó. Ví dụ, "Động vật" là một khái niệm chung, nhưng bạn không thể tạo ra một "con động vật" chung chung được, bạn phải tạo ra "con chó", "con mèo". Lúc này, Animal nên là abstract class.
    • Bạn muốn các lớp con phải có một hành vi cụ thể nào đó, nhưng cách thực hiện hành vi đó lại khác nhau. Như ví dụ startEngine() ở trên, mọi xe đều khởi động, nhưng cách khởi động khác nhau.
    • Bạn muốn cung cấp một số triển khai mặc định (concrete methods) cùng với các phương thức trừu tượng. Điều này giúp tái sử dụng code tốt hơn so với interface (trước Java 8).
  • Không nên dùng khi:

    • Bạn chỉ muốn định nghĩa một "hợp đồng" thuần túy, không có bất kỳ triển khai nào. Lúc này, interface là lựa chọn tốt hơn, vì nó tập trung hoàn toàn vào việc định nghĩa hành vi mà không dính dáng gì đến trạng thái (thuộc tính) hay triển khai mặc định.
    • Lớp của bạn đã đủ "hoàn chỉnh" và có thể tạo đối tượng trực tiếp. Đừng biến nó thành abstract nếu không có lý do chính đáng.

Vậy đó, abstract không hề "trừu tượng" chút nào nếu chúng ta hiểu đúng bản chất của nó. Nó là một công cụ mạnh mẽ trong OOP giúp chúng ta xây dựng các hệ thống linh hoạt, dễ mở rộng và có cấu trúc rõ ràng. Hãy thực hành nhiều vào, rồi các em sẽ thấy nó "ngấm" lúc nào không hay!

Chúc các em "code trâu" và "debug ít"! Hẹn gặp lại trong bài học tiếp theo của anh Creyt!

Thuộc Series: Java – OOP

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!