---
# System prepended metadata

title: Java Review All

---

## 1. Java OOP 
#### Khái niệm: package trong Java dùng để tổ chức các lớp (classes) thành các nhóm hợp lý, giúp quản lý mã nguồn, tránh xung đột tên và kiểm soát quyền truy cập.

#### Quy ước đặt tên: Tên package thường là chữ thường, theo thứ tự ngược của tên miền (ví dụ: com.yourcompany.project).
#### Import: Khi sử dụng một lớp từ package khác, bạn cần import nó.
#### Truy cập: Các access modifier (public, protected, default, private) sẽ ảnh hưởng đến khả năng truy cập giữa các package.

#### Kế thừa 
Cho phép một lớp (lớp con/derived class) kế thừa các thuộc tính và phương thức từ một lớp khác (lớp cha/base class), giúp tái sử dụng mã và tạo ra một hệ thống phân cấp.

```java
class Animal { 
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

class Dog extends Animal { // Lớp con kế thừa từ Animal
    public Dog(String name) {
        super(name); // Gọi constructor của lớp cha
    }

    public void bark() {
        System.out.println(name + " is barking.");
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        Dog myDog = new Dog("Buddy");
        myDog.eat();  // Phương thức kế thừa từ Animal
        myDog.bark(); // Phương thức riêng của Dog
    }
}
```

* Java chỉ hỗ trợ đơn kế thừa (một lớp con chỉ kế thừa từ một lớp cha trực tiếp).
* Sử dụng từ khóa extends để kế thừa.
* super() được dùng để gọi constructor của lớp cha.
* Các phương thức private và constructor của lớp cha không được kế thừa trực tiếp

#### Đóng gói 
Là việc che giấu thông tin bên trong của một đối tượng và chỉ cho phép truy cập thông qua các phương thức công khai (public methods), bảo vệ dữ liệu và đảm bảo tính toàn vẹn.

```java
class Account {
    private String accountNumber; // Dữ liệu private
    private double balance;       // Dữ liệu private

    public Account(String accountNumber, double initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    // Getter cho accountNumber
    public String getAccountNumber() {
        return accountNumber;
    }

    // Getter cho balance
    public double getBalance() {
        return balance;
    }

    // Setter an toàn cho balance
    public void deposit(double amount) {
        if (amount > 0) {
            this.balance += amount;
            System.out.println("Deposited: " + amount + ". New balance: " + balance);
        } else {
            System.out.println("Deposit amount must be positive.");
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && this.balance >= amount) {
            this.balance -= amount;
            System.out.println("Withdrew: " + amount + ". New balance: " + balance);
        } else {
            System.out.println("Invalid withdrawal amount or insufficient funds.");
        }
    }
}

public class EncapsulationDemo {
    public static void main(String[] args) {
        Account myAccount = new Account("123456", 1000.0);
        System.out.println("Account Number: " + myAccount.getAccountNumber());
        System.out.println("Current Balance: " + myAccount.getBalance());

        myAccount.deposit(200);
        myAccount.withdraw(500);
        myAccount.withdraw(1000); // Sẽ báo lỗi
    }
}
```

* Sử dụng private cho các thuộc tính để hạn chế truy cập trực tiếp.
* Cung cấp các phương thức public (getters và setters) để truy cập và sửa đổi dữ liệu một cách có kiểm soát.
* Tập trung vào "hành vi" hơn là "dữ liệu"

#### Đa hình 
Trong OOP, đa hình cho phép các đối tượng thuộc các lớp khác nhau (nhưng cùng có một lớp cha hoặc cùng triển khai một interface) được xử lý thông qua một giao diện chung. Có hai loại chính:
* Compile-time Polymorphism (Overloading): Nhiều phương thức cùng tên nhưng khác tham số.
* Runtime Polymorphism (Overriding): Lớp con định nghĩa lại phương thức của lớp cha

