Tính Kế Thừa trong Java 🎯
🎯 Mục tiêu: Học cách sử dụng tính kế thừa để tái sử dụng code và tạo ra các lớp con từ lớp cha trong Java.
Giới thiệu 📝
Tính kế thừa (Inheritance) là một trong những nguyên tắc cơ bản của lập trình hướng đối tượng. Nó cho phép một lớp (lớp con) kế thừa các thuộc tính và phương thức từ một lớp khác (lớp cha), giúp tái sử dụng code và tạo ra các mối quan hệ phân cấp giữa các lớp.
💡 Fun Fact: Trong Java, mọi lớp đều kế thừa từ lớp
Object
, đây là lớp gốc của tất cả các lớp trong Java.
1. Cú Pháp Cơ Bản ⚡
Kế Thừa Đơn Giản
public class Animal {
protected String name;
protected int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void makeSound() {
System.out.println("Tiếng kêu của động vật");
}
}
public class Dog extends Animal {
private String breed;
public Dog(String name, int age, String breed) {
super(name, age);
this.breed = breed;
}
@Override
public void makeSound() {
System.out.println("Gâu gâu!");
}
}
Kế Thừa Nhiều Cấp
public class Vehicle {
protected String brand;
protected String model;
public Vehicle(String brand, String model) {
this.brand = brand;
this.model = model;
}
}
public class Car extends Vehicle {
private int numberOfDoors;
public Car(String brand, String model, int numberOfDoors) {
super(brand, model);
this.numberOfDoors = numberOfDoors;
}
}
public class ElectricCar extends Car {
private double batteryCapacity;
public ElectricCar(String brand, String model, int numberOfDoors, double batteryCapacity) {
super(brand, model, numberOfDoors);
this.batteryCapacity = batteryCapacity;
}
}
2. Các Loại Kế Thừa 🔄
Kế Thừa Đơn
public class Person {
protected String name;
protected int age;
}
public class Student extends Person {
private String studentId;
}
Kế Thừa Nhiều Cấp
public class Shape {
protected String color;
}
public class Rectangle extends Shape {
protected double length;
protected double width;
}
public class Square extends Rectangle {
public Square(String color, double side) {
super(color, side, side);
}
}
3. Ví Dụ Thực Tế 🚀
Quản Lý Nhân Viên
public class Employee {
protected String id;
protected String name;
protected double baseSalary;
protected String department;
public Employee(String id, String name, double baseSalary, String department) {
this.id = id;
this.name = name;
this.baseSalary = baseSalary;
this.department = department;
}
public double calculateSalary() {
return baseSalary;
}
public void displayInfo() {
System.out.printf("Nhân viên: %s, ID: %s, Phòng ban: %s, Lương: %.2f%n",
name, id, department, calculateSalary());
}
}
public class Manager extends Employee {
private double bonus;
private List<Employee> subordinates;
public Manager(String id, String name, double baseSalary, String department, double bonus) {
super(id, name, baseSalary, department);
this.bonus = bonus;
this.subordinates = new ArrayList<>();
}
@Override
public double calculateSalary() {
return baseSalary + bonus;
}
public void addSubordinate(Employee employee) {
subordinates.add(employee);
}
public void removeSubordinate(Employee employee) {
subordinates.remove(employee);
}
@Override
public void displayInfo() {
System.out.printf("Quản lý: %s, ID: %s, Phòng ban: %s, Lương: %.2f, Thưởng: %.2f%n",
name, id, department, baseSalary, bonus);
System.out.println("Nhân viên dưới quyền:");
for (Employee emp : subordinates) {
System.out.printf("- %s%n", emp.name);
}
}
}
Quản Lý Hình Học
public abstract class Shape {
protected String color;
protected boolean filled;
public Shape(String color, boolean filled) {
this.color = color;
this.filled = filled;
}
public abstract double getArea();
public abstract double getPerimeter();
public void displayInfo() {
System.out.printf("Hình: %s, Màu: %s, Đã tô: %b, Diện tích: %.2f, Chu vi: %.2f%n",
getClass().getSimpleName(), color, filled, getArea(), getPerimeter());
}
}
public class Circle extends Shape {
private double radius;
public Circle(String color, boolean filled, double radius) {
super(color, filled);
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}
public class Rectangle extends Shape {
private double length;
private double width;
public Rectangle(String color, boolean filled, double length, double width) {
super(color, filled);
this.length = length;
this.width = width;
}
@Override
public double getArea() {
return length * width;
}
@Override
public double getPerimeter() {
return 2 * (length + width);
}
}
Quản Lý Phương Tiện
public class Vehicle {
protected String brand;
protected String model;
protected int year;
protected String licensePlate;
public Vehicle(String brand, String model, int year, String licensePlate) {
this.brand = brand;
this.model = model;
this.year = year;
this.licensePlate = licensePlate;
}
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, Biển số: %s%n",
brand, model, year, licensePlate);
}
}
public class Car extends Vehicle {
private int numberOfDoors;
private String transmissionType;
public Car(String brand, String model, int year, String licensePlate,
int numberOfDoors, String transmissionType) {
super(brand, model, year, licensePlate);
this.numberOfDoors = numberOfDoors;
this.transmissionType = transmissionType;
}
@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() {
super.displayInfo();
System.out.printf("Số cửa: %d, Hộp số: %s%n", numberOfDoors, transmissionType);
}
}
public class Motorcycle extends Vehicle {
private boolean hasSidecar;
private String engineType;
public Motorcycle(String brand, String model, int year, String licensePlate,
boolean hasSidecar, String engineType) {
super(brand, model, year, licensePlate);
this.hasSidecar = hasSidecar;
this.engineType = engineType;
}
@Override
public void start() {
System.out.println("Đạp cần khởi động");
System.out.println("Bật công tắc điện");
System.out.println("Khởi động động cơ");
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.printf("Có bánh phụ: %b, Loại động cơ: %s%n", hasSidecar, engineType);
}
}
4. Best Practices ✅
-
Sử Dụng Protected Cho Thuộc Tính Kế Thừa
// Không nên
public class Parent {
private String name; // Private không thể truy cập từ lớp con
}
// Nên
public class Parent {
protected String name; // Protected có thể truy cập từ lớp con
} -
Gọi Constructor Lớp Cha
// Không nên
public class Child extends Parent {
public Child() {
// Quên gọi super()
}
}
// Nên
public class Child extends Parent {
public Child() {
super();
}
} -
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");
}
}
5. Lỗi Thường Gặp ⚠️
-
Quên Gọi super()
// Lỗi
public class Child extends Parent {
public Child() {
// Quên gọi super()
}
}
// Đúng
public class Child extends Parent {
public Child() {
super();
}
} -
Gọi super() Sai Vị Trí
// Lỗi
public class Child extends Parent {
public Child() {
doSomething(); // Code trước super()
super();
}
}
// Đúng
public class Child extends Parent {
public Child() {
super();
doSomething(); // Code sau super()
}
} -
Kế Thừa Từ Lớp Final
// Lỗi
public final class Parent { }
public class Child extends Parent { } // Lỗi: không thể kế thừa từ lớp final
// Đúng
public class Parent { }
public class Child extends Parent { }
💡 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 tính kế thừa 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ề tính đa hình (Polymorphism)
- Học cách sử dụng interface và abstract class
- Thực hành với package và import
- Tìm hiểu về xử lý ngoại lệ (Exception Handling)