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