Skip to content

Java Interview Question

 

Topic: Common

 


Junior Level (0–2 years)

 

🔹 Core Java

What is immutable object?

Answer

 🔹 Immutable object là gì?

  • Immutable object (đối tượng bất biến) là đối tượng mà trạng thái (giá trị các thuộc tính) của nó không thể thay đổi sau khi được tạo ra.
  • Tức là, một khi bạn đã khởi tạo object, thì tất cả field của nó sẽ luôn giữ nguyên đến cuối vòng đời.

🔹 Đặc điểm

  1. Không thể thay đổi sau khi tạo
    • Mọi method “thay đổi” dữ liệu thực chất sẽ tạo ra một object mới thay vì thay đổi object hiện tại.
  2. Thread-safe tự nhiên
    • Vì không thay đổi được, nên có thể chia sẻ giữa nhiều thread mà không sợ bị race condition.
  3. So sánh dễ dàng
    • Do giá trị không đổi, nên kết quả của hashCode()equals() luôn ổn định.
    • Thường được dùng làm key trong HashMap hoặc phần tử trong HashSet.

🔹 Ví dụ trong Java

  • String là class immutable: javaCopyEditString s1 = "Hello"; String s2 = s1.concat(" World"); System.out.println(s1); // "Hello" (không đổi) System.out.println(s2); // "Hello World" (object mới)concat() không thay đổi s1 mà trả về object mới.
  • Wrapper classes (Integer, Double, Boolean, …) cũng là immutable.

🔹 Cách tạo immutable object

  1. Khai báo class là final (không cho kế thừa).
  2. Các field là privatefinal.
  3. Không cung cấp setter.
  4. Nếu có field là object khác, thì cần clone hoặc copy để tránh bị thay đổi từ bên ngoài.

Ví dụ:

javaCopyEditpublic final class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
}

🔹 Ưu và nhược điểm

✅ Ưu điểm:

  • An toàn trong môi trường đa luồng.
  • Dễ debug, dễ test.
  • Có thể cache hoặc dùng lại mà không lo thay đổi dữ liệu.

❌ Nhược điểm:

Mỗi lần “thay đổi” là phải tạo object mới → có thể tốn bộ nhớ hơn trong một số trường hợp (ví dụ xử lý chuỗi lớn).

What are the differencces between == and .equals() ?

Answer

🔹 .equals() Method

  • Purpose: Compares references (for objects) or values (for primitives).
  • Primitives (int, double, char, etc.):
    == compares the actual values.

int a = 5, b = 5; System.out.println(a == b); // true

  • Objects:
    == checks if both references point to the same memory location (i.e., the same object).

String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1 == s2); // false (different objects in memory)


🔹 .equals() Method

  • Purpose: Compares the contents/values of objects.
  • Defined in the Object class, but often overridden (e.g., in String, Integer, List, etc.).
  • Default implementation in Object behaves like == (compares references).
  • Example with String:

String s1 = new String("hello"); String s2 = new String("hello"); System.out.println(s1.equals(s2)); // true (same content)


🔹 Key Differences

Aspect==.equals()
PrimitivesCompares actual valuesNot applicable (primitives don’t have methods)
ObjectsCompares references (same memory address)Compares contents/values (if overridden)
Default behaviorAlways reference checkReference check (unless overridden)
Overridable?❌ No (operator fixed)✅ Yes (custom classes can override)

What are HashMap and Hashtable? Differences?

Answer

 🔹 What is a HashMap?

  • Introduced in Java 1.2 (part of Collections Framework).
  • Stores data as key–value pairs.
  • Uses hashing internally:
    • hashCode() → find the bucket
    • equals() → check if key already exists.
  • Not synchronized → better performance in single-threaded apps.
  • Allows 1 null key and multiple null values.

🔹 What is a Hashtable?

  • Introduced in Java 1.0 (legacy class, before Collections Framework).
  • Also stores key–value pairs using hashing.
  • Synchronized → thread-safe but slower.
  • Does NOT allow null keys or null values.
  • Largely replaced by HashMap + Collections.synchronizedMap() or ConcurrentHashMap.

🔹 Key Differences

FeatureHashMapHashtable
PackageJava 1.2, part of Collections FrameworkJava 1.0, legacy class
Synchronization❌ Not synchronized (faster)✅ Synchronized (thread-safe, slower)
Null keys/values1 null key, multiple null values allowed❌ No null keys or null values allowed
IterationIterator (fail-fast → ConcurrentModificationException)Enumerator (legacy, not fail-fast)
PerformanceFaster in single-threaded appsSlower due to synchronization overhead
Preferred in new code✅ Yes❌ No (use ConcurrentHashMap instead)

Explain the concept of OOP: Encapsulation, Inheritance, Polymorphism, Abstraction.

Answer

 

What is a final variable/class/method?

Answer

 🔹 1. final Variable

  • A final variable’s value cannot be changed once assigned.
  • It makes the variable a constant (if primitive) or a constant reference (if object reference).

Examples:

javaCopyEditfinal int x = 10;
// x = 20; // ❌ Compile error: cannot assign a value to final variable

final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // ✅ allowed (object can mutate)
sb = new StringBuilder("New"); // ❌ not allowed (reference cannot change)

👉 Meaning: final locks the reference, not necessarily the contents.


🔹 2. final Method

  • A final method cannot be overridden by subclasses.
  • Useful when you want to fix a behavior and prevent subclasses from changing it.

Example:

javaCopyEditclass Parent {
    public final void display() {
        System.out.println("This is final method");
    }
}

class Child extends Parent {
    // ❌ Compile error: cannot override final method
    // public void display() { ... }
}

🔹 3. final Class

  • A final class cannot be inherited (extended).
  • Ensures immutability or security (nobody can subclass and alter its behavior).

Example:

javaCopyEditfinal class Utility {
    public static void log(String msg) {
        System.out.println(msg);
    }
}

// ❌ Compile error: cannot subclass final class
// class ExtendedUtility extends Utility { }
  • Real-world: java.lang.String is final → ensures immutability and security.

🔹 Quick Summary

UsageEffect
final variableValue (or reference) cannot be reassigned
final methodMethod cannot be overridden
final classClass cannot be extended

Mnemonic:

Class → no inheritance

Variable → constant

Method → locked behavior

What is the difference between ArrayList and LinkedList?

Answer

 🔹 1. Cấu trúc lưu trữ

  • ArrayList
    • Dựa trên mảng động (dynamic array).
    • Các phần tử được lưu liên tiếp trong bộ nhớ.
    • Khi mảng đầy, nó sẽ tự động cấp phát mảng mới (thường gấp đôi kích thước) rồi copy dữ liệu sang.
  • LinkedList
    • Dựa trên danh sách liên kết kép (doubly linked list).
    • Mỗi phần tử (node) chứa:
      • Giá trị (data)
      • Con trỏ đến node trước (prev)
      • Con trỏ đến node sau (next)
    • Các phần tử không cần nằm liên tiếp trong bộ nhớ.

🔹 2. Truy cập phần tử (Access)

  • ArrayList:
    • O(1) để truy cập phần tử theo index (random access).
    • Rất nhanh khi bạn hay dùng get(i).
  • LinkedList:
    • O(n) để truy cập phần tử theo index, vì phải duyệt tuần tự từ đầu hoặc cuối list.
    • Thích hợp hơn cho duyệt tuần tự chứ không phải random access.

🔹 3. Thêm / Xóa phần tử (Insert / Delete)

  • ArrayList:
    • Thêm vào cuối: O(1) (trung bình).
    • Thêm / xóa ở giữa hoặc đầu: O(n) vì cần dịch chuyển (shift) các phần tử sau đó.
  • LinkedList:
    • Thêm / xóa ở đầu hoặc cuối: O(1).
    • Thêm / xóa ở giữa: cần duyệt đến vị trí trước → O(n) để tìm, nhưng khi đã có con trỏ node thì chèn/xóa là O(1).

