Việc làm

22/09 · Hồ Chí Minh · Hybrid

Backend Engineer (Java/Spring) - Naver Clip

NAVER VIETNAM · Backend Developer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

10 câu hỏi có thể gặp khi phỏng vấn

Chọn theo kỹ năng và cấp của tin (Java, Mobile (Android, iOS, Flutter)). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.

  1. 01Checked exception và unchecked exception khác nhau thế nào? try-with-resources dùng để làm gì?Java · Junior

    Gợi ý trả lời

    Checked exception (kế thừa Exception nhưng không phải RuntimeException) bị compiler bắt buộc phải catch hoặc khai báo throws, ví dụ IOException. Unchecked (RuntimeException và con của nó) thì không, ví dụ NullPointerException, IllegalArgumentException.

    • Checked hợp với lỗi bên ngoài mà caller có thể xử lý được; unchecked thường là lỗi lập trình hoặc vi phạm điều kiện.
    • try-with-resources tự gọi close() cho mọi object implement AutoCloseable, kể cả khi có exception:
    try (var reader = Files.newBufferedReader(path)) {
        return reader.readLine();
    }
    

    Hay sai: catch (Exception e) {} rồi bỏ trống, làm mất dấu lỗi. Ít nhất phải log kèm stack trace, hoặc bọc lại thành exception có ý nghĩa và ném tiếp.

    Ôn thêm câu hỏi Java →
  2. 02HashMap hoạt động bên trong như thế nào? So sánh ArrayList và LinkedList.Java · Junior

    Gợi ý trả lời

    HashMap là mảng các bucket. Khi put, nó tính hashCode() của key, suy ra chỉ số bucket, rồi lưu entry vào đó. Nhiều key trùng bucket thì nối thành danh sách; từ Java 8, khi một bucket quá dài thì chuyển thành cây đỏ đen để tránh tìm kiếm O(n).

    • Khi số phần tử vượt capacity * loadFactor (mặc định 0.75) thì map resize gấp đôi và phân bố lại.
    • get/put trung bình O(1). HashMap không thread-safe.

    ArrayList dựa trên mảng: truy cập theo index O(1), thêm cuối nhanh, chèn/xoá ở giữa phải dời phần tử. LinkedList là danh sách liên kết đôi: truy cập theo index O(n), tốn bộ nhớ hơn cho mỗi node.

    Người phỏng vấn muốn nghe: trên thực tế ArrayList gần như luôn nhanh hơn nhờ dữ liệu nằm liền nhau trong bộ nhớ (cache-friendly).

    Ôn thêm câu hỏi Java →
  3. 03Dependency Injection trong Spring là gì? Vì sao nên dùng constructor injection thay cho @Autowired trên field?Java

    Gợi ý trả lời

    IoC container của Spring tạo và quản lý các bean, rồi "tiêm" phụ thuộc vào class cần dùng, thay vì class tự new. Nhờ vậy code ít phụ thuộc cứng, dễ thay implementation và dễ test.

    Constructor injection được khuyên dùng vì:

    • Field có thể là final, object luôn ở trạng thái đầy đủ sau khi tạo.
    • Test unit không cần Spring: chỉ cần new OrderService(mockRepo).
    • Constructor quá nhiều tham số là dấu hiệu class đang ôm nhiều việc.
    • Class chỉ có một constructor thì không cần ghi @Autowired.

    Nên nói thêm về scope: mặc định bean là singleton (một instance cho cả context), ngoài ra có prototype, request, session.

    Hay sai: để state thay đổi được (ví dụ biến đếm, list) trong bean singleton. Nhiều request chạy song song sẽ dùng chung và gây race condition.

    Ôn thêm câu hỏi Java →
  4. 04Vì sao không được làm việc nặng trên main thread (UI thread)? Bạn xử lý thế nào?Mobile (Android, iOS, Flutter) · Junior

    Gợi ý trả lời

    Main thread vẽ UI và nhận thao tác người dùng. Mỗi frame chỉ có khoảng 16 ms (60 Hz) hoặc ít hơn với màn 120 Hz, nên việc nặng sẽ gây giật, lag; Android chặn quá lâu sẽ báo ANR.

    Cách xử lý theo nền tảng:

    • Android: coroutines với Dispatchers.IO cho I/O, Dispatchers.Default cho tính toán, sau đó cập nhật state trên Main.
    • iOS: async/await với Swift concurrency; code UI đánh dấu @MainActor.
    • Flutter: Dart chạy một isolate; I/O bất đồng bộ không chặn, nhưng tính toán nặng (parse JSON lớn, xử lý ảnh) nên đẩy sang isolate khác bằng Isolate.run hoặc compute.

    Hay sai: nghĩ async là đủ. Hàm async trong Dart vẫn chạy trên cùng isolate, nên vòng lặp tính toán nặng vẫn làm đơ UI.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  5. 05Kotlin coroutines: suspend function, scope, dispatcher và structured concurrency là gì?Mobile (Android, iOS, Flutter)

    Gợi ý trả lời

    Coroutine là tác vụ nhẹ có thể tạm dừng (suspend) mà không chặn thread. suspend fun chỉ gọi được từ coroutine hoặc từ suspend function khác.

    • Scope quyết định vòng đời: viewModelScope, lifecycleScope tự huỷ coroutine khi ViewModel hoặc màn hình bị huỷ.
    • Dispatcher quyết định chạy ở đâu: Main, IO, Default.
    • Structured concurrency: coroutine con gắn với cha; cha huỷ thì con huỷ, con lỗi thì lan lên cha (trừ khi dùng SupervisorJob).
    viewModelScope.launch {
        val user = withContext(Dispatchers.IO) { repo.fetchUser() }
        _state.value = UiState.Success(user)
    }
    

    Hay sai: dùng GlobalScope gây rò rỉ; bắt CancellationException trong catch (e: Exception) mà không ném lại, làm hỏng cơ chế huỷ.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  6. 06@Transactional hoạt động thế nào? Vì sao có lúc gắn annotation rồi mà transaction không chạy?Java

    Gợi ý trả lời

    Spring tạo proxy bao quanh bean. Khi gọi method có @Transactional qua proxy, proxy mở transaction, gọi method thật, rồi commit hoặc rollback.

    Những lý do hay gặp khiến nó "không ăn":

    • Self-invocation: method A trong cùng class gọi this.b() có @Transactional, lời gọi không đi qua proxy.
    • Method là private hoặc final: proxy không chặn được lời gọi.
    • Mặc định chỉ rollback với unchecked exception và Error. Checked exception vẫn commit, trừ khi khai báo rollbackFor = Exception.class.
    • Exception bị catch và nuốt bên trong method.

    Nên biết thêm propagation (REQUIRED, REQUIRES_NEW) và readOnly = true cho các truy vấn chỉ đọc.

    Người phỏng vấn muốn nghe: bạn giải thích được bằng cơ chế proxy, không chỉ liệt kê lỗi.

    Ôn thêm câu hỏi Java →
  7. 07Lỗi N+1 query trong JPA/Hibernate là gì và xử lý ra sao?Java

    Gợi ý trả lời

    N+1 xảy ra khi bạn lấy N bản ghi bằng 1 query, rồi mỗi lần truy cập quan hệ lazy lại phát sinh thêm 1 query. Ví dụ lấy 100 đơn hàng, rồi gọi order.getCustomer() trong vòng lặp là thành 101 query.

    Cách xử lý:

    • JOIN FETCH trong JPQL: select o from Order o join fetch o.customer.
    • @EntityGraph(attributePaths = "customer") trên method của repository.
    • @BatchSize hoặc hibernate.default_batch_fetch_size để gom lazy load thành câu IN (...).
    • Với màn hình chỉ đọc, trả thẳng DTO projection thay vì entity.

    Phát hiện bằng cách bật log SQL (spring.jpa.show-sql hoặc log của Hibernate) khi dev, hoặc dùng thống kê của Hibernate.

    Hay sai: đổi sang FetchType.EAGER cho xong. Cách này chỉ đổi chỗ lỗi và làm mọi truy vấn khác nặng hơn. Fetch join nhiều collection cùng lúc cũng dễ gây tích Descartes.

    Ôn thêm câu hỏi Java →
  8. 08Bộ nhớ JVM gồm những vùng nào? Garbage Collector hoạt động ra sao và Java có bị memory leak không?Java

    Gợi ý trả lời

    Hai vùng chính: stack (mỗi thread một stack, chứa biến cục bộ và tham chiếu) và heap (chứa object, dùng chung). Ngoài ra có Metaspace chứa thông tin class.

    • GC tìm các object không còn được tham chiếu từ "GC roots" (biến trên stack, field static...) và thu hồi.
    • Heap chia theo thế hệ: object mới nằm ở young generation, sống lâu thì được chuyển lên old generation.
    • G1 là GC mặc định; ZGC hợp khi cần pause rất ngắn với heap lớn.

    Java vẫn leak khi object không dùng nữa mà vẫn còn tham chiếu:

    • Cache hoặc static Map chỉ thêm, không bao giờ xoá.
    • ThreadLocal không remove() trong thread pool.
    • Listener đăng ký mà không huỷ.

    Người phỏng vấn muốn nghe: phân biệt được OutOfMemoryError: Java heap space với lỗi Metaspace, và biết dùng heap dump để tìm ai đang giữ object.

    Ôn thêm câu hỏi Java →
  9. 09Swift concurrency: async/await, Task, actor và @MainActor hoạt động thế nào?Mobile (Android, iOS, Flutter)

    Gợi ý trả lời

    Swift concurrency đưa bất đồng bộ vào ngôn ngữ, cùng với kiểm tra data race lúc biên dịch (chặt hơn từ Swift 6).

    • async/await: viết code bất đồng bộ tuần tự, thay cho completion handler lồng nhau.
    • Task { } tạo tác vụ mới; async let và TaskGroup chạy song song có cấu trúc. Task hỗ trợ huỷ hợp tác qua Task.checkCancellation().
    • actor: kiểu tham chiếu mà trạng thái bên trong chỉ được truy cập tuần tự, tránh data race. Gọi từ ngoài phải await.
    • @MainActor: đảm bảo code chạy trên main thread, dùng cho ViewModel và UI.
    • Sendable: đánh dấu kiểu an toàn khi chuyển giữa các vùng concurrency.
    @MainActor final class ProfileViewModel: ObservableObject {
        @Published var user: User?
        func load() async { user = try? await api.fetchUser() }
    }
    

    Hay sai: actor reentrancy: sau mỗi await, trạng thái của actor có thể đã bị thay đổi.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  10. 10Bạn nghĩ sao về việc làm thêm giờ (OT)?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Trả lời thẳng thắn và cân bằng: sẵn sàng khi thật sự cần, nhưng không coi OT kéo dài là bình thường.

    Câu mẫu: "Em sẵn sàng làm thêm khi có việc gấp như sự cố production hay trước một đợt release quan trọng. Nhưng nếu OT diễn ra thường xuyên, em nghĩ cần xem lại cách estimate hoặc phạm vi, và em sẽ chủ động nêu điều đó với team. Em cũng muốn hỏi thêm: ở team mình OT thường xảy ra trong trường hợp nào và được tính như thế nào ạ?"

    Đây cũng là lúc bạn tìm hiểu ngược: OT có được trả lương hoặc nghỉ bù theo quy định không, tần suất ra sao. Câu trả lời của họ cho bạn biết nhiều về văn hoá công ty.

    Hay sai: hứa "OT thoải mái" để lấy điểm, rồi vào làm mới thấy không chịu nổi.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở NAVER VIETNAM?

Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.

Chia sẻ kinh nghiệm phỏng vấn