super keyword: 'Hack' ngược dòng thời gian gọi 'sếp' cha trong Java OOP!
Java – OOP

super keyword: 'Hack' ngược dòng thời gian gọi 'sếp' cha trong Java OOP!

Author

Admin System

@root

Ngày xuất bản

19 Mar, 2026

Lượt xem

1 Lượt

super keyword

Chào các "công dân số" của Creyt! Hôm nay, chúng ta sẽ "giải mã" một "điệp viên" thầm lặng nhưng cực kỳ quyền lực trong thế giới Java OOP: super keyword. Nghe tên đã thấy "siêu" rồi đúng không? Nó chính là "chiếc chìa khóa vạn năng" giúp bạn "hack" ngược dòng thời gian, kết nối trực tiếp với "tổ tiên" (lớp cha) của mình, ngay cả khi bạn đã "lột xác" thành một phiên bản Gen Z đầy cá tính.

super keyword là gì và để làm gì?

Đơn giản mà nói, super trong Java giống như một "bộ đàm" bí mật giúp bạn nói chuyện trực tiếp với lớp cha (parent class) của một đối tượng. Hãy hình dung bạn là một "influencer" Gen Z, có phong cách, tư duy riêng biệt, nhưng đôi khi vẫn muốn "mượn" một chiếc áo khoác vintage từ tủ đồ của bố (lớp cha) để phối đồ, hoặc muốn hỏi bố về một "bí kíp" làm việc mà bạn đã "độ lại" theo cách của mình. super chính là cái "bộ đàm" đó, giúp bạn chỉ thẳng: "Ê, cái này là của bố, không phải của con đâu nha!" Nó giúp bạn truy cập các thành viên (thuộc tính, phương thức) hoặc thậm chí là "quy trình sinh ra" (constructor) của lớp cha.

Tại sao chúng ta cần super?

Trong lập trình hướng đối tượng, đặc biệt là khi bạn dùng khái niệm "kế thừa" (inheritance), con cái có thể "đè đầu cưỡi cổ" (override) các phương thức hay thậm chí là "che giấu" (hide) các thuộc tính của cha. Khi đó, nếu bạn muốn dùng phiên bản gốc của cha, thay vì phiên bản đã được con cái "độ lại", thì super chính là vị cứu tinh. Nó đảm bảo bạn không bị "nhầm lẫn" giữa cái của cha và cái của con.

Case 1: Gọi method/thuộc tính của cha (khi bị con "đè đầu cưỡi cổ")

Tưởng tượng bạn có một phương thức diLam() trong lớp Bo. Khi bạn tạo lớp Con và cũng có một phương thức diLam() riêng (đã override), làm sao để gọi diLam() của cha mà không phải tạo ra một đối tượng Bo riêng biệt? Chính là dùng super.diLam(). Tương tự với thuộc tính.

class Bo {
    String ngheNghiep = "Kỹ sư";

    void diLam() {
        System.out.println("Bố đi làm với tâm thế Kỹ sư chuyên nghiệp.");
    }
}

class Con extends Bo {
    String ngheNghiep = "Streamer"; // Thuộc tính bị che giấu

    @Override
    void diLam() {
        super.diLam(); // Gọi phương thức diLam() của lớp Bo
        System.out.println("Con đi làm với tâm thế Streamer sáng tạo.");
        System.out.println("Nghề nghiệp của bố (qua super): " + super.ngheNghiep); // Truy cập thuộc tính của lớp Bo
        System.out.println("Nghề nghiệp của con (hiện tại): " + this.ngheNghiep); // Truy cập thuộc tính của lớp Con
    }
}

public class DemoSuperKeyword {
    public static void main(String[] args) {
        Con cuTin = new Con();
        cuTin.diLam();
    }
}

Output:

Bố đi làm với tâm thế Kỹ sư chuyên nghiệp.
Con đi làm với tâm thế Streamer sáng tạo.
Nghề nghiệp của bố (qua super): Kỹ sư
Nghề nghiệp của con (hiện tại): Streamer

Ở đây, super.diLam() giúp "cu Tín" vẫn giữ được "nề nếp" của bố trước khi thể hiện cá tính riêng. Và super.ngheNghiep giúp nó khoe nghề nghiệp "chuẩn men" của bố, dù nó đã có nghề "Streamer" cực cool của riêng mình.

Illustration

Case 2: Gọi constructor của cha (để cha "sinh ra" mình trước)

Đây là trường hợp "bắt buộc" mà bạn phải dùng super(). Khi một đối tượng của lớp con được tạo, Java yêu cầu lớp cha phải được khởi tạo trước. Constructor của lớp con sẽ tự động gọi constructor mặc định (không tham số) của lớp cha. Nhưng nếu lớp cha chỉ có constructor có tham số thì sao? Hoặc bạn muốn gọi một constructor cụ thể của cha? Lúc đó, bạn phải tự tay thêm super(...) vào dòng đầu tiên của constructor lớp con.