🔹 4. Dung lượng bộ nhớ (Memory)

  • ArrayList:
    • Tốn ít bộ nhớ hơn, chỉ lưu giá trị.
    • Nhưng có thể lãng phí dung lượng mảng đã cấp phát mà chưa dùng.
  • LinkedList:
    • Tốn nhiều bộ nhớ hơn do mỗi node phải lưu thêm 2 con trỏ (prev, next).

🔹 5. Khi nào dùng cái nào?

  • Dùng ArrayList khi:
    • Cần truy cập nhanh theo index.
    • Các thao tác thêm / xóa chủ yếu ở cuối list.
    • Muốn tiết kiệm bộ nhớ.
  • Dùng LinkedList khi:
    • Thêm / xóa thường xuyên ở đầu hoặc giữa danh sách.
    • Không quan trọng truy cập ngẫu nhiên bằng index.
    • Muốn tránh cost copy dữ liệu khi mảng bị resize (như trong ArrayList).

🔹 6. Tóm tắt (So sánh nhanh)

Tiêu chíArrayListLinkedList
Cấu trúcMảng độngDanh sách liên kết kép
Truy cập (get/set)O(1)O(n)
Thêm cuối (add)O(1) trung bìnhO(1)
Thêm đầu/giữaO(n)O(1) sau khi tìm node
Xóa cuốiO(1)O(1)
Xóa đầu/giữaO(n)O(1) sau khi tìm node
Bộ nhớÍt overhead hơnTốn thêm con trỏ prev/next

🔹 Một số ví dụ thực tế

  • ArrayList
    • Danh sách sinh viên, sản phẩm → cần truy cập ngẫu nhiên nhanh.
    • Lưu cache tạm (read nhiều, update ít).
    • Danh sách log/record thêm cuối liên tục.
  • LinkedList
    • Cài đặt Queue/Stack: offer(), poll(), push(), pop().
    • Xử lý lịch sử (Undo/Redo): thêm/xóa đầu nhanh.
    • Trình duyệt duyệt trang trước/sau (prev/next navigation).

🔹 Kinh nghiệm chọn nhanh

  • Nếu 90% thao tác là đọc/truy cập theo indexArrayList.
  • Nếu 90% thao tác là chèn/xóa nhiều ở đầu hoặc giữaLinkedList.
  • Còn nếu cần cấu trúc hàng đợi (FIFO/LIFO) → hay dùng LinkedList hoặc ArrayDeque.

What is method overloading vs method overriding?

Answer

 🔹 Method Overloading

Definition:

  • Same method name, but different parameter lists (different number or type of arguments) within the same class.
  • Happens at compile-time → also called compile-time polymorphism.
  • Return type may be different, but parameter list must be different.

Example (Overloading):

