Mối quan hệ giữa JDK, JRE và JVM
JDK (Java Development Kit): Bộ công cụ phát triển phần mềm, bao gồm JRE và các công cụ biên dịch/debugger.
JRE (Java Runtime Environment): Môi trường chạy thực tế, bao gồm JVM và các thư viện lớp cơ bản.
JVM (Java Virtual Machine): Máy ảo, chịu trách nhiệm thực thi bytecode và quản lý bộ nhớ.
Các kiểu dữ liệu trong Java
Java chia làm hai nhóm chính:
Kiểu nguyên thủy (Primitive): bao gồm các loại số (byte, short, int, long), số thực (float, double), ký tự (char) và logic (boolean).
Kiểu tham chiếu (Reference): String là kiểu tham chiếu phổ biến nhất, được khai báo là final (bất biến). Biến tham chiếu lưu trữ địa chỉ bộ nhớ đối tượng trên Heap. Các nhóm này bao gồm lớp (class), giao diện (interface) và mảng (array).
Truy cập biến non-static trong môi trường static
Không thể thực hiện. Biến static thuộc về lớp và được nạp khi JVM khởi động, trong khi biến non-static thuộc về đối tượng cụ thể. Tại thời điểm static chạy, đối tượng có thể chưa được tạo, dẫn đến lỗi biên dịch hoặc runtime.
Phân biệt Overriding và Overloading
Overriding (Ghi đè): Định nghĩa lại phương thức của lớp cha trong lớp con, giữ nguyên签 danh (signature) nhưng thay đổi hành vi.
Overloading (Nạp chồng): Định nghĩa nhiều phương thức cùng tên trong cùng một lớp nhưng danh sách tham số khác nhau (kiểu hoặc số lượng).
So sánh Interface và Abstract Class
Interface: Chứa các phương thức trừu tượng (mặc định). Một lớp có thể implements nhiều interface. Định nghĩa "những gì" đối tượng có thể làm.
Abstract Class: Có thể chứa cả phương thức trừu tượng và phương thức đã triển khai. Chỉ hỗ trợ đơn kế thừa. Dùng để chia sẻ code giữa các lớp con liên quan.
Tổng hợp về Collection: List, Set, Map
List: Lưu trữ theo thứ tự, cho phép trùng lặp.
- ArrayList: Dựa trên mảng động. Truy cập nhanh, chèn/xóa chèn do phải dịch chuyển phần tử. Không an toàn luồng.
- LinkedList: Cấu trúc danh sách liên kết đôi. Thêm/xóa nhanh, truy cập ngẫu nhiên chậm hơn. Hỗ trợ Stack/Queue.
- Vector: Tương tự ArrayList nhưng cũ hơn và an toàn luồng (synchronized), hiệu suất thấp hơn.
Set: Không cho phép phần tử trùng lặp, chỉ tối đa một phần tử null.
- HashSet: Dùng hash table, không đảm bảo thứ tự. Tốc độ tìm kiếm nhanh.
- LinkedHashSet: Kế thừa HashSet, thêm danh sách liên kết để duy trì thứ tự chèn.
- TreeSet: Dựa trên TreeMap (cây đỏ-đen), tự động sắp xếp theo tự nhiên hoặc Comparator. Không chấp nhận null.
Map: Lưu trữ cặp Key-Value. Key là duy nhất, Value có thể trùng.
- HashMap: Cho phép null key/value, không đồng bộ.
- TreeMap: Sắp xếp theo Key.
Sự khác biệt giữa HashMap và HashTable
HashMap: Không đồng bộ (non-thread-safe), hiệu suất cao, cho phép một key null và nhiều value null.
HashTable: Đồng bộ (thread-safe), hiệu suất thấp hơn, không cho phép null key hoặc null value. Lớp cũ, ít dùng trong hiện tại.
So sánh ArrayList và Vector
Giống nhau: Đều là mảng động, hỗ trợ truy cập ngẫu nhiên nhanh.
Khác nhau:
- Đồng bộ hóa: Vector an toàn luồng (phương thức synchronized), ArrayList không.
- Hiệu năng: Vector chậm hơn do xử lý khóa.
- Mở rộng: Vector mặc định tăng gấp đôi kích thước khi đầy, ArrayList tăng 50%.
- Duyệt: Vector hỗ trợ cả Enumeration và Iterator, ArrayList chỉ hỗ trợ Iterator.
Các cách tạo luồng (Thread)
1. Kế thừa lớp Thread:
public class Worker extends Thread {
public void run() {
System.out.println("Worker đang chạy...");
}
public static void main(String[] args) {
new Worker().start();
}
}
2. Triển khai interface Runnable:
public class Task implements Runnable {
public void run() {
System.out.println("Task đang thực thi...");
}
public static void main(String[] args) {
Thread t = new Thread(new Task());
t.start();
}
}
3. Sử dụng Callable và FutureTask:
public class DataCall implements Callable<String> {
public String call() throws Exception {
return "Kết quả tính toán";
}
public static void main(String[] args) throws Exception {
DataCall c = new DataCall();
FutureTask<String> ft = new FutureTask<>(c);
new Thread(ft).start();
System.out.println(ft.get());
}
}
Cơ chế Garbage Collection (GC)
GC tự động giám sát và thu hồi vùng nhớ của các đối tượng không còn được tham chiếu để tránh rò rỉ bộ nhớ. Lập trình viên không cần giải phóng bộ nhớ thủ công. Có thể gợi ý GC chạy bằng `System.gc()` hoặc `Runtime.getRuntime().gc()`, nhưng không được JVM cam kết thực hiện ngay lập tức.
Hibernate so với MyBatis
Hibernate: Framework ORM tự động hóa cao, ẩn chi tiết SQL, ánh xạ đối tượng trực tiếp sang bảng cơ sở dữ liệu. Phù hợp cho thao tác CRUD phức tạp.
MyBatis: Bán tự động (Semi-auto), SQL được viết riêng trong file XML và ánh xạ thủ công vào Java. Đưa lại sự kiểm soát tốt hơn cho lập trình viên đối với câu lệnh SQL.
Final, Finally, Finalize
final: Từ khóa sửa đổi. Nếu sửa lớp thì lớp không thể được kế thừa; sửa phương thức thì không thể ghi đè; sửa biến thì biến trở thành hằng số.
finally: Khối lệnh trong cơ chế xử lý ngoại lệ, luôn được thực thi sau khối try-catch (dù có exception hay không).
finalize: Phương thức được gọi bởi Garbage Collector trước khi đối tượng bị thu hồi bộ nhớ, dùng để dọn dẹp tài nguyên (ít được khuyến khích dùng hiện nay).
Thuật toán đảo ngược chuỗi
public String reverse(String text) {
if (text == null || text.length() <= 1) {
return text;
}
return reverse(text.substring(1)) + text.charAt(0);
}
Thuật toán sắp xếp nổi bọt (Bubble Sort)
public void sortBubble(int[] numbers) {
int n = numbers.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (numbers[j] > numbers[j + 1]) {
// Hoán đổi vị trí
int temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
}
}
}
}
Kiến trúc RESTful
REST là phong cách kiến trúc giao tiếp giữa Client và Server. Sử dụng các động từ HTTP chuẩn (GET, POST, PUT, DELETE) để thao tác tài nguyên. Ưu điểm: gọn nhẹ, phân tầng rõ ràng, dễ dàng cache và mở rộng.
Mô hình Spring
Spring là framework mã nguồn mở dùng để đơn giản hóa lập trình J2EE. Nó là thùng chứa (container) thực hiện IoC (Inversion of Control) và DI (Dependency Injection), cũng như hỗ trợ mạnh mẽ AOP (Lập trình hướng khía cạnh).
Kịch bản Switch/Case rơi tự do (Fall-through)
int val = 2;
int res = 0;
switch (val) {
case 1:
res += 2;
case 2:
res += 4; // val=2 nhảy vào đây, res=4
case 3:
res += 6; // Không có break, tiếp tục chạy xuống, res=10
}
// Kết quả: res = 10
Triển khai mẫu Singleton (Đơn nhất)
public class DatabaseConnection {
private static final DatabaseConnection INSTANCE = new DatabaseConnection();
private DatabaseConnection() {
// Private constructor để ngăn tạo instance từ bên ngoài
}
public static DatabaseConnection getInstance() {
return INSTANCE;
}
}
Logics xử lý HashMap với key trùng lặp
Map<String, String> map = new HashMap<>();
map.put("name", null);
map.put("name", "jack");
map.put("name", null);
System.out.println(map.size()); // Kết quả là 1
Giải thích: Trong HashMap, key phải duy nhất. Khi put key "jack" đè lên "null", sau đó lại put "null" đè lên "jack", cuối cùng chỉ còn 1 entry duy nhất.
Annotation trong Spring MVC
@RequestMapping: Sử dụng để ánh xạ các request HTTP cụ thể đến các phương thức xử lý trong Controller.
@Autowired: Sử dụng để tiêm phụ thuộc (Dependency Injection), tự động tìm và gắn đối tượng bean (theo kiểu hoặc tên) vào biến được đánh dấu.
Giao dịch trong Spring (Transaction)
- Programmatic: Quản lý giao dịch bằng code thủ công (beginTransaction, commit), khó bảo trì.
- Declarative: Sử dụng AOP, phổ biến nhất là dùng annotation `@Transactional` hoặc cấu hình XML, tách biệt logic nghiệp vụ và logic quản lý giao dịch.
Tập tin cấu hình trong Spring Boot
Application.properties/yml: Dùng để cấu hình ứng dụng chính, datasource, logging, v.v.
Bootstrap.properties/yml: Dùng cho bối cảnh bootstrap (Context bootstrap), thường dùng khi kết nối với Spring Cloud Config hoặc cấu hình các thuộc tính không thể bị ghi đè (encryption/decryption).
Spring Boot hỗ trợ cả định dạng `.properties` và `.yml` (dùng thụt lề cây).
Các loại JOIN trong MySQL
Inner Join: Chỉ trả về các dòng khi có sự khớp dữ liệu ở cả hai bảng.
Left Join: Trả về tất cả dòng từ bảng trái, nếu không khớp bên phải thì để NULL.
Right Join: Trả về tất cả dòng từ bảng phải, nếu không khớp bên trái thì để NULL.
Các vấn đề về Cache
Cache Penetration (Xâm nhập): Hacker truy vấn dữ liệu không tồn tại (cache miss, DB miss). Giải pháp: Cache key-null với thời gian ngắn, hoặc validate dữ liệu đầu vào.
Cache Breakdown (Đánh sập): Hotkey hết hạn, hàng loạt request đồng thời đánh thẳng vào DB. Giải pháp: Distributed Lock (Lock phân tán) để chỉ 1 thread được query DB, các thread khác chờ.
Cache Avalanche (Tàn lụi): Lớn lượng key hết hạn cùng lúc làm DB quá tải. Giải pháp: Thiết lập thời gian hết hạn ngẫu nhiên (random TTL).
Process, Thread, Daemon Thread
Process: Đơn vị cơ bản của tài nguyên hệ thống, chứa các luồng.
Thread: Đơn vị cơ bản của CPU调度 (lightweight process), chia sẻ tài nguyên của process.
Daemon Thread: Luồng nền, chạy hỗ trợ (như GC, garbage collection), sẽ tắt khi không còn User Thread nào chạy.
Khác biệt Sleep và Wait
sleep(long ms): Luồng ngủ nhưng không nhả lock (monitor). Thời gian ngủ được định sẵn.
wait(): Phải trong synchronized block, nhả lock và chuyển sang trạng thái chờ (waiting pool). Chỉ quay lại khi được notify/notifyAll.
Khác biệt synchronized và Lock
synchronized: Từ khóa Java, được JVM quản lý, tự động nhả lock khi code block xong hoặc có lỗi. Không thể gián đoạn.
Lock (Interface): Cần khai báo và giải phóng lock thủ công (trong finally). Hỗ trợ tryLock (thử lấy lock), fair lock (công bằng) và khả năng gián đoạn luồng chờ.
HTTP GET vs POST
GET: Dữ liệu nằm trên URL, chỉ truy xuất, không an toàn cho mật khẩu, dễ cache. Tạo 1 gói TCP.
POST: Dữ liệu nằm trong Body request, dùng để tạo/sửa dữ liệu, bảo mật hơn, không cache mặc định. Tạo 2 gói TCP (header rồi body).
Index trong MySQL
Clustered Index (Chỉ số nhóm): Thứ tự lưu trữ vật lý của dữ liệu theo index. Một bảng chỉ có 1 clustered index (thường là Primary Key).
Covering Index (Chỉ số bao phủ): Index chứa toàn bộ các cột cần thiết cho câu lệnh SELECT, giúp không cần quét bảng (table lookup), tăng tốc độ truy vấn.
Phạm vi truy cập (Access Modifiers)
public: Truy cập mọi nơi.
private: Chỉ trong lớp chứa.
protected: Cùng package và lớp con (kể khác package).
default: Chỉ cùng package.
So sánh == và equals()
==: So sánh địa chỉ bộ nhớ (tham chiếu).
equals(): So sánh nội dung (giá trị). (Trừ khi class không override equals thì nó behaves giống ==).
Về String trong Java
String không phải kiểu nguyên thủy, là kiểu tham chiếu.
String a = "i"; -> Lưu trong String Constant Pool.
String b = new String("i"); -> Lưu trong Heap (đối tượng mới). Giá trị bằng nhau nhưng địa chỉ khác nhau.
Lớp trừu tượng (Abstract Class)
Lớp trừu tượng không thể được khởi tạo (new). Không thể sửa bằng final (vì mục đích chính là để kế thừa). Lớp trừu tượng không bắt buộc phải có phương thức trừu tượng, nhưng nếu có phương thức trừu tượng thì lớp bắt buộc phải là trừu tượng.
Khái niệm Đa luồng và Đồng bộ
Parallel (Song song): Nhiều sự kiện xảy ra cùng một thời điểm (cần nhiều CPU).
Concurrent (Đồng thời): Nhiều sự kiện diễn ra trong cùng một khoảng thời gian (luân phiên CPU).
Synchronous (Đồng bộ): Xong việc này mới làm việc kia (blocking).
Asynchronous (Bất đồng bộ): Gửi yêu cầu và làm việc khác, kết quả trả về sau (non-blocking).
Bài tập SQL
1. Tính tổng điểm từng học sinh:
SELECT student_name, SUM(score)
FROM exam_results
GROUP BY student_name
ORDER BY total_score DESC;
2. Đếm số người đạt điểm > 80 theo môn:
SELECT subject_name, COUNT(*)
FROM exam_results
WHERE score > 80
GROUP BY subject_name;
3. Các hàm tổng hợp (Aggregate Functions):
COUNT(): Đếm số lượng bản ghi.SUM(): Tổng các giá trị.AVG(): Giá trị trung bình.MAX(): Giá trị lớn nhất.MIN(): Giá trị nhỏ nhất.
4. Tìm nhân viên có lương cao nhất trong công ty 'company1':
SELECT emp_name
FROM works
WHERE company_name = 'company1'
AND salary = (SELECT MAX(salary) FROM works WHERE company_name = 'company1');
5. Truy vấn ID chẵn:
SELECT * FROM student WHERE MOD(id, 2) = 0;