##### Overriding
```javascript!
class Shape {
    public void draw() {
        System.out.println("Drawing a shape.");
    }
}

class Circle extends Shape {
    @Override // Ghi đè phương thức draw() của lớp cha
    public void draw() {
        System.out.println("Drawing a circle.");
    }
}

class Rectangle extends Shape {
    @Override // Ghi đè phương thức draw() của lớp cha
    public void draw() {
        System.out.println("Drawing a rectangle.");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        Shape s1 = new Circle();    // Đối tượng Circle được tham chiếu bởi kiểu Shape
        Shape s2 = new Rectangle(); // Đối tượng Rectangle được tham chiếu bởi kiểu Shape
        Shape s3 = new Shape();     // Đối tượng Shape

        s1.draw(); // Gọi phương thức draw() của Circle
        s2.draw(); // Gọi phương thức draw() của Rectangle
        s3.draw(); // Gọi phương thức draw() của Shape
    }
}
```


##### Overloading
```javascript!
class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) { // Overload
        return a + b;
    }

    public int add(int a, int b, int c) { // Overload
        return a + b + c;
    }
}

public class OverloadingDemo {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        System.out.println("Sum of 2 ints: " + calc.add(5, 10));
        System.out.println("Sum of 2 doubles: " + calc.add(5.5, 10.5));
        System.out.println("Sum of 3 ints: " + calc.add(1, 2, 3));
    }
}
```

* Overriding: Phải có mối quan hệ thừa kế (extends). Phương thức của lớp con phải có cùng chữ ký (tên, số lượng và kiểu tham số) và kiểu trả về (hoặc kiểu con của kiểu trả về) với phương thức của lớp cha.
* Overloading: Có thể xảy ra trong cùng một lớp. Chữ ký phương thức phải khác nhau.
* Đa hình giúp viết mã linh hoạt, dễ mở rộng và bảo trì.

#### Trừu tượng 


##### Abstract class 

```javascript
abstract class Vehicle { // Lớp trừu tượng
    String make;
    String model;

    public Vehicle(String make, String model) {
        this.make = make;
        this.model = model;
    }

    public void displayInfo() { // Phương thức concrete
        System.out.println("Make: " + make + ", Model: " + model);
    }

    public abstract void start(); // Phương thức trừu tượng (không có phần thân)
    public abstract void stop();  // Phương thức trừu tượng
}

class Car extends Vehicle {
    public Car(String make, String model) {
        super(make, model);
    }

    @Override
    public void start() {
        System.out.println("Car " + model + " started by turning key.");
    }

    @Override
    public void stop() {
        System.out.println("Car " + model + " stopped by turning key off.");
    }
}

class Motorcycle extends Vehicle {
    public Motorcycle(String make, String model) {
        super(make, model);
    }

    @Override
    public void start() {
        System.out.println("Motorcycle " + model + " started by kicking start lever.");
    }

    @Override
    public void stop() {
        System.out.println("Motorcycle " + model + " stopped by pressing brake.");
    }
}

public class AbstractionDemo {
    public static void main(String[] args) {
        // Vehicle myVehicle = new Vehicle("Generic", "Model"); // Không thể tạo đối tượng từ lớp trừu tượng
        Car myCar = new Car("Honda", "CRV");
        myCar.displayInfo();
        myCar.start();
        myCar.stop();

        System.out.println("---");

        Motorcycle myMotorcycle = new Motorcycle("Yamaha", "YZF-R3");
        myMotorcycle.displayInfo();
        myMotorcycle.start();
        myMotorcycle.stop();
    }
}
```


##### Inteface

```java
interface Drivable { // Giao diện
    void drive();
    void turn(String direction);
}

class Truck implements Drivable { // Lớp Truck triển khai giao diện Drivable
    String name;

    public Truck(String name) {
        this.name = name;
    }

    @Override
    public void drive() {
        System.out.println(name + " is driving on the highway.");
    }

    @Override
    public void turn(String direction) {
        System.out.println(name + " is turning " + direction + ".");
    }
}

public class InterfaceDemo {
    public static void main(String[] args) {
        Truck bigRig = new Truck("Big Rig");
        bigRig.drive();
        bigRig.turn("left");
    }
}
```