javaCopyEditclass Calculator {
    int add(int a, int b) {
        return a + b;
    }

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

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

Calculator calc = new Calculator();
System.out.println(calc.add(2, 3));       // calls add(int,int)
System.out.println(calc.add(2.5, 3.5));   // calls add(double,double)
System.out.println(calc.add(1, 2, 3));    // calls add(int,int,int)

🔹 Method Overriding

Definition:

  • Subclass provides a specific implementation of a method that is already defined in its superclass.
  • Same method name, same parameters, same return type (or covariant return type).
  • Happens at runtime → also called runtime polymorphism.
  • Requires inheritance.

Example (Overriding):

javaCopyEditclass Animal {
    void sound() {
        System.out.println("Some generic sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Bark");
    }
}

Animal a = new Dog();
a.sound(); // Bark (runtime decision)

🔹 Key Differences

AspectOverloadingOverriding
DefinitionSame name, different parametersSame name, same parameters, subclass redefines
Return typeCan be different (if params differ)Must be same (or covariant)
Occurs inSame class (or subclass too)Subclass only (inheritance required)
Polymorphism typeCompile-time polymorphismRuntime polymorphism
Access modifiersNo restrictionCannot reduce visibility (e.g. publicprotected not allowed)
Annotations@Override not applicable@Override applicable

In short:

Overriding = subclass changes behavior of parent method (runtime).

Overloading = same method name, different parameters (compile-time).

Explain basic access modifiers: public, private, protected, default.

Answer
ModifierSame ClassSame PackageSubclass (diff package)Other Packages
public
protected
default
private

🔹 Exception Handling

Difference between checked and unchecked exceptions?

Answer

 🔹 Checked Exceptions (compile-time)

Must be either handled (try/catch) or declared (throws).

I/O & File Handling

  • IOException
  • FileNotFoundException
  • EOFException
  • InterruptedIOException

SQL & Database

  • SQLException
  • SQLTimeoutException

Class Loading & Reflection

  • ClassNotFoundException
  • InstantiationException
  • IllegalAccessException
  • InvocationTargetException
  • NoSuchMethodException

Concurrency

  • InterruptedException
  • ExecutionException

Other Common

  • CloneNotSupportedException
  • TimeoutException

🔹 Unchecked Exceptions (runtime)

Extend RuntimeException, compiler does not force handling.

Null & Illegal State

  • NullPointerException
  • IllegalStateException
  • UnsupportedOperationException

Arithmetic & Numbers

  • ArithmeticException
  • NumberFormatException
  • InputMismatchException

Array & Collections

  • ArrayIndexOutOfBoundsException
  • StringIndexOutOfBoundsException
  • IndexOutOfBoundsException
  • ConcurrentModificationException
  • NoSuchElementException

Casting & Class Issues

IllegalArgumentException

ClassCastException

How does try-catch-finally work?

Answer

 

🔹 Java Basics

What is the purpose of static?

Answer

 

What are constructors? Can a constructor be private?

Answer

 

Explain the lifecycle of a Java object.

Answer

 🔹 1. Khởi tạo (Creation)

  • Khi bạn dùng new hoặc reflection (Class.newInstance(), Constructor.newInstance()) → JVM sẽ:
    1. Cấp phát bộ nhớ trên Heap cho object.
    2. Gọi constructor để khởi tạo giá trị ban đầu.
    3. Gán reference (biến tham chiếu) trỏ đến object đó.

Ví dụ:

javaCopyEditPerson p = new Person("Alice"); // Object được tạo trong heap

🔹 2. Sử dụng (In Use / Live)

  • Object được chương trình sử dụng thông qua các reference.
  • Trong giai đoạn này, bạn có thể gọi method, thay đổi state (nếu không immutable).

Ví dụ:

javaCopyEditp.setAge(25);
System.out.println(p.getName());

🔹 3. Không còn tham chiếu (Unreachable)

  • Khi không còn biến nào trỏ đến object, nó trở thành unreachable.
  • Object lúc này không thể truy cập được nữa, và chờ để Garbage Collector (GC) dọn dẹp.

Ví dụ:

javaCopyEditp = null; // object "Alice" không còn tham chiếu

🔹 4. Dọn dẹp bởi Garbage Collector (Garbage Collection)

  • JVM chạy Garbage Collector để giải phóng bộ nhớ.
  • Trước khi bị huỷ, nếu class có override finalize() (trước Java 9, nay deprecated) thì method đó được gọi một lần duy nhất.

Ví dụ (không khuyến khích dùng finalize):

javaCopyEdit@Override
protected void finalize() throws Throwable {
    System.out.println("Object is being garbage collected");
}

🔹 5. Huỷ (Destroyed)

  • Sau khi GC dọn dẹp, bộ nhớ được giải phóng.
  • Object biến mất hoàn toàn khỏi Heap.

🔹 Tóm tắt Vòng đời của Object

Huỷ (Destroyed) → Object bị xoá khỏi heap.

Tạo (Creation)new → JVM cấp phát heap + gọi constructor.

Sống (In Use) → Object được dùng qua reference.

Mất tham chiếu (Unreachable) → không còn biến nào trỏ đến.

GC dọn dẹp (Garbage Collection) → JVM thu hồi bộ nhớ.

🔹 String & Collections

Why are Strings immutable?

Answer

 🔹 Summary

Why are Strings immutable?

Reliable, bug-free APIs (no unexpected modifications).

Security (sensitive data protection).

String Pool efficiency (safe reuse of literals).

Thread-safety (no need for synchronization).

Hashing performance (stable hashCode).

How does HashMap work internally?

Answer

 

What is the difference between List, Set, and Map?

Answer

 🔹 1. List

  • Đặc điểm:
    • Dữ liệu có thứ tự (ordered).
    • Cho phép phần tử trùng lặp.
    • Có thể truy cập bằng chỉ số (index).
  • Các implementation phổ biến: ArrayList, LinkedList, Vector, Stack.
  • Ví dụ:
javaCopyEditList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("A"); // cho phép trùng lặp
System.out.println(list); // [A, B, A]

🔹 2. Set

  • Đặc điểm:
    • Không cho phép phần tử trùng lặp.
    • Không đảm bảo thứ tự (trừ một số loại như LinkedHashSet, TreeSet).
  • Các implementation phổ biến:
    • HashSet → không thứ tự.
    • LinkedHashSet → giữ thứ tự chèn vào.
    • TreeSet → sắp xếp theo tự nhiên hoặc comparator.
  • Ví dụ:
javaCopyEditSet<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("A"); // bị bỏ qua
System.out.println(set); // [A, B] (thứ tự không đảm bảo)

🔹 3. Map

  • Đặc điểm:
    • Lưu dữ liệu theo cặp key–value.
    • Key phải duy nhất, nhưng value có thể trùng lặp.
    • Không kế thừa Collection, mà là một interface riêng.
  • Các implementation phổ biến:
    • HashMap → nhanh, không đảm bảo thứ tự.
    • LinkedHashMap → giữ thứ tự chèn.
    • TreeMap → sắp xếp theo key.
  • Ví dụ:
javaCopyEditMap<Integer, String> map = new HashMap<>();
map.put(1, "Alice");
map.put(2, "Bob");
map.put(1, "Charlie"); // ghi đè value cho key=1
System.out.println(map); // {1=Charlie, 2=Bob}

🔹 Bảng so sánh nhanh

CollectionThứ tựCho phép trùng lặpTruy cập theo indexDữ liệu lưu dạng
List✅ Có✅ CóPhần tử (element)
SetKhông (hoặc có tuỳ loại)❌ Không❌ KhôngPhần tử (element)
MapKhông (hoặc có tuỳ loại)❌ Key không, ✅ Value có❌ KhôngCặp (key → value)

Tóm lại:

Map → khi cần ánh xạ key → value.

List → khi cần danh sách có thứ tự, cho phép trùng.

Set → khi cần tập hợp không trùng lặp.

When do you use ArrayList vs LinkedList?

Answer

 


Mid-Level (2–5 years)

 

🔹 Java Features

What are Streams and how do you use them?

Answer

 🔹 1. Streams là gì?

  • Stream trong Java (từ Java 8) là một API dùng để xử lý tập hợp dữ liệu (collections, arrays, I/O) theo cách khai báo (declarative).
  • Nó cho phép lập trình functional style (giống như SQL, hoặc map/filter trong các ngôn ngữ functional).
  • Streams ≠ Collections:
    • Collection lưu trữ dữ liệu.
    • Stream chỉ là dòng dữ liệu (pipeline) được tạo từ collection/array và xử lý tuần tự hoặc song song.

🔹 2. Đặc điểm chính

  1. Không lưu trữ dữ liệu → chỉ xử lý dữ liệu có sẵn.
  2. Lười thực thi (lazy) → chỉ chạy khi có thao tác “terminal” (như collect(), forEach()).
  3. Có thể tuần tự hoặc song song (parallelStream()).
  4. Hỗ trợ pipeline: kết hợp nhiều operation liên tiếp (map, filter, sort, reduce…).

🔹 3. Cách sử dụng Streams

Quy trình 3 bước:

  1. Tạo stream từ collection/array.

List<String> list = Arrays.asList("Java", "Python", "C++");

Stream<String> stream = list.stream();

  1. Trung gian (Intermediate operations): xử lý, biến đổi (không thực thi ngay).
    • map() → biến đổi dữ liệu
    • filter() → lọc dữ liệu
    • sorted() → sắp xếp
    • distinct() → loại trùng
  2. Kết thúc (Terminal operations): kích hoạt xử lý stream.
    • collect() → gom kết quả thành list/set/map
    • forEach() → duyệt và in ra
    • reduce() → gộp dữ liệu thành 1 giá trị

Intermediate: filter, map, flatMap, distinct, sorted, peek, limit, skip, mapToInt/Long/Double.

Terminal: forEach, forEachOrdered, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, findAny.


🔹 5. Streams tuần tự vs song song

  • Sequential Stream: xử lý lần lượt (mặc định).
  • Parallel Stream: xử lý song song (đa luồng) → tăng tốc với dữ liệu lớn.

list.parallelStream()
.forEach(System.out::println);

How does Optional help avoid NullPointerException?

Answer

  • Optional giúp thay thế null bằng cơ chế an toàn.
  • Bắt buộc developer phải xử lý rõ ràng trường hợp không có giá trị.
  • Cung cấp nhiều API tiện lợi (map, orElse, ifPresent, …) để viết code ngắn gọn, sạch sẽ.
  • Nhờ vậy, giảm thiểu tối đa lỗi NullPointerException.

What is the difference between synchronized and Lock?

Answer

 🔹 1. synchronized

  • keyword (từ khóa) có sẵn của Java.
  • Dùng để đảm bảo chỉ một thread được truy cập vào block hoặc method cùng lúc.
  • JVM tự động quản lý lock và release lock.

Ví dụ:

javaCopyEditpublic synchronized void increment() {
    count++;
}

Hoặc:

javaCopyEditsynchronized (this) {
    count++;
}

👉 Ưu điểm: đơn giản, dễ dùng.
👉 Nhược điểm:

  • Ít linh hoạt.
  • Không thể kiểm soát lock chi tiết (không biết có lock được hay không, không có tryLock, không có timeout).
  • Khi một thread bị block thì phải chờ đến khi lock được giải phóng.

🔹 2. Lock (trong java.util.concurrent.locks)

  • Lock là một interface (Java 5+) cho phép kiểm soát chi tiết hơn việc khóa.
  • Cần explicit lock/unlock (người lập trình phải gọi).

Ví dụ:

javaCopyEditLock lock = new ReentrantLock();

lock.lock(); 
try {
    count++;
} finally {
    lock.unlock(); // phải nhớ giải phóng lock
}

👉 Ưu điểm:

  • Linh hoạt hơn synchronized.
  • Hỗ trợ tryLock() → thử lấy lock mà không chờ vô hạn.
  • Hỗ trợ lockInterruptibly() → thread có thể bị interrupt khi đang chờ lock.
  • Hỗ trợ fair lock (công bằng, FIFO).
  • Có thể tạo nhiều điều kiện chờ với Condition (tương tự wait/notify, nhưng mạnh hơn).

👉 Nhược điểm:

  • Lập trình viên phải tự quản lý lock.unlock() (nếu quên → deadlock).
  • Code phức tạp hơn.

🔹 3. Bảng so sánh nhanh

Tiêu chísynchronizedLock (ReentrantLock v.v.)
Bản chấtTừ khóa (keyword) của JavaInterface trong java.util.concurrent
Cơ chế unlockJVM tự động release khi ra khỏi blockPhải gọi unlock() thủ công (trong finally)
Công bằng (fairness)❌ Không hỗ trợ✅ Hỗ trợ fair lock
Thử lấy lock❌ KhôngtryLock()
Timeout khi chờ lock❌ Không✅ Có (tryLock với timeout)
Interrupt khi chờ lock❌ KhônglockInterruptibly()
Condition (chờ & thông báo)Chỉ có wait/notify/notifyAllCondition linh hoạt hơn
Đơn giản / dễ dùng✅ Rất dễ dùng❌ Phức tạp hơn, dễ quên unlock

Explain Functional Interfaces and Lambdas.

Answer

 🔹 1. Functional Interface là gì?

  • interface chỉ có duy nhất 1 method trừu tượng (abstract method) → gọi là SAM (Single Abstract Method).
  • Có thể có thêm default method hoặc static method, nhưng chỉ được phép có 1 abstract method.
  • Thường được đánh dấu bằng annotation @FunctionalInterface (không bắt buộc, nhưng nên dùng để compiler kiểm tra).

Ví dụ:

javaCopyEdit@FunctionalInterface
interface MyFunction {
    int apply(int x, int y); // chỉ 1 abstract method
}

👉 Một số functional interface có sẵn trong java.util.function:

  • Predicate<T> → nhận T, trả về boolean.
  • Function<T, R> → nhận T, trả về R.
  • Consumer<T> → nhận T, không trả về gì (void).
  • Supplier<T> → không nhận gì, trả về T.
  • UnaryOperator<T> → nhận T, trả về T.
  • BinaryOperator<T> → nhận 2 T, trả về T.

🔹 2. Lambda Expression là gì?

  • Lambda là cú pháp rút gọn để triển khai (implement) một functional interface.
  • Giúp viết code ngắn gọn, rõ ràng hơn thay vì phải dùng anonymous class.

Cú pháp:

javaCopyEdit(parameters) -> expression
(parameters) -> { statements }

🔹 3. Ví dụ minh họa

Truyền thống (Anonymous class):

javaCopyEditMyFunction add = new MyFunction() {
    @Override
    public int apply(int x, int y) {
        return x + y;
    }
};
System.out.println(add.apply(2, 3)); // 5

Với Lambda:

javaCopyEditMyFunction add = (x, y) -> x + y;
System.out.println(add.apply(2, 3)); // 5

👉 Rút gọn rất nhiều so với cách cũ.


🔹 4. Dùng Lambda với Functional Interface có sẵn

javaCopyEditList<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Consumer<T>
names.forEach(name -> System.out.println(name));

// Predicate<T>
names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println);

