Skip to main content

Ghi Đè Phương Thức trong Java 🎯

🎯 Mục tiêu: Học cách sử dụng ghi đè phương thức để tùy chỉnh hành vi của phương thức từ lớp cha trong lớp con.

Giới thiệu 📝

Ghi đè phương thức (Method Overriding) là một tính năng của Java cho phép lớp con định nghĩa lại một phương thức đã tồn tại trong lớp cha. Điều này giúp lớp con có thể tùy chỉnh hành vi của phương thức từ lớp cha cho phù hợp với nhu cầu cụ thể của mình.

💡 Fun Fact: Ghi đè phương thức là một dạng của tính đa hình (polymorphism) trong Java, cho phép một phương thức có nhiều cách triển khai khác nhau trong các lớp khác nhau.

1. Cú Pháp Cơ Bản ⚡

Ghi Đè Đơn Giản

public class Animal {
public void makeSound() {
System.out.println("Tiếng kêu của động vật");
}
}

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

Ghi Đè Với Kiểu Trả Về

public class Shape {
public double calculateArea() {
return 0.0;
}
}

public class Circle extends Shape {
private double radius;

@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}

2. Các Loại Ghi Đè 🔄

Ghi Đè Với Access Modifier

public class Parent {
protected void display() {
System.out.println("Parent class");
}
}

public class Child extends Parent {
@Override
public void display() { // Có thể mở rộng phạm vi truy cập
System.out.println("Child class");
}
}

Ghi Đè Với Exception

public class Parent {
public void process() throws IOException {
// Xử lý file
}
}

public class Child extends Parent {
@Override
public void process() throws FileNotFoundException { // Có thể thu hẹp phạm vi exception
// Xử lý file
}
}

3. Ví Dụ Thực Tế 🚀

Quản Lý Nhân Viên

public class Employee {
protected String name;
protected double baseSalary;

public Employee(String name, double baseSalary) {
this.name = name;
this.baseSalary = baseSalary;
}

public double calculateSalary() {
return baseSalary;
}

public void displayInfo() {
System.out.printf("Nhân viên: %s, Lương cơ bản: %.2f%n",
name, baseSalary);
}
}

public class Manager extends Employee {
private double bonus;

public Manager(String name, double baseSalary, double bonus) {
super(name, baseSalary);
this.bonus = bonus;
}

@Override
public double calculateSalary() {
return baseSalary + bonus;
}

@Override
public void displayInfo() {
System.out.printf("Quản lý: %s, Lương cơ bản: %.2f, Thưởng: %.2f%n",
name, baseSalary, bonus);
}
}

Quản Lý Hình Học

public abstract class Shape {
protected String color;

public Shape(String color) {
this.color = color;
}

public abstract double calculateArea();

public void displayInfo() {
System.out.printf("Hình: %s, Màu: %s, Diện tích: %.2f%n",
getClass().getSimpleName(), color, calculateArea());
}
}

public class Rectangle extends Shape {
private double length;
private double width;

public Rectangle(String color, double length, double width) {
super(color);
this.length = length;
this.width = width;
}

@Override
public double calculateArea() {
return length * width;
}

@Override
public void displayInfo() {
System.out.printf("Hình chữ nhật: Màu %s, Chiều dài: %.2f, Chiều rộng: %.2f, Diện tích: %.2f%n",
color, length, width, calculateArea());
}
}

Quản Lý Phương Tiện

public class Vehicle {
protected String brand;
protected String model;
protected int year;

public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
}

public void start() {
System.out.println("Khởi động phương tiện");
}

public void stop() {
System.out.println("Dừng phương tiện");
}

public void displayInfo() {
System.out.printf("Phương tiện: %s %s %d%n", brand, model, year);
}
}

public class Car extends Vehicle {
private int numberOfDoors;

public Car(String brand, String model, int year, int numberOfDoors) {
super(brand, model, year);
this.numberOfDoors = numberOfDoors;
}

@Override
public void start() {
System.out.println("Vặn chìa khóa xe");
System.out.println("Đạp phanh");
System.out.println("Khởi động động cơ");
}

@Override
public void displayInfo() {
System.out.printf("Xe hơi: %s %s %d, Số cửa: %d%n",
brand, model, year, numberOfDoors);
}
}

4. Best Practices ✅

  1. Sử Dụng @Override Annotation

    // Không nên
    public class Child extends Parent {
    public void display() { // Không có @Override
    System.out.println("Child class");
    }
    }

    // Nên
    public class Child extends Parent {
    @Override
    public void display() {
    System.out.println("Child class");
    }
    }
  2. Gọi Phương Thức Lớp Cha Khi Cần

    // Không nên
    public class Child extends Parent {
    @Override
    public void process() {
    // Code mới hoàn toàn
    }
    }

    // Nên
    public class Child extends Parent {
    @Override
    public void process() {
    super.process(); // Gọi phương thức lớp cha
    // Thêm code mới
    }
    }
  3. Giữ Tính Nhất Quán

    // Không nên
    public class Parent {
    public double calculate() {
    return 0.0;
    }
    }

    public class Child extends Parent {
    @Override
    public double calculate() {
    return -1.0; // Không nhất quán với mục đích của phương thức
    }
    }

    // Nên
    public class Child extends Parent {
    @Override
    public double calculate() {
    return 10.0; // Giá trị hợp lý
    }
    }

5. Lỗi Thường Gặp ⚠️

  1. Ghi Đè Phương Thức Private

    // Lỗi
    public class Parent {
    private void display() { }
    }

    public class Child extends Parent {
    @Override
    private void display() { } // Lỗi: không thể ghi đè phương thức private
    }

    // Đúng
    public class Parent {
    protected void display() { }
    }

    public class Child extends Parent {
    @Override
    protected void display() { }
    }
  2. Ghi Đè Phương Thức Final

    // Lỗi
    public class Parent {
    public final void display() { }
    }

    public class Child extends Parent {
    @Override
    public void display() { } // Lỗi: không thể ghi đè phương thức final
    }
  3. Ghi Đè Với Kiểu Trả Về Không Tương Thích

    // Lỗi
    public class Parent {
    public Number getValue() {
    return 1;
    }
    }

    public class Child extends Parent {
    @Override
    public String getValue() { // Lỗi: kiểu trả về không tương thích
    return "1";
    }
    }

    // Đúng
    public class Child extends Parent {
    @Override
    public Integer getValue() {
    return 1;
    }
    }

💡 Lời khuyên: Hãy thực hành với các ví dụ thực tế để hiểu rõ hơn về cách sử dụng ghi đè phương thức trong các tình huống khác nhau.

Tiếp Theo 🎯

Trong các bài học tiếp theo, chúng ta sẽ:

  • Tìm hiểu về constructor
  • Học cách sử dụng đệ quy (Recursion)
  • Thực hành với tính đa hình (Polymorphism)
  • Tìm hiểu về interface và abstract class