Giai đoạn 2: Lập trình Hướng đối tượng trong Java

Chào mừng đến với Giai đoạn 2
Chào mừng đến với giai đoạn Lập trình Hướng đối tượng! Nếu bạn đã hoàn thành Giai đoạn 1, bạn đã hiểu cú pháp cơ bản của Java. Bây giờ chúng ta sẽ đi sâu vào các nguyên lý OOP và xem Java triển khai chúng với độ chính xác và sức mạnh như thế nào.
Nếu bạn đến từ các ngôn ngữ như Python hoặc JavaScript, bạn sẽ thấy cách tiếp cận OOP của Java rõ ràng và có cấu trúc hơn. Đây không phải là sự dư thừa—mà là sự rõ ràng giúp mở rộng cho các codebase lớn.
Thời gian cam kết: 2 tuần, 1-2 giờ mỗi ngày
Yêu cầu: Hoàn thành Giai đoạn 1 hoặc nắm vững kiến thức cơ bản Java tương đương
Những gì bạn sẽ học
Khi kết thúc Giai đoạn 2, bạn sẽ có thể:
✅ Thiết kế class với tính đóng gói đúng cách
✅ Sử dụng kế thừa hiệu quả với ghi đè phương thức
✅ Triển khai đa hình cho code linh hoạt
✅ Lựa chọn giữa abstract class và interface
✅ Làm việc với inner class và anonymous class
✅ Ghi đè các phương thức của Object class đúng cách
✅ Áp dụng các best practice OOP và nguyên tắc thiết kế
Class và Object: Phân tích chi tiết
Cấu trúc của một Class
Một Java class được thiết kế tốt sẽ đóng gói dữ liệu và hành vi:
public class BankAccount {
// Fields (biến instance)
private String accountNumber;
private double balance;
private String ownerName;
// Constructor
public BankAccount(String accountNumber, String ownerName) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = 0.0;
}
// Methods
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return true;
}
return false;
}
// Getter (accessor)
public double getBalance() {
return balance;
}
}Các nguyên tắc chính:
- Field là private (ẩn dữ liệu)
- Constructor khởi tạo trạng thái
- Method cung cấp truy cập có kiểm soát
- Logic nghiệp vụ được đóng gói
Nhiều Constructor (Overloading)
Java cho phép nhiều constructor với các tham số khác nhau:
public class Employee {
private String name;
private int id;
private double salary;
// Constructor mặc định
public Employee() {
this("Unknown", 0, 0.0);
}
// Constructor với name và id
public Employee(String name, int id) {
this(name, id, 50000.0); // Lương mặc định
}
// Constructor đầy đủ
public Employee(String name, int id, double salary) {
this.name = name;
this.id = id;
this.salary = salary;
}
}
// Cách sử dụng
var emp1 = new Employee();
var emp2 = new Employee("Alice", 101);
var emp3 = new Employee("Bob", 102, 75000.0);Chuỗi constructor với this():
- Phải là câu lệnh đầu tiên trong constructor
- Giảm trùng lặp code
- Thiết lập constructor "chính"
Block Khởi tạo
Java cung cấp các block khởi tạo cho thiết lập phức tạp:
public class Configuration {
private Map<String, String> settings;
// Block khởi tạo instance (chạy trước constructor)
{
settings = new HashMap<>();
settings.put("timeout", "30");
settings.put("retries", "3");
System.out.println("Instance block executed");
}
// Block khởi tạo static (chạy một lần khi class được load)
static {
System.out.println("Configuration class loaded");
// Khởi tạo tài nguyên static
}
public Configuration() {
System.out.println("Constructor executed");
}
}Thứ tự thực thi:
- Block khởi tạo static (một lần, khi class được load)
- Block khởi tạo instance (trước mỗi constructor)
- Thân constructor
Tính đóng gói và Access Modifier
Tính đóng gói là về truy cập có kiểm soát vào dữ liệu. Java có bốn cấp độ truy cập:
Ma trận Access Modifier
public class AccessDemo {
public String publicField; // Truy cập ở mọi nơi
protected String protectedField; // Truy cập trong package + subclass
String packageField; // Package-private (mặc định)
private String privateField; // Chỉ trong class này
public void publicMethod() { }
protected void protectedMethod() { }
void packageMethod() { }
private void privateMethod() { }
}| Modifier | Cùng Class | Cùng Package | Subclass | Mọi nơi |
|---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
| default | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
Best Practice: Mẫu Đóng gói
public class User {
// Field private
private String username;
private String email;
private LocalDateTime createdAt;
// Constructor
public User(String username, String email) {
this.username = username;
this.email = email;
this.createdAt = LocalDateTime.now();
}
// Getter public
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
// Không có setter cho createdAt (bất biến sau khi tạo)
public LocalDateTime getCreatedAt() {
return createdAt;
}
// Setter có kiểm soát với validation
public void setEmail(String email) {
if (email != null && email.contains("@")) {
this.email = email;
} else {
throw new IllegalArgumentException("Invalid email");
}
}
}Tại sao tính đóng gói quan trọng:
- Kiểm soát cách dữ liệu được truy cập và sửa đổi
- Thêm logic validation
- Thay đổi cài đặt bên trong mà không làm hỏng code bên ngoài
- Ẩn độ phức tạp
Kế thừa
Kế thừa cho phép các class kế thừa field và method từ class cha.
Kế thừa Cơ bản
// Class cha
public class Vehicle {
protected String brand;
protected int year;
public Vehicle(String brand, int year) {
this.brand = brand;
this.year = year;
}
public void startEngine() {
System.out.println(brand + " engine started");
}
public void displayInfo() {
System.out.println(year + " " + brand);
}
}
// Class con
public class Car extends Vehicle {
private int numberOfDoors;
public Car(String brand, int year, int numberOfDoors) {
super(brand, year); // Gọi constructor cha
this.numberOfDoors = numberOfDoors;
}
public void honk() {
System.out.println("Beep beep!");
}
}
// Cách sử dụng
Car myCar = new Car("Toyota", 2024, 4);
myCar.startEngine(); // Method kế thừa
myCar.honk(); // Method riêngGhi đè Method
Class con có thể ghi đè method của class cha:
public class ElectricCar extends Vehicle {
private int batteryCapacity;
public ElectricCar(String brand, int year, int batteryCapacity) {
super(brand, year);
this.batteryCapacity = batteryCapacity;
}
// Ghi đè method cha
@Override
public void startEngine() {
System.out.println(brand + " electric motor activated silently");
}
// Ghi đè và mở rộng
@Override
public void displayInfo() {
super.displayInfo(); // Gọi phiên bản cha
System.out.println("Battery: " + batteryCapacity + " kWh");
}
}Annotation @Override:
- An toàn khi compile (lỗi nếu method không thực sự ghi đè)
- Ghi nhận ý định
- Giúp các công cụ refactor
- Luôn sử dụng nó!
Từ khóa super
public class SportsCar extends Car {
private int topSpeed;
public SportsCar(String brand, int year, int doors, int topSpeed) {
super(brand, year, doors); // Gọi constructor cha
this.topSpeed = topSpeed;
}
@Override
public void displayInfo() {
super.displayInfo(); // Gọi method cha
System.out.println("Top speed: " + topSpeed + " mph");
}
}Quy tắc cho super():
- Phải là câu lệnh đầu tiên trong constructor
- Dùng để gọi constructor cha
- Nếu không gọi tường minh, Java tự động gọi
super()
Đa hình
Đa hình có nghĩa là "nhiều hình thức"—khả năng xử lý các object có kiểu khác nhau một cách thống nhất.
Đa hình Compile-Time (Method Overloading)
Cùng tên method, tham số khác nhau:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
public String add(String a, String b) {
return a + b;
}
}
// Cách sử dụng
Calculator calc = new Calculator();
calc.add(5, 10); // Gọi phiên bản int
calc.add(5.5, 10.5); // Gọi phiên bản double
calc.add(1, 2, 3); // Gọi phiên bản ba tham số
calc.add("Hello", "World"); // Gọi phiên bản StringĐa hình Runtime (Method Overriding)
Tham chiếu cha có thể chứa object con:
public class Animal {
public void makeSound() {
System.out.println("Some generic animal sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof! Woof!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
// Đa hình trong hành động
Animal myDog = new Dog(); // Tham chiếu cha, object con
Animal myCat = new Cat();
myDog.makeSound(); // "Woof! Woof!" (phiên bản Dog)
myCat.makeSound(); // "Meow!" (phiên bản Cat)
// Mảng các động vật khác nhau
Animal[] animals = {new Dog(), new Cat(), new Dog()};
for (Animal animal : animals) {
animal.makeSound(); // Gọi method thích hợp tại runtime
}Điểm quan trọng: Method được gọi phụ thuộc vào kiểu object thực tế, không phải kiểu tham chiếu.
Ví dụ Đa hình Thực tế
public interface PaymentProcessor {
boolean processPayment(double amount);
}
public class CreditCardProcessor implements PaymentProcessor {
@Override
public boolean processPayment(double amount) {
System.out.println("Processing credit card payment: $" + amount);
// Logic thẻ tín dụng
return true;
}
}
public class PayPalProcessor implements PaymentProcessor {
@Override
public boolean processPayment(double amount) {
System.out.println("Processing PayPal payment: $" + amount);
// Logic PayPal
return true;
}
}
public class CheckoutService {
public void checkout(PaymentProcessor processor, double amount) {
if (processor.processPayment(amount)) {
System.out.println("Payment successful!");
}
}
}
// Cách sử dụng
CheckoutService checkout = new CheckoutService();
checkout.checkout(new CreditCardProcessor(), 100.0);
checkout.checkout(new PayPalProcessor(), 50.0);Thiết kế này cho phép thêm phương thức thanh toán mới mà không thay đổi CheckoutService.
Abstract Class
Abstract class cung cấp cài đặt một phần và đóng vai trò là class cơ sở:
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// Method trừu tượng (không có cài đặt)
public abstract double calculateArea();
// Method cụ thể (có cài đặt)
public void displayColor() {
System.out.println("Color: " + color);
}
// Mẫu template method
public final void display() {
displayColor();
System.out.println("Area: " + calculateArea());
}
}
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
}
// Cách sử dụng
Shape circle = new Circle("Red", 5.0);
Shape rectangle = new Rectangle("Blue", 4.0, 6.0);
circle.display(); // "Color: Red\nArea: 78.54"
rectangle.display(); // "Color: Blue\nArea: 24.0"Quy tắc abstract class:
- Không thể khởi tạo trực tiếp
- Có thể có cả method trừu tượng và cụ thể
- Có thể có constructor, field, và static method
- Subclass phải triển khai tất cả method trừu tượng
Interface
Interface định nghĩa các hợp đồng mà class phải thực hiện:
public interface Drawable {
void draw(); // Ngầm định là public và abstract
default void show() { // Method mặc định (Java 8+)
System.out.println("Showing drawable");
draw();
}
}
public interface Resizable {
void resize(double factor);
}
// Class triển khai nhiều interface
public class Image implements Drawable, Resizable {
private String filename;
private int width;
private int height;
public Image(String filename, int width, int height) {
this.filename = filename;
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing " + filename + " at " + width + "x" + height);
}
@Override
public void resize(double factor) {
width = (int) (width * factor);
height = (int) (height * factor);
}
}Tính năng Interface (Java hiện đại)
Default method (Java 8+):
public interface Logger {
void log(String message);
default void logError(String message) {
log("ERROR: " + message);
}
default void logWarning(String message) {
log("WARNING: " + message);
}
}Static method (Java 8+):
public interface MathUtils {
static int add(int a, int b) {
return a + b;
}
static double calculateCircleArea(double radius) {
return Math.PI * radius * radius;
}
}
// Cách sử dụng
int sum = MathUtils.add(5, 10);Private method (Java 9+):
public interface DataProcessor {
default void processData(String data) {
validateData(data); // Gọi private method
// Xử lý...
}
private void validateData(String data) {
if (data == null || data.isEmpty()) {
throw new IllegalArgumentException("Invalid data");
}
}
}Abstract Class vs Interface
Khi nào dùng abstract class:
- Chia sẻ code giữa các class có quan hệ gần nhau
- Cần field non-static hoặc non-final
- Cần member non-public
- Muốn khai báo constructor
Khi nào dùng interface:
- Định nghĩa hợp đồng cho các class không liên quan
- Hỗ trợ đa kế thừa hành vi
- Mong đợi API ổn định (có thể thêm default method sau)
- Muốn đạt được liên kết lỏng lẻo
Ví dụ so sánh:
// Abstract class: quan hệ "is-a"
public abstract class Employee {
protected String name;
protected double salary;
public abstract double calculateBonus();
public void giveRaise(double percentage) {
salary *= (1 + percentage / 100);
}
}
// Interface: khả năng "can-do"
public interface Trainable {
void attendTraining(String course);
boolean hasCompleted(String course);
}
// Cài đặt
public class Developer extends Employee implements Trainable {
private List<String> completedCourses = new ArrayList<>();
@Override
public double calculateBonus() {
return salary * 0.15;
}
@Override
public void attendTraining(String course) {
completedCourses.add(course);
}
@Override
public boolean hasCompleted(String course) {
return completedCourses.contains(course);
}
}Mẹo: Một class có thể kế thừa một abstract class nhưng triển khai nhiều interface.
Từ khóa this
this tham chiếu đến instance object hiện tại:
public class User {
private String name;
private int age;
// 1. Phân biệt field với parameter
public User(String name, int age) {
this.name = name; // this.name là field, name là parameter
this.age = age;
}
// 2. Gọi constructor khác
public User(String name) {
this(name, 0); // Gọi User(String, int)
}
// 3. Trả về instance hiện tại (fluent API)
public User setName(String name) {
this.name = name;
return this; // Trả về object hiện tại
}
public User setAge(int age) {
this.age = age;
return this;
}
// 4. Truyền instance hiện tại cho method
public void registerUser(UserService service) {
service.register(this); // Truyền object User hiện tại
}
}
// Cách dùng fluent API
User user = new User("Alice")
.setName("Alice Johnson")
.setAge(30);Inner Class và Nested Class
Member Inner Class
public class OuterClass {
private String outerField = "Outer";
public class InnerClass {
public void display() {
// Có thể truy cập member của outer class
System.out.println(outerField);
}
}
}
// Cách sử dụng
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();Static Nested Class
public class OuterClass {
private static String staticField = "Static";
private String instanceField = "Instance";
public static class StaticNestedClass {
public void display() {
System.out.println(staticField); // OK
// System.out.println(instanceField); // LỖI: không thể truy cập instance member
}
}
}
// Cách sử dụng (không cần outer instance)
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();
nested.display();Sử dụng static nested class khi nested class không cần truy cập member instance của outer class.
Local Inner Class
public class OuterClass {
public void method() {
final String localVar = "Local";
// Local class bên trong method
class LocalInnerClass {
public void display() {
System.out.println(localVar); // Có thể truy cập biến local final
}
}
LocalInnerClass local = new LocalInnerClass();
local.display();
}
}Anonymous Inner Class
Hữu ích cho cài đặt một lần:
public interface Greeting {
void greet(String name);
}
public class Demo {
public void demonstrate() {
// Anonymous class triển khai interface
Greeting greeting = new Greeting() {
@Override
public void greet(String name) {
System.out.println("Hello, " + name + "!");
}
};
greeting.greet("Alice");
}
}Thay thế hiện đại: Lambda expression (được đề cập trong Giai đoạn 4 với Lập trình Hàm):
Greeting greeting = name -> System.out.println("Hello, " + name + "!");Enum
Enum định nghĩa một tập hợp cố định các hằng số:
Enum Cơ bản
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
// Cách sử dụng
Day today = Day.MONDAY;
if (today == Day.SATURDAY || today == Day.SUNDAY) {
System.out.println("Weekend!");
}
// Switch với enum
switch (today) {
case MONDAY -> System.out.println("Start of work week");
case FRIDAY -> System.out.println("TGIF!");
case SATURDAY, SUNDAY -> System.out.println("Weekend!");
default -> System.out.println("Midweek");
}Enum với Field và Method
public enum OrderStatus {
PENDING("Order received", 1),
PROCESSING("Being processed", 2),
SHIPPED("Out for delivery", 3),
DELIVERED("Delivered successfully", 4),
CANCELLED("Order cancelled", -1);
private final String description;
private final int priority;
// Constructor (luôn là private)
OrderStatus(String description, int priority) {
this.description = description;
this.priority = priority;
}
public String getDescription() {
return description;
}
public int getPriority() {
return priority;
}
public boolean isActive() {
return priority > 0;
}
}
// Cách sử dụng
OrderStatus status = OrderStatus.SHIPPED;
System.out.println(status.getDescription()); // "Out for delivery"
System.out.println(status.isActive()); // trueTiện ích Enum
// Lấy tất cả giá trị enum
for (Day day : Day.values()) {
System.out.println(day);
}
// Chuyển string sang enum
Day day = Day.valueOf("MONDAY");
// Lấy tên enum
String name = Day.MONDAY.name(); // "MONDAY"
// Lấy vị trí enum
int ordinal = Day.MONDAY.ordinal(); // 0Các Method của Object Class
Mọi class trong Java đều kế thừa từ Object. Ba method quan trọng cần ghi đè:
equals()
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
// 1. Kiểm tra nếu là cùng object
if (this == obj) return true;
// 2. Kiểm tra nếu null hoặc class khác
if (obj == null || getClass() != obj.getClass()) return false;
// 3. Cast và so sánh field
Point other = (Point) obj;
return x == other.x && y == other.y;
}
}Với Java 17+, sử dụng tiện ích Objects:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Point other = (Point) obj;
return Objects.equals(x, other.x) && Objects.equals(y, other.y);
}hashCode()
Quy tắc: Nếu hai object bằng nhau (qua equals()), chúng phải có cùng hash code.
public class Point {
private final int x;
private final int y;
@Override
public boolean equals(Object obj) {
// ... (như trên)
}
@Override
public int hashCode() {
return Objects.hash(x, y); // Tiện ích Java 7+
}
}Tại sao nó quan trọng: Các collection như HashMap và HashSet sử dụng hash code để tăng hiệu năng.
toString()
public class Point {
private final int x;
private final int y;
@Override
public String toString() {
return "Point{x=" + x + ", y=" + y + "}";
}
}
// Cách sử dụng
Point p = new Point(3, 4);
System.out.println(p); // "Point{x=3, y=4}" (thay vì "Point@1a2b3c")Mẹo: Hầu hết các IDE có thể tự động tạo equals(), hashCode(), và toString() cho bạn.
Record Class (Java 16+)
Với các data carrier đơn giản, sử dụng record:
public record Point(int x, int y) {}
// Tự động cung cấp:
// - Constructor: new Point(3, 4)
// - Getter: point.x(), point.y()
// - equals(), hashCode(), toString()
// - Field bất biếnRecord hoàn hảo cho DTO, value object, và data transfer. Xem phân tích sâu về Java Generics để biết thêm về record với generic.
Best Practice
1. Ưu tiên Composition hơn Inheritance
// ❌ Inheritance: liên kết chặt chẽ
public class Car extends Engine {
// Car "is-an" Engine? Không hợp lý
}
// ✅ Composition: linh hoạt
public class Car {
private Engine engine; // Car "has-an" Engine
public Car(Engine engine) {
this.engine = engine;
}
public void start() {
engine.start();
}
}Tại sao: Composition linh hoạt hơn và dễ test hơn.
2. Lập trình với Interface, không phải Implementation
// ❌ Phụ thuộc vào concrete class
public class OrderProcessor {
private ArrayList<Order> orders; // Gắn với ArrayList
}
// ✅ Phụ thuộc vào interface
public class OrderProcessor {
private List<Order> orders; // Có thể là ArrayList, LinkedList, etc.
public OrderProcessor(List<Order> orders) {
this.orders = orders;
}
}3. Giữ Class Tập trung (Single Responsibility)
// ❌ Quá nhiều trách nhiệm
public class User {
public void saveToDatabase() { }
public void sendEmail() { }
public void generateReport() { }
public void validateInput() { }
}
// ✅ Một trách nhiệm cho mỗi class
public class User { /* Chỉ dữ liệu user */ }
public class UserRepository { /* Thao tác database */ }
public class EmailService { /* Thao tác email */ }
public class ReportGenerator { /* Thao tác report */ }
public class UserValidator { /* Logic validation */ }4. Sử dụng Enum thay vì Hằng số
// ❌ Số và chuỗi ma thuật
public static final int STATUS_PENDING = 1;
public static final int STATUS_APPROVED = 2;
public static final int STATUS_REJECTED = 3;
// ✅ Enum type-safe
public enum Status {
PENDING, APPROVED, REJECTED
}5. Ghi đè equals() và hashCode() cùng nhau
// ❌ Chỉ ghi đè equals()
public class User {
@Override
public boolean equals(Object obj) { /* ... */ }
// Thiếu hashCode()! HashMap/HashSet sẽ bị lỗi
}
// ✅ Ghi đè cả hai
public class User {
@Override
public boolean equals(Object obj) { /* ... */ }
@Override
public int hashCode() { /* ... */ }
}6. Làm Class Bất biến khi có thể
// ✅ Immutable class
public final class ImmutableUser {
private final String name;
private final String email;
public ImmutableUser(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public String getEmail() {
return email;
}
// Không có setter!
}Lợi ích: Thread-safe, dễ suy luận, có thể cache.
Các Lỗi Thường gặp
❌ Phá vỡ Tính đóng gói với Public Field
// Tệ
public class User {
public String name; // Truy cập trực tiếp
public int age;
}
user.age = -5; // Không có validation!
// Tốt
public class User {
private String name;
private int age;
public void setAge(int age) {
if (age >= 0) {
this.age = age;
}
}
}❌ Không sử dụng @Override
public class Child extends Parent {
// Lỗi chính tả trong tên method - sẽ không ghi đè!
public void disply() { // Nên là display()
// ...
}
}
// ✅ Sử dụng @Override - compiler sẽ bắt lỗi
@Override
public void display() {
// ...
}❌ So sánh Object với ==
String s1 = new String("hello");
String s2 = new String("hello");
s1 == s2; // false (object khác nhau)
s1.equals(s2); // true (nội dung giống nhau)
// Luôn sử dụng .equals() cho object!❌ Trả về Mutable Collection
public class Team {
private List<Player> players = new ArrayList<>();
// ❌ Lộ trạng thái nội bộ
public List<Player> getPlayers() {
return players; // Caller có thể sửa đổi!
}
// ✅ Trả về view không thể sửa đổi
public List<Player> getPlayers() {
return Collections.unmodifiableList(players);
}
// ✅ Hoặc trả về bản sao
public List<Player> getPlayers() {
return new ArrayList<>(players);
}
}❌ Sử dụng Inheritance chỉ để tái sử dụng Code
// ❌ Tệ: Car không phải là Vehicle mà cũng là GPS
public class Car extends Vehicle, GPS { // Không thể làm thế này!
// ...
}
// ✅ Tốt: Sử dụng composition
public class Car extends Vehicle {
private GPS gps; // Quan hệ has-a
public Car(GPS gps) {
this.gps = gps;
}
}Bài tập Thực hành
Củng cố kỹ năng OOP của bạn với các bài tập này:
- Hệ thống Tài khoản Ngân hàng: Tạo class
Account,SavingsAccount,CheckingAccountvới kế thừa và đóng gói phù hợp - Máy tính Hình học: Xây dựng abstract class
Shapevới cài đặtCircle,Rectangle,Triangle - Quản lý Nhân viên: Thiết kế abstract class
Employee, triển khaiFullTimeEmployeevàContractorvới tính lương đa hình - Trò chơi Bài: Tạo class
CardvàDeck, sử dụng enum cho chất và rank - Hệ thống Thư viện: Thiết kế class
Book,Member, vàLibraryvới quan hệ và đóng gói phù hợp
Tóm tắt
Bây giờ bạn đã làm chủ Lập trình Hướng đối tượng trong Java:
✅ Thiết kế class với tính đóng gói phù hợp
✅ Sử dụng kế thừa và ghi đè method hiệu quả
✅ Triển khai đa hình cho code linh hoạt
✅ Lựa chọn giữa abstract class và interface
✅ Làm việc với inner, nested, và anonymous class
✅ Sử dụng enum cho hằng số type-safe
✅ Ghi đè các method Object đúng cách
✅ Áp dụng các best practice OOP và nguyên tắc thiết kế
Các Bước Tiếp theo
Bạn đã sẵn sàng chuyển sang Giai đoạn 3: Xử lý Ngoại lệ & I/O, nơi bạn sẽ học:
- Cơ chế xử lý exception
- Block try-catch-finally
- Exception tùy chỉnh
- Thao tác File I/O
- Serialization
- Try-with-resources
🚀 Tiếp tục Giai đoạn 3: Core Java APIs →
Trước: Giai đoạn 1: Kiến thức Cơ bản Java ← Tiếp: Giai đoạn 3: Core Java APIs → Liên quan: Java Generics Giải thích →
Chúc bạn code vui vẻ! ☕
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.