// Function<T, R>
List<Integer> lengths = names.stream()
                             .map(s -> s.length())
                             .toList();

🔹 5. Lợi ích

  • Code ngắn gọn, dễ đọc.
  • Hỗ trợ lập trình hàm (functional style).
  • Kết hợp mạnh mẽ với Streams API.
  • Giảm bớt boilerplate code khi dùng anonymous class.

🔹 6. Tóm lại

Kết hợp lại giúp Java hỗ trợ functional programming (Streams, xử lý dữ liệu theo pipeline).

Functional Interface = interface có 1 abstract method.

Lambda = cú pháp rút gọn để implement method đó.

🔹 Multithreading

How does ExecutorService work?

Answer

 🔹 1. ExecutorService là gì?

  • Là một interface trong package java.util.concurrent.
  • Nó quản lý và điều phối một pool (nhóm) thread thay vì bạn phải tự tạo và quản lý từng thread bằng tay (new Thread(...).start()).
  • Bạn chỉ cần gửi task (Runnable/Callable) → ExecutorService sẽ tự phân công cho thread thích hợp để chạy.

👉 Giúp tách biệt:

  • Người lập trình chỉ quan tâm đến nhiệm vụ (task).
  • Còn ExecutorService lo phần thread management.

🔹 2. Các thành phần chính

  • Task:
    • Runnable → chạy nhưng không trả về giá trị.
    • Callable<T> → chạy và trả về giá trị kiểu T, có thể ném exception.
  • Future<T>:
    • Đại diện cho kết quả của một tác vụ bất đồng bộ (asynchronous).
    • Có thể: kiểm tra đã xong chưa (isDone()), lấy kết quả (get()), hủy task (cancel()).
  • Thread Pool:
    • Nhóm các thread được tái sử dụng để tránh chi phí tạo/destroy thread nhiều lần.
    • Một số loại pool hay dùng:
      • Executors.newFixedThreadPool(n) → số thread cố định.
      • Executors.newCachedThreadPool() → tạo thread khi cần, tái sử dụng khi rảnh.
      • Executors.newSingleThreadExecutor() → chỉ có 1 thread.
      • Executors.newScheduledThreadPool(n) → chạy task trễ hoặc định kỳ.

🔹 3. Ví dụ cơ bản

javaCopyEditimport java.util.concurrent.*;

public class ExecutorExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // Runnable (không có kết quả trả về)
        executor.submit(() -> {
            System.out.println("Task 1 chạy trên " + Thread.currentThread().getName());
        });

        // Callable (có kết quả trả về)
        Future<Integer> future = executor.submit(() -> {
            System.out.println("Task 2 chạy trên " + Thread.currentThread().getName());
            return 42;
        });

        // Lấy kết quả từ Callable
        Integer result = future.get();
        System.out.println("Kết quả: " + result);

        // Đóng Executor
        executor.shutdown();
    }
}

Kết quả (ví dụ):

arduinoCopyEditTask 1 chạy trên pool-1-thread-1
Task 2 chạy trên pool-1-thread-2
Kết quả: 42

🔹 4. Các method quan trọng

  • submit(Runnable/Callable) → gửi task, trả về Future.
  • invokeAll(Collection<Callable>) → chạy tất cả task, trả về list Future.
  • invokeAny(Collection<Callable>) → lấy kết quả của task hoàn thành đầu tiên.
  • shutdown() → ngừng nhận task mới, chờ task hiện tại chạy xong.
  • shutdownNow() → cố gắng dừng tất cả task ngay lập tức.
  • awaitTermination(timeout, unit) → chờ cho tới khi tất cả task kết thúc hoặc hết thời gian.

🔹 5. Cách hoạt động bên trong

  1. Bạn tạo ExecutorService với một thread pool.
  2. Khi gọi submit(), task sẽ được đưa vào hàng đợi (queue).
  3. Các worker thread trong pool sẽ lấy task từ queue để thực thi.
  4. Nếu task là Callable, kết quả sẽ được wrap trong Future.
  5. Khi gọi shutdown(), Executor không nhận task mới nữa, đợi task cũ xong rồi thoát.

🔹 6. Lợi ích

  • Tái sử dụng thread → tiết kiệm tài nguyên, tránh overhead.
  • Quản lý dễ dàng → tập trung vào task thay vì thread.
  • Hỗ trợ bất đồng bộ qua Future.
  • Mở rộng tốt → xử lý nhiều task cùng lúc hiệu quả.
  • Có thể lập lịch (schedule) cho task chạy sau hoặc lặp lại.

Tóm lại:
ExecutorService là một bộ quản lý thread pool trong Java. Bạn gửi task (Runnable/Callable), nó tự phân phối cho các thread trong pool thực hiện, và trả về kết quả qua Future. Nó giúp code gọn gàng, hiệu quả, và dễ mở rộng khi làm việc với đa luồng.

What’s the difference between wait() and sleep()?

Answer

 🔹 1. sleep()

  • Thuộc class Thread (Thread.sleep(millis)).
  • Tạm dừng (pause) thread hiện tại trong một khoảng thời gian xác định.
  • Sau khi hết thời gian, thread sẽ tự động tiếp tục chạy.
  • Không giải phóng (release) lock/monitor nếu thread đang giữ.
  • Thường dùng để tạo delay hoặc mô phỏng chờ đợi có thời gian.

Ví dụ:

javaCopyEditsynchronized void demoSleep() throws InterruptedException {
    System.out.println("Start");
    Thread.sleep(2000); // tạm dừng 2 giây
    System.out.println("End");
}

🔹 2. wait()

  • Thuộc class Object (mọi object trong Java đều có).
  • Chỉ có thể gọi wait() khi thread đang giữ monitor lock (tức là bên trong synchronized).
  • Khi gọi wait():
    • Thread sẽ tạm dừng.
    • Đồng thời giải phóng lock để thread khác có thể vào synchronized block.
  • Thread sẽ được đánh thức lại bằng:
    • notify() → đánh thức một thread đang wait.
    • notifyAll() → đánh thức tất cả thread đang wait.