###### Abstract Class:
* Có thể có cả phương thức trừu tượng (không có phần thân) và phương thức concrete (có phần thân).
* Không thể tạo đối tượng trực tiếp từ lớp trừu tượng.
* Các lớp con phải triển khai tất cả các phương thức trừu tượng của lớp cha trừu tượng hoặc chúng cũng phải là lớp trừu tượng.
* Sử dụng khi có mối quan hệ "is-a" mạnh mẽ và bạn muốn chia sẻ mã (các phương thức concrete).
###### Interface:
* Trước Java 8, tất cả các phương thức trong interface đều ngầm định là public abstract. Từ Java 8, có thể có default và static methods.
* Không thể có các thuộc tính (instance variables), chỉ có hằng số (public static final).
* Một lớp có thể triển khai nhiều interface (implements).
* Sử dụng khi muốn định nghĩa một "khả năng" hoặc "hợp đồng" mà các lớp có thể tuân theo, bất kể hệ thống phân cấp thừa kế của chúng.


## 2. Collection
* Là một tập hợp các lớp và interface
* List,Set,Queue,Map đều là Collection
 
List: danh sách có thứ tự, cho phép trùng lặp.
→ ArrayList, LinkedList
```javascript
        List<String> names = new ArrayList<>();

        // Thêm phần tử (có thể trùng lặp)
        names.add("An");
        names.add("Bình");
```


##### Set: tập hợp không trùng lặp, không đảm bảo thứ tự.
→ Ví dụ: HashSet, TreeSet

```javascript
         Set<String> fruits = new HashSet<>();

        fruits.add("Táo");
        fruits.add("Cam");
        fruits.add("Táo"); // Trùng lặp -> không thêm
        fruits.add("Xoài");

        System.out.println("HashSet:");
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
```


##### Queue: hàng đợi, thường xử lý theo FIFO (First In First Out).
→ Ví dụ: PriorityQueue, LinkedList
```javascript
Queue<String> queue = new LinkedList<>();

        // Thêm phần tử vào hàng đợi
        queue.add("A");
        queue.add("B");
        queue.add("C");

        System.out.println("Hàng đợi ban đầu: " + queue);

        // Lấy và xóa phần tử đầu tiên (FIFO)
        String first = queue.poll();
        System.out.println("Lấy ra: " + first);
        System.out.println("Hàng đợi sau khi poll: " + queue);

        // Xem phần tử đầu tiên mà không xóa
        System.out.println("Phần tử ở đầu: " + queue.peek());
```



##### Map: lưu theo cặp key -> value
* Lưu trữ dữ liệu theo cặp Key – Value.
* Key là duy nhất, nhưng Value có thể trùng lặp.
* Dùng khi bạn muốn ánh xạ (mapping) một giá trị với một khóa, ví dụ: MSSV → Sinh viên.

HashMap: nhanh, không có thứ tự.
TreeMap: sắp xếp Key tự động.

```javascript
Map<String, Integer> students = new HashMap<>();

        // Thêm dữ liệu
        students.put("An", 20);
        students.put("Bình", 22);
        students.put("Cường", 21);
        students.put("An", 23); // Ghi đè vì Key "An" đã tồn tại

        // In ra Map
        System.out.println("Danh sách HashMap:");
        for (Map.Entry<String, Integer> entry : students.entrySet()) {
            System.out.println(entry.getKey() + " - " + entry.getValue());
        }

Map<Integer, String> products = new TreeMap<>();

        products.put(3, "Chuột");
        products.put(1, "Bàn phím");
        products.put(2, "Màn hình");

        System.out.println("Danh sách TreeMap (sắp xếp theo key):");
        for (Map.Entry<Integer, String> entry : products.entrySet()) {
            System.out.println(entry.getKey() + " - " + entry.getValue());
        }

```


## 3.Enum
Enum (viết tắt của Enumeration) là kiểu dữ liệu đặc biệt trong Java, dùng để định nghĩa một tập các giá trị cố định, không thay đổi.
Có một danh sách hằng số có ý nghĩa liên quan đến nhau (ví dụ: ngày trong tuần, trạng thái đơn hàng, màu sắc…), thì gom chúng lại bằng enum để dễ quản lý, tránh nhầm lẫn, và code cũng rõ ràng hơn.

