Các Đặc Trưng Của Lập Trình Hướng Đối Tượng Trong Java

Các đặc trưng của lập trình hướng đối tượng (OOP) trong Java là nội dung cốt lõi trong các cuộc phỏng vấn kỹ thuật. Bài viết này sẽ phân tích chi tiết bốn đặc trưng chính, khái niệm quan trọng, các câu hỏi thường gặpứng dụng thực tế, kèm theo ví dụ minh họa và giải thích sâu sắc.

I. Phân Tích Bốn Đặc Trưng Chính

  1. Bao Bọc (Encapsulation)
  • Ý tưởng cốt lõi: Ẩn các chi tiết triển khai bên trong đối tượng, chỉ cung cấp các giao diện cần thiết (thường thông qua phương thức getter/setter).
  • Cách thực hiện: Sử dụng từ khóa private cho các trường dữ liệu, cung cấp phương thức truy cập công khai. Có thể thêm logic kiểm tra trong phương thức (ví dụ: setYearOfBirth(int year) kiểm tra năm sinh hợp lệ).
  • Lợi ích: Tăng tính bảo mật dữ liệu, cải thiện khả năng bảo trì mã, giảm sự phụ thuộc giữa các module.
  • Câu hỏi thường gặp:
  • Làm thế nào để thiết kế một lớp bất biến? → Khai báo lớp là final, tất cả các trường dữ liệu là private final, không cung cấp phương thức setter.
  • Ưu điểm của bao bọc? → Ẩn thông tin, tính độc lập của module.
  • Ví dụ mã:
public class Employee {
    private String fullName;
    private int yearOfBirth;

    public void setYearOfBirth(int year) {
        if (year >= 1900 && year <= 2023) {
            this.yearOfBirth = year;
        }
    }

    public int getYearOfBirth() {
        return yearOfBirth;
    }
}
  1. Kế Thừa (Inheritance)
  • Ý tưởng cốt lõi: Lớp con kế thừa thuộc tính và phương thức từ lớp cha, đạt được tái sử dụng mã và mở rộng (mối quan hệ is-a). Java chỉ hỗ trợ kế thừa đơn.
  • Từ khóa: extends dùng để kế thừa lớp, super dùng để gọi phương thức khởi tạo của lớp cha hoặc thành viên.
  • Câu hỏi thường gặp:
  • Phương thức khởi tạo có thể bị ghi đè không? → Không thể, nhưng có thể bị ghi đè. Lớp con gọi phương thức khởi tạo của lớp cha thông qua super().
  • Vì sao Java không hỗ trợ kế thừa đa? → Để tránh vấn đề "kế thừa hình thoi", gây mâu thuẫn khi gọi phương thức. Có thể mô phỏng kế thừa đa thông qua việc triển khai nhiều giao diện.
  • Ví dụ mã:
class Vehicle {
    void makeNoise() { System.out.println("Vehicle sound"); }
}
class Car extends Vehicle {
    @Override
    void makeNoise() { System.out.println("Vroom"); }
}
  1. Đa Hình (Polymorphism)
  • Ý tưởng cốt lõi: Cùng một thao tác áp dụng cho các đối tượng khác nhau, tạo ra hành vi khác nhau. Bao gồm đa hình thời gian biên dịch (overloading) và đa hình thời gian chạy (overriding).
  • Cơ chế: Liên kết động tại thời gian chạy (dùng tham chiếu của lớp cha để trỏ đến đối tượng của lớp con).
  • Câu hỏi thường gặp:
  • Sự khác biệt giữa ghi đè (override) và ghi chồng (overload)? | Tính chất | Ghi đè (Override) | Ghi chồng (Overload) | |---|---|---| | Ký hiệu phương thức | Phải giống nhau | Phải khác nhau (loại tham số, số lượng, thứ tự) | | Loại trả về | Giống nhau hoặc là lớp con | Thay đổi được | | Thời điểm liên kết | Liên kết động tại thời gian chạy | Liên kết tĩnh tại thời gian biên dịch | | Mục đích | Thực hiện đa hình | Cung cấp các phiên bản khác nhau của cùng một phương thức |
  • Cơ chế thực hiện đa hình? → Tham chiếu của lớp cha hoặc giao diện có thể trỏ đến đối tượng cụ thể của lớp con, phương thức được gọi tại thời gian chạy.
  • Ví dụ mã:
Vehicle myCar = new Car(); // Chuyển đổi lên
myCar.makeNoise(); // In ra "Vroom" (liên kết động với phương thức của lớp Car)
  1. Trừu Tượng (Abstraction)
  • Ý tưởng cốt lõi: Trích xuất các đặc điểm chung của đối tượng, tạo thành lớp hoặc giao diện, bỏ qua chi tiết triển khai.
  • Thực hiện: Thông qua lớp trừu tượng (abstract class, có thể chứa một phần triển khai) hoặc giao diện (interface, từ JDK 8 trở lên có thể chứa phương thức mặc định).
  • Câu hỏi thường gặp: Sự khác biệt giữa lớp trừu tượng và giao diện? | Tính chất | Lớp Trừu Tượng (Abstract Class) | Giao Diện (Interface) | |---|---|---| | Phương thức | Có thể chứa phương thức trừu tượng và phi trừu tượng | Từ JDK 8 trước, tất cả phương thức đều là trừu tượng; sau này có thể chứa phương thức mặc định/định tĩnh | | Trường | Có thể chứa biến thực thể | Trường là static final | | Kế thừa | Kế thừa đơn | Triển khai đa | | Phương thức khởi tạo | Có | Không | | Mục đích thiết kế | Tái sử dụng mã, biểu thị mối quan hệ "is-a" | Định nghĩa giao thức, biểu thị khả năng "has-a" |