Ví dụ:

javaCopyEditsynchronized void demoWait() throws InterruptedException {
    System.out.println("Thread vào wait");
    wait(); // thread nhả lock và chờ notify
    System.out.println("Thread được đánh thức");
}

🔹 3. Bảng so sánh nhanh

Tiêu chísleep()wait()
Thuộc lớp nàoThreadObject
Cần synchronized?❌ Không✅ Phải gọi trong synchronized
Giải phóng lock?❌ Không✅ Có
Đánh thức bằng gì?Tự động sau khi hết thời giannotify() hoặc notifyAll() từ thread khác
Mục đích chínhTạm dừng thread trong thời gian cố địnhCơ chế giao tiếp giữa các thread

🔹 4. Tóm lại

wait() → thread tạm dừng và giải phóng lock, chờ thread khác gọi notify()/notifyAll(), dùng cho thread synchronization/communication.

sleep() → cho thread “ngủ” trong một khoảng thời gian, không giải phóng lock, dùng để tạo delay.

What is volatile? How does it work?

Answer

 🔹 1. volatile là gì?

  • volatile là một từ khóa (keyword) trong Java, dùng để khai báo biến.
  • Nó báo cho JVM và Compiler rằng:
    • Biến này sẽ được chia sẻ giữa nhiều thread.
    • Luôn luôn đọc/ghi trực tiếp từ bộ nhớ chính (main memory) thay vì từ cache CPU hoặc bản sao trong thread-local.

👉 Nếu không dùng volatile, một thread có thể giữ bản sao cũ của biến trong cache và không thấy thay đổi từ thread khác.


🔹 2. Cách hoạt động

  • Bình thường, mỗi thread có thể cache biến → gây visibility problem (thread này update nhưng thread khác không thấy ngay).
  • Khi khai báo volatile:
    • Mỗi lần ghi (write) → giá trị sẽ được flush ngay xuống main memory.
    • Mỗi lần đọc (read) → sẽ lấy trực tiếp từ main memory.

🔹 3. Ví dụ không dùng volatile (có bug)

javaCopyEditclass SharedData {
    boolean running = true;

    void run() {
        while (running) {
            // làm việc
        }
    }
}

SharedData data = new SharedData();
new Thread(data::run).start();

// Ở thread khác:
data.running = false; // Thread run() có thể KHÔNG thấy thay đổi này

👉 Có thể dẫn đến vòng lặp vô hạn do thread giữ giá trị running = true trong cache.


🔹 4. Ví dụ dùng volatile (an toàn hơn)

javaCopyEditclass SharedData {
    volatile boolean running = true;

    void run() {
        while (running) {
            // làm việc
        }
    }
}

👉 Khi một thread gán running = false, thread khác sẽ nhìn thấy ngay và thoát khỏi vòng lặp.


🔹 5. Điểm cần lưu ý

  1. Đảm bảo tính nhìn thấy (visibility)
    • Các thay đổi của biến volatile luôn được nhìn thấy bởi mọi thread.
  2. Không đảm bảo tính nguyên tử (atomicity)
    • Ví dụ count++ không an toàn ngay cả khi countvolatile.
    • Muốn an toàn phải dùng synchronized hoặc AtomicInteger.
  3. Không lock
    • Khác với synchronized, volatile không khóa thread.
    • Nhanh và nhẹ hơn, nhưng yếu hơn.
  4. Quan hệ happens-before
    • Một lần ghi vào biến volatile sẽ xảy ra trước (happens-before) mọi lần đọc sau đó → giúp tránh reorder (tái sắp xếp lệnh) bởi JVM/CPU.

🔹 6. Khi nào dùng volatile?

  • Dùng khi:
    • Biến chia sẻ giữa nhiều thread.
    • Update chỉ là gán giá trị đơn giản (boolean flag, status, signal stop).
  • Không dùng khi:
    • Cần các thao tác phức tạp, như count++, check-then-act.
    • Khi đó phải dùng synchronized hoặc các class trong java.util.concurrent (vd: AtomicInteger).

✅ Tóm lại

Thích hợp dùng cho các cờ trạng thái (flag, signal, boolean).

volatile đảm bảo visibility (các thread thấy cùng một giá trị mới nhất).

Không đảm bảo atomicity (các phép toán nhiều bước vẫn không an toàn).

What is the difference between Runnable and Thread?

Answer

 🔹 1. Thread

  • Thread là một class trong Java (java.lang.Thread).
  • Đại diện cho một luồng thực thi trong JVM.
  • Có thể tạo thread bằng cách kế thừa (extends) class Thread và override phương thức run().

Ví dụ:

javaCopyEditclass MyThread extends Thread {
    public void run() {
        System.out.println("Thread đang chạy...");
    }
}

public class Test {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();  // bắt đầu luồng mới
    }
}

🔹 2. Runnable

  • Runnable là một functional interface (java.lang.Runnable) có 1 phương thức duy nhất run().
  • Đại diện cho một nhiệm vụ (task) để thực thi, nhưng không phải là thread.
  • Để chạy, bạn phải đưa Runnable vào trong một Thread.

Ví dụ:

javaCopyEditclass MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable task đang chạy...");
    }
}

public class Test {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable());
        t1.start();
    }
}

Hoặc với lambda (Java 8+):

javaCopyEditThread t1 = new Thread(() -> System.out.println("Runnable với lambda"));
t1.start();

🔹 3. So sánh chi tiết

Tiêu chíThreadRunnable
KiểuLà một class (extends Thread)Là một interface (implements Runnable)
Kế thừaKế thừa Thread nghĩa là không thể kế thừa class khác (Java không hỗ trợ đa kế thừa).Với Runnable, bạn vẫn có thể kế thừa class khác.
Tách biệt nhiệm vụVừa chứa logic task vừa chứa logic thread.Chỉ chứa task, thread thực thi được tạo bên ngoài.
Chia sẻ tài nguyênKhó chia sẻ nhiệm vụ giữa nhiều thread.Một Runnable có thể được nhiều Thread chạy song song.
Thực tế dùngÍt dùng, chỉ khi muốn custom thread class.Được khuyến khích sử dụng cho task song song.

🔹 4. Kết luận

  • Thread = người công nhân (thực thi).
  • Runnable = công việc mà người công nhân thực hiện.

👉 Thực tế hiện nay (nhất là khi dùng ExecutorService), gần như luôn dùng Runnable hoặc Callable, chứ ít khi kế thừa Thread.

🔹 Design & Best Practices

What is SOLID principle?

Answer

 🔹 1. S — Single Responsibility Principle (SRP)

  • A class should have only one reason to change.
  • In other words, each class should only do one job/responsibility.

✅ Benefit: Keeps code easier to maintain and test.

Example (Bad):

javaCopyEditclass Report {
    void generateReport() {}
    void saveToFile() {}
    void printReport() {}
}

👉 This class handles generation, persistence, and printing → multiple responsibilities.

Better (SRP applied):

javaCopyEditclass ReportGenerator { void generate() {} }
class ReportSaver { void save() {} }
class ReportPrinter { void print() {} }

🔹 2. O — Open/Closed Principle (OCP)

  • Software entities (classes, modules, functions) should be open for extension but closed for modification.
  • You should be able to add new functionality without changing existing code.

✅ Benefit: Avoids breaking existing code when adding features.

Example (using polymorphism):

javaCopyEditinterface Shape { double area(); }

class Circle implements Shape { double area() { return ...; } }
class Rectangle implements Shape { double area() { return ...; } }

// Add new shape without modifying existing ones
class Triangle implements Shape { double area() { return ...; } }

🔹 3. L — Liskov Substitution Principle (LSP)

  • Objects of a superclass should be replaceable with objects of its subclasses without breaking the program.
  • Subtypes must behave like their parent type.