class SinhVien {
    String ten;
    int tuoi;

    SinhVien(String ten, int tuoi) {
        this.ten = ten;
        this.tuoi = tuoi;
        System.out.println("Sinh viên gốc đã được tạo: " + ten + ", " + tuoi + " tuổi.");
    }
}

class SinhVienIT extends SinhVien {
    String chuyenNganh;

    SinhVienIT(String ten, int tuoi, String chuyenNganh) {
        super(ten, tuoi); // BẮT BUỘC phải là dòng đầu tiên, gọi constructor của lớp cha
        this.chuyenNganh = chuyenNganh;
        System.out.println("Sinh viên IT chuyên ngành " + chuyenNganh + " đã được tạo.");
    }
}

public class DemoSuperConstructor {
    public static void main(String[] args) {
        SinhVienIT hacker = new SinhVienIT("Huy Coder", 20, "An toàn thông tin");
    }
}

Output:

Sinh viên gốc đã được tạo: Huy Coder, 20 tuổi.
Sinh viên IT chuyên ngành An toàn thông tin đã được tạo.

Thấy chưa? super(ten, tuoi) ở đây giống như "giấy khai sinh" của "Huy Coder", đảm bảo rằng phần "SinhVien" cơ bản của cậu ta được tạo ra trước khi cậu ta "lột xác" thành "SinhVienIT" với chuyên ngành "An toàn thông tin" cực ngầu.

Mẹo (Best Practices) từ Creyt để ghi nhớ và dùng hiệu quả

  • super() luôn là "đạo luật" đầu tiên: Khi gọi constructor của cha, super(...) phải là câu lệnh đầu tiên trong constructor của con. Không có ngoại lệ. Coi như đó là "phép tắc" của gia đình.
  • super không phải this: this là "tôi" (đối tượng hiện tại), super là "bố tôi" (lớp cha của đối tượng hiện tại). Đừng nhầm lẫn! this gọi phương thức/thuộc tính của chính lớp đó, super gọi của lớp cha.
  • Dùng khi "đụng hàng": Khi tên phương thức hoặc thuộc tính trong lớp con trùng với lớp cha (override hoặc hide), super là cách duy nhất để truy cập phiên bản của cha.
  • Không thể dùng super trong static context: super liên quan đến đối tượng, mà static thì không. Nên super không thể dùng trong phương thức hoặc khối static.

Thử nghiệm và Ứng dụng thực tế

Trong thế giới code thực tế, super được dùng như cơm bữa, đặc biệt trong các framework lớn hay thư viện UI. Đây là những "thử nghiệm" mà các "tiền bối" đã và đang ứng dụng:

  • Web Frameworks (Spring, Struts): Khi bạn tạo một Controller mới trong Spring, nó thường extends từ một BaseController nào đó. Bạn có thể override các phương thức như init() hay destroy() nhưng vẫn muốn gọi super.init() để đảm bảo các thiết lập cơ bản của framework được thực thi.
  • Game Development: Bạn có lớp Character và các lớp con như Warrior, Mage. Warrior có thể override phương thức attack(), nhưng vẫn muốn gọi super.attack() để xử lý sát thương cơ bản trước khi thêm hiệu ứng đặc biệt của Warrior.
  • UI Libraries (Android Development): Khi bạn tạo một CustomView bằng cách extends từ android.view.View hoặc android.widget.TextView, bạn thường sẽ override các phương thức như onDraw() hoặc onTouchEvent(). Trong những phương thức này, việc gọi super.onDraw(canvas) hoặc super.onTouchEvent(event) là cực kỳ quan trọng để đảm bảo View gốc vẫn xử lý các tác vụ cơ bản như vẽ nền, xử lý sự kiện mặc định. Nếu không có super, View của bạn có thể không hoạt động đúng hoặc mất đi các tính năng cơ bản.

Khi nào nên dùng super?

  • Mở rộng chức năng, không phá vỡ gốc: Khi bạn muốn thêm tính năng mới cho một phương thức của lớp cha, nhưng vẫn muốn giữ lại logic gốc của nó. Gọi super.phuongThucCuaCha() rồi thêm code của bạn vào.
  • Đảm bảo khởi tạo đúng đắn: Bắt buộc phải dùng super(...) trong constructor của lớp con nếu lớp cha chỉ có constructor có tham số, hoặc bạn muốn gọi một constructor cụ thể của cha.
  • Truy cập thuộc tính/phương thức bị che giấu/override: Khi bạn cần truy cập phiên bản của lớp cha mà lớp con đã định nghĩa lại hoặc che giấu.

Vậy là, super không chỉ là một keyword, nó là cầu nối giữa các thế hệ code, giúp bạn xây dựng những hệ thống mạnh mẽ, có tổ chức và dễ bảo trì. Hãy dùng nó một cách thông minh, và bạn sẽ trở thành một "coder Gen Z" thực thụ, vừa hiện đại vừa biết trân trọng "di sản" từ lớp cha!

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!