```javascript
enum OrderStatus {
    NEW,
    PROCESSING,
    SHIPPED,
    DELIVERED,
    CANCELLED
}

class Order {
    private int id;
    private String customerName;
    private OrderStatus status; // dùng enum

    public Order(int id, String customerName) {
        this.id = id;
        this.customerName = customerName;
        this.status = OrderStatus.NEW; // mặc định khi tạo là NEW
    }

    public void updateStatus(OrderStatus newStatus) {
        this.status = newStatus;
        System.out.println("Đơn hàng " + id + " đổi trạng thái sang: " + newStatus);
    }

    public void printInfo() {
        System.out.println("Order ID: " + id + ", Customer: " + customerName + ", Status: " + status);
    }
}

public class EnumRealExample {
    public static void main(String[] args) {
        Order order1 = new Order(101, "Nguyễn Văn A");

        order1.printInfo();

        // Cập nhật trạng thái
        order1.updateStatus(OrderStatus.PROCESSING);
        order1.updateStatus(OrderStatus.SHIPPED);
        order1.updateStatus(OrderStatus.DELIVERED);
    }
}

```


## 4. Generic
Khái niệm: Generic cho phép bạn viết các lớp, giao diện và phương thức hoạt động với các kiểu dữ liệu khác nhau mà vẫn đảm bảo an toàn kiểu (type safety) tại thời điểm biên dịch. Điều này giúp loại bỏ nhu cầu ép kiểu (type casting) và giảm lỗi ClassCastException khi chạy.

```javascript
// Lớp Generic Calculator
class GenericCalculator<T extends Number> { // Ràng buộc T phải là một kiểu Number

    // Phương thức cộng
    public double add(T num1, T num2) {
        return num1.doubleValue() + num2.doubleValue();
    }

    // Phương thức trừ
    public double subtract(T num1, T num2) {
        return num1.doubleValue() - num2.doubleValue();
    }

    // Phương thức nhân
    public double multiply(T num1, T num2) {
        return num1.doubleValue() * num2.doubleValue();
    }

    // Phương thức chia
    public double divide(T num1, T num2) {
        if (num2.doubleValue() == 0) {
            throw new IllegalArgumentException("Cannot divide by zero!");
        }
        return num1.doubleValue() / num2.doubleValue();
    }
}

public class GenericDemo {
    public static void main(String[] args) {
        // Sử dụng với Integer
        GenericCalculator<Integer> intCalculator = new GenericCalculator<>();
        System.out.println("Integer Addition: " + intCalculator.add(10, 5));
        System.out.println("Integer Subtraction: " + intCalculator.subtract(10, 5));
        System.out.println("Integer Multiplication: " + intCalculator.multiply(10, 5));
        System.out.println("Integer Division: " + intCalculator.divide(10, 5));
        System.out.println("--------------------");

        // Sử dụng với Double
        GenericCalculator<Double> doubleCalculator = new GenericCalculator<>();
        System.out.println("Double Addition: " + doubleCalculator.add(10.5, 5.2));
        System.out.println("Double Subtraction: " + doubleCalculator.subtract(10.5, 5.2));
        System.out.println("Double Multiplication: " + doubleCalculator.multiply(10.5, 5.2));
        System.out.println("Double Division: " + doubleCalculator.divide(10.5, 5.2));
        System.out.println("--------------------");

        // Sử dụng với Float
        GenericCalculator<Float> floatCalculator = new GenericCalculator<>();
        System.out.println("Float Addition: " + floatCalculator.add(10.5f, 5.2f));
        // ... các phép toán khác tương tự
    }
}
```

## 5. Thread
Có hai cách chính để tạo một luồng trong Java:
* Kế thừa lớp Thread: Ghi đè phương thức run().
* Triển khai giao diện Runnable: Triển khai phương thức run(). Đây là cách được khuyến nghị vì nó cho phép lớp của bạn kế thừa các lớp khác


##### Triển khai Runnable
```javascript
// Tác vụ 1: Đếm ngược
class CountdownTask implements Runnable {
    private String taskName;

    public CountdownTask(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System.out.println(taskName + " starting.");
        for (int i = 5; i >= 1; i--) {
            System.out.println(taskName + ": " + i);
            try {
                Thread.sleep(500); 
            } catch (InterruptedException e) {
                System.out.println(taskName + " interrupted.");
                Thread.currentThread().interrupt(); // Đặt lại trạng thái ngắt
                return;
            }
        }
        System.out.println(taskName + " finished.");
    }
}

// Tác vụ 2: Đếm xuôi
class CountupTask implements Runnable {
    private String taskName;

    public CountupTask(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System.out.println(taskName + " starting.");
        for (int i = 0; i < 5; i++) {
            System.out.println(taskName + ": " + i);
            try {
                Thread.sleep(700); // Dừng 0.7 giây
            } catch (InterruptedException e) {
                System.out.println(taskName + " interrupted.");
                Thread.currentThread().interrupt();
                return;
            }
        }
        System.out.println(taskName + " finished.");
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        System.out.println("Main thread started.");

        // Tạo và khởi chạy Thread 1
        Thread thread1 = new Thread(new CountdownTask("Thread-Countdown"));
        thread1.start(); 

        // Tạo và khởi chạy Thread 2
        Thread thread2 = new Thread(new CountupTask("Thread-Countup"));
        thread2.start();

        System.out.println("Main thread will now wait for other threads to finish (optional).");

        // thread1.join(); // Main thread đợi thread1 hoàn thành
        // thread2.join(); // Main thread đợi thread2 hoàn thành
        // try {
        //     thread1.join();
        //     thread2.join();
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // }


        System.out.println("Main thread finished.");
    }
}
```



##### Triển khai kế thừa Thread


```javascript
class EvenThread extends Thread {
    private int limit;
    private int sum = 0;

    public EvenThread(int limit) {
        this.limit = limit;
    }

    public int getSum() {
        return sum;
    }

    @Override
    public void run() {
        for (int i = 0; i <= limit; i++) {
            if (i % 2 == 0) {
                sum += i;
            }
        }
        System.out.println(getName() + " tính xong: Tổng số chẵn = " + sum);
    }
}

class OddThread extends Thread {
    private int limit;
    private int sum = 0;

    public OddThread(int limit) {
        this.limit = limit;
    }

    public int getSum() {
        return sum;
    }

    @Override
    public void run() {
        for (int i = 1; i <= limit; i++) {
            if (i % 2 != 0) {
                sum += i;
            }
        }
        System.out.println(getName() + " tính xong: Tổng số lẻ = " + sum);
    }
}

public class EvenOddSumExample {
    public static void main(String[] args) {
        int limit = 100; // tính từ 1 đến 100

        EvenThread evenThread = new EvenThread(limit);
        OddThread oddThread = new OddThread(limit);

        evenThread.setName("Thread-Chẵn");
        oddThread.setName("Thread-Lẻ");

        // Khởi động 2 luồng
        evenThread.start();
        oddThread.start();

        // Đợi cả 2 luồng kết thúc
        try {
            evenThread.join();
            oddThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // In kết quả cuối cùng
        int totalEven = evenThread.getSum();
        int totalOdd = oddThread.getSum();

        System.out.println("\n=== KẾT QUẢ CUỐI CÙNG ===");
        System.out.println("Tổng số chẵn = " + totalEven);
        System.out.println("Tổng số lẻ  = " + totalOdd);
    }
}

```


## 6. File
* Serialization (ObjectInputStream / ObjectOutputStream): khi lưu đối tượng vào file.
* Đóng luồng đúng cách
* Dùng try-with-resources để tránh quên đóng file:

```javascript
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
    writer.write("Hello Java!");
} catch (IOException e) {
    e.printStackTrace();
}

```




```java

import java.io.*;
import java.util.ArrayList;
import java.util.List;

// Lớp Product để lưu trữ thông tin sản phẩm
class Product implements Serializable { // Cần Serializable để ghi đối tượng vào file
    private static final long serialVersionUID = 1L; // Để kiểm soát phiên bản khi deserialize

    private String id;
    private String name;
    private double price;

    public Product(String id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "Product [ID=" + id + ", Name=" + name + ", Price=" + price + "]";
    }
}

public class FileIOProductDemo {
    private static final String FILE_NAME = "products.txt"; // Tên file để lưu trữ

    public static void main(String[] args) {
        // 1. Tạo danh sách sản phẩm
        List<Product> products = new ArrayList<>();
        products.add(new Product("P001", "Laptop ABC", 1200.0));
        products.add(new Product("P002", "Mouse XYZ", 25.5));
        products.add(new Product("P003", "Keyboard DEF", 75.0));

        // 2. Ghi danh sách sản phẩm vào file
        writeProductsToFile(products, FILE_NAME);

        // 3. Đọc danh sách sản phẩm từ file và hiển thị
        List<Product> loadedProducts = readProductsFromFile(FILE_NAME);

        System.out.println("--- Products loaded from file: ---");
        if (loadedProducts.isEmpty()) {
            System.out.println("No products found in the file.");
        } else {
            for (Product product : loadedProducts) {
                System.out.println(product);
            }
        }
    }

    // Phương thức ghi danh sách sản phẩm vào file
    public static void writeProductsToFile(List<Product> products, String fileName) {
        // Sử dụng try-with-resources để tự động đóng writer
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
            for (Product product : products) {
                // Ghi mỗi sản phẩm vào một dòng, phân tách bằng dấu phẩy
                writer.write(product.getId() + "," + product.getName() + "," + product.getPrice());
                writer.newLine(); // Xuống dòng cho sản phẩm tiếp theo
            }
            System.out.println("Products successfully written to " + fileName);
        } catch (IOException e) {
            System.err.println("Error writing products to file: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // Phương thức đọc danh sách sản phẩm từ file
    public static List<Product> readProductsFromFile(String fileName) {
        List<Product> products = new ArrayList<>();
        // Sử dụng try-with-resources để tự động đóng reader
        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = reader.readLine()) != null) { // Đọc từng dòng cho đến khi hết file
                String[] parts = line.split(","); // Tách chuỗi bằng dấu phẩy
                if (parts.length == 3) {
                    String id = parts[0];
                    String name = parts[1];
                    double price = Double.parseDouble(parts[2]);
                    products.add(new Product(id, name, price));
                } else {
                    System.err.println("Skipping malformed line: " + line);
                }
            }
            System.out.println("Products successfully read from " + fileName);
        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + fileName);
        } catch (IOException e) {
            System.err.println("Error reading products from file: " + e.getMessage());
            e.printStackTrace();
        } catch (NumberFormatException e) {
            System.err.println("Error parsing price: " + e.getMessage());
            e.printStackTrace();
        }
        return products;
    }
}

```

## 7. JavaFx
* Cách cài đặt javafx vào IDE
* Hiểu được luồng chạy của ứng dụng javafx
* Tạo được ví dụ đơn giản

## 8. Design Pattern
* Singleton
* Factory
* Decorator
* Observer
* Strategy
* Builder
* Bridge

## 9.JDBC

JDBC là một API của Java dùng để kết nối và tương tác với các cơ sở dữ liệu quan hệ. Nó cung cấp một tập hợp các lớp và giao diện cho phép ứng dụng Java thực hiện các thao tác như kết nối CSDL, thực thi câu lệnh SQL, và xử lý kết quả.

* Hiểu được mô hình tổng quan
* Hiểu được cách cài connnector vào dự án

Driver: Mỗi loại CSDL (MySQL, PostgreSQL, Oracle, SQL Server) yêu cầu một JDBC Driver riêng. Driver này là một tập hợp các lớp Java triển khai giao diện JDBC để giao tiếp với CSDL cụ thể đó

Các bước cơ bản của JDBC:
* Tải Driver: Class.forName("com.mysql.cj.jdbc.Driver"); (không bắt buộc từ JDBC 4.0 trở lên, nhưng vẫn thường được thấy).
* Thiết lập Connection: Connection conn = DriverManager.getConnection(url, user, password);
* Tạo Statement/PreparedStatement:
  Statement: Để thực thi các câu lệnh SQL tĩnh.