✅ Benefit: Prevents inheritance misuse.

Bad Example:

javaCopyEditclass Bird { void fly() {} }
class Ostrich extends Bird { void fly() { throw new UnsupportedOperationException(); } }

👉 Ostrich violates LSP because not all birds can fly.

Better:

javaCopyEditinterface Bird {}
interface FlyableBird extends Bird { void fly(); }

class Sparrow implements FlyableBird { public void fly() {} }
class Ostrich implements Bird {}

🔹 4. I — Interface Segregation Principle (ISP)

  • Clients should not be forced to implement methods they don’t use.
  • Better to have many small, specific interfaces than one large, general-purpose interface.

✅ Benefit: Prevents “fat interfaces”.

Bad Example:

javaCopyEditinterface Machine {
    void print();
    void scan();
    void fax();
}

class OldPrinter implements Machine {
    public void print() {}
    public void scan() { throw new UnsupportedOperationException(); }
    public void fax() { throw new UnsupportedOperationException(); }
}

Better:

javaCopyEditinterface Printer { void print(); }
interface Scanner { void scan(); }
interface Fax { void fax(); }

class OldPrinter implements Printer {
    public void print() {}
}

🔹 5. D — Dependency Inversion Principle (DIP)

  • High-level modules should not depend on low-level modules.
  • Both should depend on abstractions (interfaces).
  • Abstractions should not depend on details; details should depend on abstractions.

✅ Benefit: Improves flexibility and testability (e.g., using dependency injection).

Bad Example:

javaCopyEditclass MySQLDatabase {
    void connect() {}
}

class UserService {
    private MySQLDatabase db = new MySQLDatabase();
}

Better (DIP applied):

javaCopyEditinterface Database { void connect(); }

class MySQLDatabase implements Database { public void connect() {} }
class PostgreSQLDatabase implements Database { public void connect() {} }

class UserService {
    private Database db;
    UserService(Database db) { this.db = db; }
}

✅ Summary (SOLID)

D → Depend on abstractions, not concrete implementations.

S → One class = One responsibility.

O → Extend without modifying existing code.

L → Subclasses must behave like parent class.

I → Many small interfaces > one fat interface.

How do you implement Singleton correctly?

Answer

 🔹 1. What is Singleton?

A design pattern where a class has only one instance during the entire application lifecycle, and it provides a global access point to that instance.


🔹 2. Common Implementations

(a) Eager Initialization

  • Instance is created at class loading.
  • Simple but may waste memory if unused.
javaCopyEditpublic class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {} // prevent instantiation

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

(b) Lazy Initialization (not thread-safe)

  • Creates instance only when needed.
  • But not safe in multithreaded environments.
javaCopyEditpublic class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

(c) Thread-Safe Singleton (synchronized method)

  • Works but synchronized makes it slower.
javaCopyEditpublic class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

(d) Double-Checked Locking (Best Pre-Java 5 fix needed)

  • Improves performance by synchronizing only once when instance is created.
  • Must declare instance as volatile.
