## 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