II. Các Khái Niệm Bổ Sung Quan Trọng

  1. Từ Khóa Truy Cập
  • private > default (trong package) > protected (trong package + lớp con) > public.
  1. Từ Khóa final
  • Dùng cho lớp: Không thể kế thừa (ví dụ: String).
  • Dùng cho phương thức: Không thể ghi đè.
  • Dùng cho biến: Giá trị cơ bản không thể thay đổi, tham chiếu đối tượng không thể thay đổi (nhưng nội dung đối tượng có thể thay đổi).
  1. superthis
  • super: Truy cập thành viên của lớp cha hoặc gọi phương thức khởi tạo của lớp cha.
  • this: Truy cập thành viên của đối tượng hiện tại hoặc gọi phương thức khởi tạo của lớp hiện tại.
  • Sự khác biệt: super trỏ đến lớp cha, this trỏ đến đối tượng hiện tại; super()this() phải được đặt ở dòng đầu tiên của phương thức khởi tạo, không thể cùng tồn tại.
  1. Sao Chép Đối Tượng
  • Sự khác biệt giữa sao chép nông (shallow copy) và sao chép sâu (deep copy): Sao chép sâu sao chép đối tượng và tất cả các đối tượng con được tham chiếu, bản sao hoàn toàn độc lập với đối tượng gốc; sao chép nông chỉ sao chép đối tượng, không sao chép các đối tượng được tham chiếu, các đối tượng con vẫn chia sẻ với đối tượng gốc.
  1. ==equals()
  • ==: So sánh địa chỉ bộ nhớ.
  • equals(): Mặc định hành vi giống ==, nhưng thường được ghi đè để so sánh nội dung logic của đối tượng (ví dụ: String).
  • Khi ghi đè equals() phải ghi đè hashCode(): Ngược lại, có thể xảy ra tình trạng hai đối tượng logic bằng nhau nhưng có giá trị hashCode khác nhau, gây xung đột trong các tập hợp như HashMap.

III. Ứng Dụng Thực Tế Và Nguyên Tắc Thiết Kế

  1. Nguyên Tắc SOLID (Nền tảng thiết kế OOP)
  • Nguyên tắc Trách Nhiệm Đơn (SRP): Một lớp chỉ chịu trách nhiệm cho một lĩnh vực chức năng cụ thể.
  • Nguyên tắc Mở Rộng - Đóng Khóa (OCP): Mở rộng cho phép mở rộng, đóng khóa cho phép sửa đổi.
  • Nguyên tắc Thay Thế Liskov (LSP): Lớp con phải có thể thay thế lớp cha.
  • Nguyên tắc Tách Giao Diện (ISP): Sử dụng nhiều giao diện cụ thể hơn là một giao diện lớn.
  • Nguyên tắc Đảo Ngược Phụ Thuộc (DIP): Mô-đun cao cấp không nên phụ thuộc vào mô-đun thấp cấp, cả hai đều nên phụ thuộc vào trừu tượng.
  1. Ứng Dụng Mẫu Thiết Kế
  • Mẫu Nhà Máy (Factory Pattern): Tạo đối tượng không trực tiếp bởi phía khách hàng, mà do phương thức nhà máy quyết định.
  • Mẫu Đơn Thể (Singleton Pattern): Đảm bảo một lớp chỉ có một thể hiện duy nhất.
  • Mẫu Người Theo Dõi (Observer Pattern): Định nghĩa mối quan hệ một-nhiều giữa các đối tượng, khi một đối tượng thay đổi trạng thái, tất cả các đối tượng phụ thuộc sẽ nhận được thông báo và tự động cập nhật.

IV. Tập Hợp Câu Hỏi Thường Gặp Trong Phỏng Vấn

  1. Phương thức khởi tạo có thể bị ghi đè không? Tại sao? → Không thể, vì phương thức khởi tạo không thể kế thừa.
  2. Giao diện và lớp trừu tượng có những khác biệt gì?
  3. Tại sao String được thiết kế là bất biến? → An toàn và ổn định, hiệu suất, thân thiện với bộ nhớ đệm, an toàn luồng.
  4. Sự khác biệt giữa truyền giá trị và truyền tham chiếu? → Truyền giá trị sao chép giá trị thực tế vào hàm, truyền tham chiếu sao chép địa chỉ đối tượng trực tiếp vào hàm.
  5. Tại sao khi ghi đè equals() phải ghi đè hashCode()? → Nếu chỉ ghi đè equals() mà không ghi đè hashCode(), có thể gây ra tình trạng hai đối tượng logic bằng nhau nhưng có giá trị hashCode khác nhau, gây xung đột trong các tập hợp như HashMap.

Thẻ: Java oop encapsulation inheritance polymorphism

Đăng vào ngày 17 tháng 6 lúc 18:32