* PreparedStatement: Ưu tiên hơn cho các câu lệnh SQL có tham số. Ngăn chặn SQL Injection và hiệu quả hơn khi thực thi nhiều lần.
* Thực thi Query/Update:
  executeQuery(): Dùng cho câu lệnh SELECT, trả về ResultSet.
  executeUpdate(): Dùng cho INSERT, UPDATE, DELETE, trả về số hàng bị ảnh hưởng.
  Xử lý ResultSet: Lặp qua resultSet.next() để lấy dữ liệu từ các cột bằng getInt(), getString(), getDouble(), v.v.
* Đóng tài nguyên: Luôn đóng Connection, Statement, ResultSet theo thứ tự ngược lại (ResultSet -> Statement -> Connection). Sử dụng try-with-resources là cách tốt nhất.


```javascript
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

// Lớp Student (Model)
class Student {
    private int id;
    private String name;
    private int age;
    private double gpa;

    public Student(int id, String name, int age, double gpa) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gpa = gpa;
    }

    // Constructor khi thêm mới (ID sẽ được tự động tạo bởi DB)
    public Student(String name, int age, double gpa) {
        this(0, name, age, gpa); // ID tạm thời là 0
    }

    public int getId() { return id; }
    public String getName() { return name; }
    public int getAge() { return age; }
    public double getGpa() { return gpa; }

    public void setId(int id) { this.id = id; } // Để cập nhật ID sau khi insert

    @Override
    public String toString() {
        return "Student [ID=" + id + ", Name=" + name + ", Age=" + age + ", GPA=" + gpa + "]";
    }
}

public class MySqlJdbcDemo {
    // Thông tin kết nối CSDL
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306/jdbcdemo?useSSL=false&serverTimezone=UTC";
    private static final String USER = "root"; // Thay bằng user của bạn
    private static final String PASSWORD = "your_password"; // Thay bằng password của bạn

    public static void main(String[] args) {
        // Tải JDBC Driver (không bắt buộc với JDBC 4.0+ nhưng là thói quen tốt)
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            System.err.println("MySQL JDBC Driver not found!");
            e.printStackTrace();
            return;
        }

        System.out.println("MySQL JDBC Driver Registered!");

        // 1. Lấy danh sách sinh viên hiện có
        System.out.println("\n--- Current Students ---");
        List<Student> students = getAllStudents();
        displayStudents(students);

        // 2. Thêm một sinh viên mới
        System.out.println("\n--- Adding a new student ---");
        Student newStudent = new Student("Charlie", 21, 3.9);
        addStudent(newStudent);

        // 3. Lấy lại danh sách để xem sinh viên mới
        System.out.println("\n--- Students after adding Charlie ---");
        students = getAllStudents();
        displayStudents(students);

        // 4. Cập nhật thông tin sinh viên (ví dụ: Bob thay đổi GPA)
        System.out.println("\n--- Updating Bob's GPA ---");
        updateStudentGpa(2, 3.7); // Giả sử Bob có ID là 2

        // 5. Lấy lại danh sách để xem cập nhật
        System.out.println("\n--- Students after updating Bob ---");
        students = getAllStudents();
        displayStudents(students);

        // 6. Xóa một sinh viên (ví dụ: Charlie)
        System.out.println("\n--- Deleting Charlie ---");
        deleteStudent(3); // Giả sử Charlie có ID là 3

        // 7. Lấy lại danh sách để xem sau khi xóa
        System.out.println("\n--- Students after deleting Charlie ---");
        students = getAllStudents();
        displayStudents(students);
    }

    // Phương thức để lấy tất cả sinh viên từ CSDL
    public static List<Student> getAllStudents() {
        List<Student> students = new ArrayList<>();
        // try-with-resources để tự động đóng Connection, Statement, ResultSet
        try (Connection connection = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
             Statement statement = connection.createStatement();
             ResultSet resultSet = statement.executeQuery("SELECT * FROM students")) {

            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int age = resultSet.getInt("age");
                double gpa = resultSet.getDouble("gpa");
                students.add(new Student(id, name, age, gpa));
            }
        } catch (SQLException e) {
            System.err.println("Error fetching students: " + e.getMessage());
            e.printStackTrace();
        }
        return students;
    }

    // Phương thức để thêm sinh viên mới vào CSDL
    public static void addStudent(Student student) {
        String SQL_INSERT = "INSERT INTO students (name, age, gpa) VALUES (?, ?, ?)";
        try (Connection connection = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
             // PreparedStatement ngăn chặn SQL Injection và hiệu quả hơn
             PreparedStatement preparedStatement = connection.prepareStatement(SQL_INSERT, Statement.RETURN_GENERATED_KEYS)) {

            preparedStatement.setString(1, student.getName());
            preparedStatement.setInt(2, student.getAge());
            preparedStatement.setDouble(3, student.getGpa());

            int rowsAffected = preparedStatement.executeUpdate();
            if (rowsAffected > 0) {
                System.out.println("Student " + student.getName() + " added successfully.");
                // Lấy ID tự động tạo
                try (ResultSet generatedKeys = preparedStatement.getGeneratedKeys()) {
                    if (generatedKeys.next()) {
                        student.setId(generatedKeys.getInt(1)); // Cập nhật ID cho đối tượng student
                    }
                }
            }
        } catch (SQLException e) {
            System.err.println("Error adding student: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // Phương thức để cập nhật GPA của sinh viên theo ID
    public static void updateStudentGpa(int studentId, double newGpa) {
        String SQL_UPDATE = "UPDATE students SET gpa = ? WHERE id = ?";
        try (Connection connection = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
             PreparedStatement preparedStatement = connection.prepareStatement(SQL_UPDATE)) {

            preparedStatement.setDouble(1, newGpa);
            preparedStatement.setInt(2, studentId);

            int rowsAffected = preparedStatement.executeUpdate();
            if (rowsAffected > 0) {
                System.out.println("Student with ID " + studentId + " updated successfully. New GPA: " + newGpa);
            } else {
                System.out.println("Student with ID " + studentId + " not found or no change.");
            }
        } catch (SQLException e) {
            System.err.println("Error updating student: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // Phương thức để xóa sinh viên theo ID
    public static void deleteStudent(int studentId) {
        String SQL_DELETE = "DELETE FROM students WHERE id = ?";
        try (Connection connection = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
             PreparedStatement preparedStatement = connection.prepareStatement(SQL_DELETE)) {

            preparedStatement.setInt(1, studentId);

            int rowsAffected = preparedStatement.executeUpdate();
            if (rowsAffected > 0) {
                System.out.println("Student with ID " + studentId + " deleted successfully.");
            } else {
                System.out.println("Student with ID " + studentId + " not found.");
            }
        } catch (SQLException e) {
            System.err.println("Error deleting student: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // Phương thức hỗ trợ hiển thị danh sách sinh viên
    private static void displayStudents(List<Student> students) {
        if (students.isEmpty()) {
            System.out.println("No students found.");
        } else {
            for (Student s : students) {
                System.out.println(s);
            }
        }
    }
}
```



## 10. Functional Interface

```javascript
import java.util.function.Function;
import java.util.function.BiFunction;

public class FunctionDemo {
    public static void main(String[] args) {       

        // Function: nhận vào String, trả về độ dài (Integer)
        Function<String, Integer> stringLength = str -> str.length();

        System.out.println(stringLength.apply("Hello"));    // 5
        System.out.println(stringLength.apply("Xin chào")); // 8

        // BiFunction: nhận vào 2 số Integer, trả về tổng
        BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
        System.out.println(add.apply(5, 7)); // 12

        // BiFunction phức tạp hơn: nối 2 chuỗi + tính độ dài
        BiFunction<String, String, Integer> concatLength = (s1, s2) -> {
            String result = s1 + s2;
            System.out.println("Chuỗi nối: " + result);
            return result.length();
        };
        System.out.println(concatLength.apply("Xin ", "chào")); // In ra "Xin chào", rồi 7
    }
}

```



## 11. Smart Code