javaCopyEditpublic class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) { // First check (no locking)
            synchronized (Singleton.class) {
                if (instance == null) { // Second check (with locking)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

(e) Bill Pugh Singleton (Recommended)

  • Uses a static inner helper class → thread-safe, lazy-loaded, no synchronization overhead.
javaCopyEditpublic class Singleton {

    private Singleton() {}

    // Inner static helper class
    private static class SingletonHelper {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHelper.INSTANCE;
    }
}

(f) Enum Singleton (Most Robust)

  • Since Java enums provide implicit guarantee of single instance, serialization, and thread-safety.
javaCopyEditpublic enum Singleton {
    INSTANCE;

    public void doSomething() {
        System.out.println("Doing something...");
    }
}

Usage:

javaCopyEditSingleton.INSTANCE.doSomething();

🔹 3. Which one to use?

  • Enum Singleton → Best for most cases (simple, safe against serialization & reflection).
  • Bill Pugh Singleton → Best if you prefer class-based approach.
  • Avoid naive lazy initialization without synchronization in multithreaded apps.

Summary:

Bill Pugh or Enum Singleton are the modern, correct implementations.

Singleton ensures only one instance.

Thread safety + lazy initialization are key concerns.

Explain Dependency Injection.

Answer

 🔹 1. Dependency Injection là gì?

  • Dependency (phụ thuộc): là một đối tượng mà class khác cần để hoạt động.
  • Injection (tiêm vào): là cách chúng ta cung cấp dependency từ bên ngoài, thay vì để class tự tạo ra.

👉 DI là một design pattern giúp giảm sự ràng buộc chặt chẽ (tight coupling), làm cho code dễ bảo trì, dễ mở rộng và dễ test.
👉 Đây cũng là một phần quan trọng của khái niệm Inversion of Control (IoC).


🔹 2. Ví dụ không dùng Dependency Injection (Tightly Coupled)

javaCopyEditclass MySQLDatabase {
    void connect() { System.out.println("Kết nối MySQL"); }
}

class UserService {
    private MySQLDatabase db = new MySQLDatabase(); // Tự tạo dependency

    void process() { db.connect(); }
}
  • UserService gắn chặt với MySQLDatabase.
  • Nếu muốn đổi sang PostgreSQL, phải sửa code của UserService.

🔹 3. Ví dụ dùng Dependency Injection (Loosely Coupled)

javaCopyEditinterface Database {
    void connect();
}

class MySQLDatabase implements Database {
    public void connect() { System.out.println("Kết nối MySQL"); }
}

class PostgreSQLDatabase implements Database {
    public void connect() { System.out.println("Kết nối PostgreSQL"); }
}

class UserService {
    private Database db;

    // Inject qua constructor
    public UserService(Database db) {
        this.db = db;
    }

    void process() { db.connect(); }
}

Sử dụng:

javaCopyEditpublic class Main {
    public static void main(String[] args) {
        Database db = new PostgreSQLDatabase(); // Tiêm phụ thuộc từ bên ngoài
        UserService service = new UserService(db);
        service.process(); // Kết nối PostgreSQL
    }
}

👉 UserService không cần biết dùng DB nào, chỉ cần một Database.


🔹 4. Các kiểu Dependency Injection

  1. Constructor Injection → tiêm qua constructor (tốt nhất). javaCopyEditpublic UserService(Database db) { this.db = db; }
  2. Setter Injection → tiêm qua setter method. javaCopyEditpublic void setDatabase(Database db) { this.db = db; }
  3. Field Injection → tiêm trực tiếp vào field (hay dùng trong Spring với @Autowired, nhưng khó test hơn). javaCopyEdit@Autowired private Database db;

🔹 5. Dependency Injection trong Spring

Spring có IoC Container quản lý dependency.

javaCopyEdit@Component
class UserService {
    private final Database db;

    @Autowired
    public UserService(Database db) {
        this.db = db;
    }
}
  • Spring sẽ tự tìm Database implementation (MySQL, PostgreSQL, …) và inject vào UserService.
  • Lập trình viên không cần new đối tượng nữa.

🔹 6. Lợi ích của Dependency Injection

✅ Giảm tight coupling (ràng buộc chặt chẽ).
✅ Dễ thay thế implementation (MySQL ↔ PostgreSQL).
✅ Dễ unit test (inject mock object).
✅ Dễ bảo trì, dễ mở rộng.


Tóm lại:

Spring Framework là ví dụ phổ biến nhất của DI.

DI = “Đừng để tôi tự tạo dependency, hãy đưa nó cho tôi”.

DI giúp code linh hoạt, dễ test, dễ bảo trì.

🔹 JVM Knowledge

What are the memory areas in JVM?

Answer

 

Explain Garbage Collection phases.

Answer

 🔹 1. Garbage Collection là gì?

  • Là cơ chế quản lý bộ nhớ tự động trong JVM.
  • GC sẽ giải phóng các đối tượng trong Heapkhông còn tham chiếu nào trỏ tới (unreachable).
  • Mục tiêu: tránh memory leak và giảm lỗi OutOfMemoryError.

🔹 2. Các giai đoạn chính của Garbage Collection

✅ Giai đoạn 1: Mark (đánh dấu)

  • GC sẽ bắt đầu từ GC Roots (như biến local trong stack, thread đang chạy, static field, JNI references).
  • duyệt object graph để tìm các object còn sống.
  • Những object nào reachable → được đánh dấu là “alive”.
  • Những object không đánh dấu → được coi là rác (garbage).

👉 Nghĩa là GC hỏi: “Ai còn sống?”.


✅ Giai đoạn 2: Sweep (quét dọn)

  • GC đi qua toàn bộ heap.
  • Giải phóng vùng nhớ của các object không được đánh dấu.
  • Các object alive vẫn giữ nguyên.
  • Kết quả: có nhiều “lỗ trống” rải rác → gây fragmentation (phân mảnh bộ nhớ).

✅ Giai đoạn 3: Compact (nén/ gom lại)

  • (Không phải lúc nào cũng có, tùy thuật toán GC).
  • GC sẽ di chuyển các object sống lại gần nhau.
  • Cập nhật lại các tham chiếu.
  • Kết quả: Heap gọn gàng, có vùng nhớ trống liền mạch để cấp phát nhanh hơn.

🔹 3. Generational Garbage Collection (thế hệ)

Java Heap chia thành:

  1. Young Generation (Eden + Survivor)
    • Nơi tạo object mới.
    • Dọn dẹp thường xuyên bằng Minor GC (nhanh).
  2. Old Generation (Tenured)
    • Nơi chứa object sống lâu.
    • Dọn bằng Major GC / Full GC (tốn kém, stop-the-world lâu).
  3. Metaspace (trước kia là PermGen)
    • Chứa metadata của class, method.

🔹 4. Ví dụ đơn giản

javaCopyEditpublic void testGC() {
    String s1 = new String("hello"); // nằm trên heap
    String s2 = new String("world");
    s1 = null; // "hello" không còn tham chiếu → eligible for GC
    System.gc(); // Gợi ý JVM chạy GC (không đảm bảo)
}
  • "hello" sẽ bị GC thu gom.
  • "world" còn tham chiếu s2 → vẫn sống.

🔹 5. Tóm tắt

JVM tối ưu bằng Generational GC → Minor GC (nhanh), Major/Full GC (chậm hơn).

Mark → tìm object còn sống.

Sweep → xóa object chết.

Compact → gom bộ nhớ tránh phân mảnh.

What is the difference between stack and heap memory?

Answer

 https://lukeiscoder.com/2025/04/15/heap-va-stack-memory/

🔹 Spring Framework (if applicable)

Difference between @Component, @Service, and @Repository.

Answer

 🔹 1. Điểm chung

  • Cả 3 đều là Spring stereotype annotations.
  • Đều đánh dấu bean để Spring IoC Container quản lý.
  • Khi Spring quét package (component scan), các class có annotation này sẽ được đưa vào ApplicationContext.

👉 Tóm lại: cả 3 đều là @Component, nhưng dùng cho mục đích khác nhau (semantic khác).


🔹 2. @Component

  • Annotation tổng quát nhất.
  • Dùng để đánh dấu một class là bean.
  • Không phân biệt rõ class này thuộc tầng nào.

Ví dụ:

javaCopyEdit@Component
public class EmailValidator {
    public boolean isValid(String email) {
        return email.contains("@");
    }
}

👉 Thích hợp cho các utility class hoặc bean chung không thuộc rõ Service/Repository.


🔹 3. @Service

  • Là một specialization của @Component.
  • Dùng cho tầng Service (business logic).
  • Giúp code dễ đọc hơn, cho thấy class này chứa logic nghiệp vụ.
  • Khi dùng AOP (Aspect-Oriented Programming), Spring có thể áp dụng behavior đặc biệt cho @Service.

Ví dụ:

javaCopyEdit@Service
public class UserService {
    public void registerUser(String username) {
        System.out.println("Register user: " + username);
    }
}

🔹 4. @Repository

  • Cũng là specialization của @Component.
  • Dùng cho tầng DAO / Persistence (làm việc với database).
  • Spring cung cấp exception translation:
    • Bắt lỗi JDBC/Hibernate (SQLException, PersistenceException, …).
    • Chuyển thành DataAccessException (runtime exception của Spring).

Ví dụ:

javaCopyEdit@Repository
public class UserRepository {
    public void save(User user) {
        // giả sử lưu vào DB
        System.out.println("Saving user: " + user.getName());
    }
}

🔹 5. Tóm tắt sự khác nhau

AnnotationTầng sử dụngĐặc điểm chính
@ComponentChung, tiện íchĐánh dấu bean tổng quát
@ServiceBusiness logicCho thấy class chứa nghiệp vụ
@RepositoryPersistence/DAOHỗ trợ exception translation với DB

Kết luận:

Về ý nghĩa thiết kế, dùng đúng annotation giúp code rõ ràng, dễ bảo trì, dễ cho developer khác hiểu ngay class này thuộc tầng nào.

Về kỹ thuật, cả 3 đều là @Component (Spring xử lý giống nhau).

What is the Spring Bean lifecycle?

Answer

 🔹 Vòng đời (Lifecycle) của một Spring Bean

Khi Spring quản lý một bean trong IoC Container, nó sẽ trải qua nhiều giai đoạn:


1. Instantiation (Khởi tạo)

  • Spring tạo instance của bean bằng constructor hoặc factory method.
  • Ví dụ: new MyService() hoặc thông qua @Bean.

2. Populate Properties (Tiêm dependencies)

  • Spring thực hiện Dependency Injection (DI).
  • Gán các dependency cho bean (thông qua @Autowired, @Value, setter, constructor).

3. BeanNameAware & BeanFactoryAware (Tùy chọn)

  • Nếu bean implement BeanNameAware → Spring sẽ gọi setBeanName().
  • Nếu bean implement BeanFactoryAware → Spring gọi setBeanFactory().
  • Cho phép bean biết mình được Spring quản lý như thế nào.

4. BeanPostProcessor (before init)

  • Trước khi chạy init method, Spring cho phép can thiệp vào bean thông qua BeanPostProcessor.postProcessBeforeInitialization().

5. Initialization (Khởi tạo sau khi inject)

  • Nếu bean implement InitializingBean → gọi afterPropertiesSet().
  • Nếu khai báo @Bean(initMethod="...") hoặc dùng @PostConstruct → gọi method này.
  • Đây là nơi bạn viết logic chuẩn bị dữ liệu, mở kết nối.

6. BeanPostProcessor (after init)

  • Sau khi init, Spring tiếp tục gọi BeanPostProcessor.postProcessAfterInitialization().
  • Thường được dùng cho proxy wrapping (AOP, transaction).

7. Bean Ready (Sẵn sàng sử dụng)

  • Lúc này bean có thể được sử dụng bởi ứng dụng.

8. Destruction (Hủy bean khi context đóng)

  • Khi ApplicationContext shutdown:
    • Nếu bean implement DisposableBean → gọi destroy().
    • Nếu khai báo @PreDestroy hoặc @Bean(destroyMethod="...") → gọi method này.
  • Đây là nơi để giải phóng tài nguyên (đóng file, connection pool, thread, socket).

🔹 Sơ đồ tóm tắt

cssCopyEdit[Khởi tạo] → [Inject dependencies] 
 → [BeanPostProcessor.beforeInit] 
 → [@PostConstruct / afterPropertiesSet / initMethod] 
 → [BeanPostProcessor.afterInit] 
 → [Bean ready] 
 → [@PreDestroy / destroy()]

🔹 Ví dụ minh họa

javaCopyEdit@Component
public class MyBean implements InitializingBean, DisposableBean {

    @PostConstruct
    public void init() {
        System.out.println("PostConstruct: init bean");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("afterPropertiesSet: bean init");
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("PreDestroy: cleanup");
    }

    @Override
    public void destroy() {
        System.out.println("destroy: bean destroyed");
    }
}

👉 Khi chạy ứng dụng:

  1. Bean được khởi tạo
  2. Inject dependencies
  3. Gọi @PostConstructafterPropertiesSet
  4. Bean sẵn sàng
  5. Khi context tắt → @PreDestroydestroy

Kết luận:
Spring Bean lifecycle gồm các bước: Khởi tạo → Inject → Init → Sẵn sàng → Destroy.
Bạn có thể hook vào các giai đoạn bằng @PostConstruct, @PreDestroy, InitializingBean, DisposableBean, hoặc BeanPostProcessor.

What is @Transactional used for?

Answer

 🔹 @Transactional dùng để làm gì?

@Transactional được dùng trong Spring để quản lý giao dịch (transaction management) khi làm việc với cơ sở dữ liệu.

Nó giúp đảm bảo:

  1. Atomicity (Tính nguyên tử) → Tất cả các thao tác trong transaction phải thành công hết, nếu lỗi thì rollback toàn bộ.
  2. Consistency (Nhất quán) → Sau khi transaction kết thúc, dữ liệu phải ở trạng thái hợp lệ.
  3. Isolation (Cách ly) → Các transaction không làm ảnh hưởng sai lệch đến nhau.
  4. Durability (Bền vững) → Khi transaction commit, dữ liệu sẽ được lưu vĩnh viễn.

(Đây chính là ACID principle trong database).


🔹 Cách hoạt động

  • Khi bạn đánh dấu method/class với @Transactional:
    • Spring sẽ tạo một proxy bao quanh method đó.
    • Khi method được gọi → Spring mở transaction.
    • Nếu method chạy thành công → transaction được commit.
    • Nếu method ném ra RuntimeException hoặc Error → transaction sẽ rollback.

🔹 Ví dụ

javaCopyEdit@Service
public class AccountService {

    @Autowired
    private AccountRepository accountRepository;

    @Transactional
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountRepository.findById(fromId).get();
        Account to = accountRepository.findById(toId).get();

        from.debit(amount);
        to.credit(amount);

        accountRepository.save(from);
        accountRepository.save(to);

        // nếu có RuntimeException xảy ra -> rollback toàn bộ
    }
}

👉 Nếu giữa chừng gặp lỗi (ví dụ số dư âm, lỗi DB) → tất cả thay đổi sẽ bị rollback.
👉 Nếu chạy thành công → dữ liệu được commit.


🔹 Các thuộc tính quan trọng

  • propagation → Xác định cách transaction lan truyền giữa các method (ví dụ: REQUIRED, REQUIRES_NEW, NESTED).
  • isolation → Mức độ cách ly (READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE).
  • timeout → Thời gian tối đa cho một transaction.
  • readOnly → Đánh dấu transaction chỉ để đọc, giúp tối ưu hiệu năng.
  • rollbackFor → Xác định exception nào thì rollback (mặc định chỉ rollback RuntimeException).

🔹 Tóm tắt dễ nhớ

Thuộc tínhÝ nghĩa
@TransactionalQuản lý transaction trong DB
CommitKhi method chạy thành công
RollbackKhi có RuntimeException / Error
propagationCách transaction lan truyền
isolationMức độ cách ly giữa transaction

Kết luận:
@Transactional đảm bảo tính an toàn dữ liệu khi thao tác với DB, giúp lập trình viên không cần viết thủ công BEGIN/COMMIT/ROLLBACK.

🔹 So sánh các propagation khi gọi lồng nhau

Propagation (inner)Gọi từ outer (REQUIRED)Ảnh hưởng commit/rollback
REQUIRED (mặc định)Tham gia cùng transaction của outerInner lỗi → rollback toàn bộ outer + inner (vì là 1 transaction)
REQUIRES_NEWTreo (suspend) outer, tạo transaction mớiInner lỗi → rollback chỉ inner; outer không bị ảnh hưởng. Ngược lại outer lỗi không rollback được inner đã commit
NESTEDDùng savepoint bên trong cùng 1 transaction vật lýInner lỗi → rollback đến savepoint (chỉ phần inner), outer tiếp tục được. Outer lỗi → rollback toàn bộ (vượt qua mọi savepoint)
SUPPORTSDùng chung nếu có; nếu không thì chạy không transactionPhụ thuộc outer; nếu không có outer thì không có rollback/commit TX
MANDATORYYêu cầu đang có TXKhông có TX → ném exception
NOT_SUPPORTEDTạm ngưng TX hiện tại, chạy không TXKhông rollback gì vì không TX
NEVERCấm có TXNếu có TX → ném exception

REQUIRES_NEW vs NESTED

  • REQUIRES_NEW: hai transaction vật lý độc lập. Inner commit/rollback không phụ thuộc outer.
  • NESTED: một transaction vật lý, inner là savepoint. Rollback inner chỉ quay về savepoint; nhưng nếu outer rollback cuối cùng, mọi thứ đều mất.


Senior Level (5+ years)

🔹 Advanced Java

How does the Java memory model work?

Answer

 

Explain CompletableFuture and non-blocking programming.

Answer

 

Explain ClassLoaders and custom ClassLoader use cases.

Answer

 

How do you handle performance tuning for a high-load Java application?

Answer

 

🔹 Architecture & Design

How do you design a microservice architecture in Java?

Answer

 

What is Circuit Breaker pattern?

Answer

 

How do you ensure thread safety in concurrent applications?

Answer

 

Explain CAP theorem and how it applies in Java-based distributed systems.

Answer

 

🔹 Framework Mastery

Deep dive: How does Spring Boot autoconfiguration work?

Answer

 

Explain how AOP (Aspect-Oriented Programming) works in Spring.

Answer

 

How would you create a custom annotation in Java?

Answer

 

🔹 System Design / Integration

How do you handle retries, idempotency, and timeouts in external API calls?

Answer

 

What’s the difference between REST and gRPC? When to use each?

Answer

 

How do you design a scalable Java-based backend system?

Answer

 


Topic: Collections

 

Beginner level

What is the difference between List, Set, and Map?

Answer

1. List

  • Ordered collection of elements
  • Duplicates allowed
  • Access by index (like array)
  • Examples: ArrayListLinkedList

Use when:

  • You care about order
  • You might have duplicate values
  • You need to access items by position

2. Set

  • Unordered collection (some types may preserve order like LinkedHashSet)
  • No duplicates allowed
  • Each element must be unique
  • Examples: HashSetTreeSetLinkedHashSet

Use when:

Order is not important (unless using specific Set types)

You need to avoid duplicates

3. Map

  • Key-value pairs
  • Keys are unique, values can be duplicated
  • No index-based access
  • Examples: HashMapTreeMapLinkedHashMap

Use when:

  • You want to map a key to a value
  • You want to look up by key

What is the difference between ArrayList and LinkedList?

Answer

 

What are the key differences between HashSet and TreeSet?

Answer

 

What is the difference between HashMap and Hashtable?

Answer

 

How does HashMap work internally and what is load factor in HashMap?

Answer

 

Intermediate-level

What is the difference between Comparable and Comparator?

Answer

 

What is the difference between Iterator and ListIterator?

Answer

 

What is the difference between Fail-fast and fail-safe?

Answer

 

What are ConcurrentHashMap and how is it different from Collections.synchronizedMap()?

Answer

 

How does sorting work in collections? (e.g., Collections.sort())
What is the use of Queue and Deque interfaces?

Answer

 

Advance level

How does CopyOnWriteArrayList work and when should it be used?

Answer

 

What are the pros and cons of HashMap vs ConcurrentHashMap?

Answer

 

What is the role of equals() and hashCode() in collections?

Answer

 

What are the internal implementations of LinkedList, HashSet, and TreeMap?

Answer

 

What happens when two keys have the same hashcode in a HashMap?

Answer

 

 

 

Published inInterview

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *