- vừa được xem lúc

Abstract Class và Interface trong Java

0 0 3

Người đăng: Bách Nguyễn Ngọc

Theo Viblo Asia

Trong lập trình hướng đối tượng, Abstract class và Interface là hai công cụ quan trọng dùng để xây dựng kiến trúc phần mềm theo hướng mở rộng và tái sử dụng. Tuy nhiên, nhiều người mới học thường dễ bị nhầm lẫn giữa hai khái niệm này. Bài viết này sẽ giúp bạn hiểu rõ bản chất, điểm giống và khác nhau giữa chúng, từ đó biết khi nào nên dùng cái nào.

A. Abtract Class

  • Abstract class (lớp trừu tượng) là một lớp không thể tạo đối tượng trực tiếp (tức khởi tảo thông qua từ khóa new), được dùng làm lớp cơ sở cho các lớp con

  • Các lớp con kế thừa (extends) phải triển khai các phương thức abtract (phương thức trừu tượng)

  • Nó có thể chứa:

    • Các phương thức trừu tượng (chưa có nội dung, chỉ khai báo),

    • Các phương thức bình thường (có nội dung cụ thể),

    • Các thuộc tính (biến).

Ví dụ

public abstract class Animal { // Phương thức trừu tượng (không có thân) public abstract void makeSound(); // Phương thức bình thường public void sleep() { System.out.println("Sleeping..."); }
}

Lớp con kế thừa phải override (ghi đè) tất cả các phương thức abstract, trừ khi lớp con cũng là abstract. Tức là:

  • Nếu lớp cha có phương thức abstract (không có thân), thì lớp con bắt buộc phải triển khai (override) phương thức đó để định nghĩa rõ hành vi.

  • Nhưng nếu lớp con đó cũng được khai báo là abstract class, thì nó không cần phải override ngay các phương thức abstract đó, vì lớp con abstract có thể vẫn để lại cho lớp con tiếp theo xử lý.

public class Dog extends Animal { @Override public void makeSound() { System.out.println("Woof Woof!"); }
}

2. Một số đặc điểm

1. Abstract Class không thể tạo đối tượng trực tiếp

Bạn không thể viết:

Animal a = new Animal("Buddy"); // Lỗi biên dịch!

vì Animal là abstract, nó chưa hoàn chỉnh, không thể khởi tạo đối tượng trực tiếp.

2. Abstract Class có thể có constructor

Mặc dù không tạo đối tượng trực tiếp từ abstract class, bạn vẫn có thể khai báo constructor trong abstract class.

Constructor này sẽ được gọi khi một đối tượng của lớp con (non-abstract) được tạo ra.

abstract class Animal { String name; public Animal(String name) { this.name = name; System.out.println("Constructor của Animal được gọi, name = " + name); } abstract void makeSound();
} class Dog extends Animal { public Dog(String name) { super(name); // Gọi constructor của lớp cha (Animal) System.out.println("Constructor của Dog được gọi"); } @Override void makeSound() { System.out.println(name + " says Woof!"); }
} public class Test { public static void main(String[] args) { Dog dog = new Dog("Buddy"); // Tạo đối tượng Dog dog.makeSound(); }
}

3. Một lớp có thể chỉ định là abstract mà không có phương thức abstract nào

Ví dụ:

abstract class A { public void hello() { System.out.println("Hello from A"); }
}
  • Lớp A ở trên là hợp lệ. Không có phương thức abstract nhưng vẫn là abstract class
  • Lí do cho việc khai báo abstract mà không có phương thức abstract thường là:
    • Lớp đó được thiết kế để không cho phép tạo trực tiếp, dù đã có đầy đủ các phương thức. Tức là nó chỉ dùng làm lớp cha để kế thừa.
    • Ép buộc thiết kế hướng kế thừa (template, strategy, factory pattern...)
    • Làm rõ ý đồ lập trình: "lớp này là nền tảng, không phải để dùng trực tiếp"

4. Abstract class giúp thiết kế theo hướng OOP, tạo ra khuôn mẫu bắt buộc các lớp con phải triển khai.

  • Khi bạn khai báo một hoặc nhiều phương thức là abstract, bạn đang nói: 👉 “Mọi lớp kế thừa từ lớp này phải tự định nghĩa hành vi cụ thể cho những phương thức đó.”

  • Điều này giúp đảm bảo tính nhất quán và kiểm soát thiết kế trong các hệ thống lớn.

abstract class Animal { // Phương thức bắt buộc lớp con phải định nghĩa abstract void makeSound(); // Hành vi chung có thể dùng lại void breathe() { System.out.println("Breathing..."); }
}
class Dog extends Animal { @Override void makeSound() { System.out.println("Woof!"); }
} class Cat extends Animal { @Override void makeSound() { System.out.println("Meow!"); }
}
  • Lớp Animal là khuôn mẫu: mọi "con vật" đều phải biết kêu (makeSound()), nhưng cụ thể kêu thế nào là do từng loài quyết định.

  • breathe() là hành vi chung → lớp cha định nghĩa sẵn → lớp con không cần viết lại.

Kết luận ngắn gọn:

Abstract class giống như “bản thiết kế khung” của một nhóm đối tượng — nó nói “bạn phải làm gì”, nhưng không nói “làm như thế nào”.

B. Interface

Trong Java, interface là một kiểu dữ liệu đặc biệt dùng để khai báo một tập hợp các phương thức trừu tượng (abstract methods) mà một lớp (class) có thể cam kết thực hiện.

Ví dụ:

public interface Animal { void makeSound(); // phương thức trừu tượng
}

Một class muốn triển khai (implement) một interface phải override tất cả các phương thức bên trong interface đó.

public class Dog implements Animal { @Override public void makeSound() { System.out.println("Woof!"); }
}

1. Một số đặc điểm

  • Các phương thức trong interface mặc định là public abstract (kể cả nếu không ghi rõ).

  • Các biến trong interface mặc định là public static final (hằng số).

  • Một lớp có thể implement nhiều interface → hỗ trợ đa kế thừa kiểu interface.

interface Flyable { void fly();
} interface Swimable { void swim();
} class Duck implements Flyable, Swimable { public void fly() { System.out.println("Duck flies"); } public void swim() { System.out.println("Duck swims"); }
}

2. Thay đổi của interface

1️⃣Phương thức mặc định (default) (có thân hàm trong interface)

  • Giúp cung cấp logic mặc định, không bắt buộc class triển khai phải override.

  • Cho phép thêm chức năng mới vào interface mà không làm hỏng các class cũ đang implements interface đó.

public interface PaymentMethod { void pay(double amount); // phương thức chính (bắt buộc override) // default method default void printReceipt(double amount) { System.out.println("Đã thanh toán số tiền: " + amount); System.out.println("Hẹn gặp lại quý khách!"); }
}

⚠️ Lưu ý về xung đột default method Nếu một class implements nhiều interface mà các interface đó có cùng tên default method, bạn phải override trong class.

interface A { default void hello() { System.out.println("Hello from A"); }
} interface B { default void hello() { System.out.println("Hello from B"); }
} class C implements A, B { @Override public void hello() { // Bắt buộc ghi đè để giải quyết xung đột A.super.hello(); // hoặc B.super.hello(); }
}

2️⃣Phương thức tĩnh (static):

  • Trong Java, từ Java 8 trở đi, bạn có thể khai báo phương thức static trong interface
  • Phương thức static trong interface chỉ được gọi qua chính tên của interface
public interface PaymentUtils { static void printSupportHotline() { System.out.println("Gọi 1800-9999 để được hỗ trợ thanh toán."); }
}

Gọi method static từ interface

public class Main { public static void main(String[] args) { PaymentUtils.printSupportHotline(); // ✅ Gọi đúng }
}

Phương thức static trong interface thường

  • Dùng để cung cấp tiện ích chung (utility) liên quan đến logic của interface.

  • Tăng tính đóng gói logic liên quan, tránh viết rải rác vào class tiện ích (utility class) như Utils.java.

3️⃣Private methods trong interface (Java 9)

  • Cho phép viết private method dùng trong nội bộ default và static, giúp tái sử dụng logic và làm code gọn gàng.
  • Không thể gọi từ bên ngoài, chỉ dùng nội bộ trong interface
interface PaymentMethod { default void validate() { log("Validating..."); } private void log(String message) { System.out.println("LOG: " + message); }
}

C. Tổng kết so sánh

Tiêu chí Abstract class Interface
Khai báo abstract class interface
Hỗ trợ kế thừa (inheritance) Chỉ được kế thừa 1 abstract class (single inheritance) Có thể implements nhiều interface
Hỗ trợ đa kế thừa ⛔️ Không ⛔️ Không (đạt được mục đích đa kế thừa thông qua implement lại nhiều interface)
Có thể chứa phương thức abstract? ✔️ Có ✔️ Có
Có thể chứa phương thức có thân hàm? ✔️ Có (normal method) ✔️ Có default, static (Java 8+)
Có thể chứa private method? ✔️ Có ✔️ Có (từ Java 9+)
Có thể chứa biến (field)? ✔️ Có (instance + static + final) ✔️ public static final (hằng số)
Constructor ✔️ Có thể có constructor ⛔️ Không thể có constructor
Access modifier Cho phép: public, protected, private, Chỉ cho phép: public, private (Java 9+), default, static

Trong lập trình Java, cả abstract class và interface đều là những công cụ quan trọng để thiết kế kiến trúc hệ thống một cách linh hoạt và có tính mở rộng cao. Tuy có điểm tương đồng – đều cho phép định nghĩa các phương thức trừu tượng – nhưng mỗi loại phục vụ những mục đích khác nhau:

  • Abstract class thích hợp khi bạn cần chia sẻ logic, trạng thái hoặc cấu trúc chung giữa các lớp.

  • Interface sử dụng khi muốn tạo ra một sườn chung cho hệ thống mà khi các lớp implement lại phải tuân theo, đặc biệt khi cần hỗ trợ đa kế thừa hoặc tích hợp với các tính năng như Lambda trong Java 8+.

Java ngày càng linh hoạt với nhiều cải tiến từ Java 8 trở đi, nên ranh giới giữa hai khái niệm này có thể mờ dần. Tuy nhiên, việc lựa chọn đúng công cụ vẫn là chìa khóa để xây dựng hệ thống mạnh mẽ, mở rộng tốt và dễ bảo trì.

Chọn đúng giữa interface và abstract class – không chỉ là lập trình, mà là thiết kế.

Bình luận

Bài viết tương tự

- vừa được xem lúc

EzyPlatform - Giới thiệu về nền tảng Lập trình từ người Việt

Giới thiệu về Ezyplatform - Nền tảng lập trình từ người Việt. Ezyplatform là gì.

0 0 23

- vừa được xem lúc

EzyPlatform - Tạo blog dễ dàng với EzyBlog - Không cần code hay thiết kế

Giới thiệu. Bạn muốn tạo một trang blog nhưng không muốn đối mặt với sự phức tạp của việc lập trình hay thiết kế web? Đừng lo, EzyPlatform là giải pháp cho bạn.

0 0 24

- vừa được xem lúc

Chapter 1: Nơi mọi thứ bắt đầu

1. Tại sao cần log. . .

0 0 16

- vừa được xem lúc

EzyPlatform - Cách tích hợp đăng nhập nhanh chóng với EzyLogin - Không cần code hay thiết kế

Giới thiệu. Bạn muốn tạo một trang web với tính năng đăng nhập, đăng ký cho người dùng, nhưng không muốn phải đối mặt với sự phức tạp của việc lập trình hay thiết kế giao diện đăng nhập? Đừng lo, EzyL

0 0 22

- vừa được xem lúc

Orm trong hibenate hoạt động như nào? Chúng ta cùng tìm hiểu trong bài viết này nhé!

Có bảo giờ các bạn tự hỏi các kĩ thuật Orm trong Hibenate, hay cách mà spring data jpa thực sự hoạt động như nào không? Hãy cùng mình tìm hiểu nhé. @Entity.

0 0 12

- vừa được xem lúc

Tối ưu hóa vòng lặp trong Java khi xử lý dữ liệu từ cơ sở dữ liệu

Khi phát triển ứng dụng Java, một trong những vấn đề phổ biến mà chúng ta phải đối mặt là tối ưu hóa hiệu suất khi làm việc với dữ liệu lớn từ cơ sở dữ liệu. Đặc biệt, khi cần lấy thông tin liên quan

0 0 7