Trong bài viết này, chúng ta sẽ cùng nhau thảo luận về một số câu hỏi phỏng vấn quan trọng liên quan đến Java SE 8 kèm theo câu trả lời chi tiết. Những câu hỏi còn lại sẽ được thảo luận trong phần tiếp theo.
Tổng hợp câu hỏi phỏng vấn Java 8
- Tại sao chúng ta lại cần những thay đổi trong Java?
- Các tính năng mới của Java SE 8 là gì?
- Các tính năng mới trong Java SE 8 mang lại những lợi ích nào?
- Biểu thức Lambda (Lambda Expression) là gì?
- Ba thành phần của một biểu thức Lambda là gì? Biểu thức Lambda có kiểu dữ liệu gì?
- Functional Interface là gì? SAM Interface là gì?
- Có thể tự định nghĩa Functional Interface của riêng mình không?
@FunctionalInterface
là gì? Các quy tắc để định nghĩa một Functional Interface? - Có bắt buộc phải dùng Annotation
@FunctionalInterface
để định nghĩa một Functional Interface không? Công dụng của nó là gì? Tại sao chúng ta cần Functional Interface trong Java? - Khi nào chúng ta nên sử dụng Stream API của Java 8? Tại sao cần dùng Stream API trong các dự án?
- Giải thích sự khác biệt giữa Collection API và Stream API?
- Spliterator trong Java SE 8 là gì? Sự khác biệt giữa Iterator và Spliterator?
- Optional trong Java 8 là gì? Công dụng và ưu điểm của Optional là gì?
- Type Inference là gì? Tính năng này có trong các phiên bản cũ hơn như Java 7 không hay chỉ có ở Java SE 8?
Chuẩn bị phỏng vấn với Java 8: Câu hỏi và Câu trả lời
Trong phần này, chúng ta sẽ lần lượt trả lời chi tiết cho từng câu hỏi được đề cập ở phần trước.
Tại sao chúng ta lại cần những thay đổi trong Java?
Oracle Corporation đã giới thiệu nhiều khái niệm mới trong Java SE 8 nhằm mang lại những lợi ích sau:
- Tận dụng hiệu quả các CPU đa lõi hiện nay: Gần đây, phần cứng máy tính đã có những thay đổi đáng kể. Hầu hết các hệ thống hiện nay đều sử dụng CPU đa lõi (2, 4, 8, 16 lõi, v.v.) để triển khai và vận hành ứng dụng. Do đó, Java cần có những cấu trúc lập trình mới để khai thác tối đa sức mạnh của các bộ xử lý này, từ đó xây dựng các ứng dụng có tính tương tranhvà khả năng mở rộngcao.
- Tận dụng các tính năng của Lập trình hàm (Functional Programming – FP): Oracle đã đưa nhiều khái niệm của Lập trình hàm vào Java SE 8 để khai thác các ưu điểm của phương pháp này.
Các tính năng mới của Java SE 8 là gì?
- Biểu thức Lambda
- Functional Interfaces
- Stream API
- API Ngày và Giờ (Date and Time API)
- Default Method và Static Method trong Interface
- Spliterator
- Tham chiếu Phương thức và Hàm tạo (Method and Constructor References)
- Các cải tiến cho Collections API
- Các cải tiến cho Concurrency Utils
- Các cải tiến cho Fork/Join Framework
- Vòng lặp nội bộ (Internal Iteration)
- Parallel Array và Parallel Collection Operations (Các thao tác mảng và tập hợp song song)
- Optional
- Type Annotations và Repeatable Annotations
- Phản chiếu tham số phương thức (Method Parameter Reflection)
- Mã hóa và giải mã Base64
- Các cải tiến IO và NIO2
- Engine JavaScript Nashorn
- Các cải tiến cho
javac
- Các thay đổi trong JVM
- Các cấu hình nhỏ gọn (Compact ProJava 8 Compact Profiles (Profile thu gọn): compact1, compact2, compact3
- JDBC 4.2
- JAXP 1.6
- Java DB 10.10
- Networking
- Các thay đổi về bảo mật
Lợi ích của các tính năng mới trong Java SE 8 là gì?
Sau đây là những lợi ích từ các tính năng mới của Java SE 8:
- Code ngắn gọn và dễ đọc hơn
- Code dễ tái sử dụng hơn
- Code dễ kiểm thử và bảo trì hơn
- Code có tính tương tranh và khả năng mở rộng cao
- Viết code xử lý song song
- Viết các thao tác giống như trên cơ sở dữ liệu
- Hiệu suất ứng dụng tốt hơn
- Tăng năng suất lập trình
Biểu thức Lambda là gì?
Biểu thức Lambda là một hàm ẩn danh (anonymous function), nhận vào một tập hợp các tham số và trả về kết quả. Nó là một khối code không có tên, có thể có hoặc không có tham số và có thể có hoặc không có kết quả. Khối code này được thực thi khi cần.
Ba phần của một biểu thức Lambda là gì? Kiểu của biểu thức Lambda là gì?
Một biểu thức Lambda bao gồm 3 phần:
- Danh sách tham số (Parameter List): Có thể không có, có một hoặc nhiều tham số. Phần này là tùy chọn.
- Toán tử mũi tên Lambda (Lambda Arrow Operator): Ký hiệu
>
được dùng để phân tách danh sách tham số và phần thân. - Thân biểu thức Lambda (Lambda Expression Body)
Kiểu của “Journal Dev” là java.lang.String
. Kiểu của “true” là Boolean
. Vậy theo cách đó, kiểu của biểu thức Lambda là gì? Kiểu của một biểu thức Lambda là một Functional Interface.
Ví dụ, kiểu của biểu thức Lambda sau đây là gì?
() -> System.out.println("Executing Lambda...");
Biểu thức Lambda này không có tham số và không trả về kết quả. Vì vậy, kiểu của nó là java.lang.Runnable
, một Functional Interface.
Functional Interface là gì? SAM Interface là gì?
Một Functional Interface là một interface chỉ chứa duy nhất một phương thức trừu tượng (abstract method).
Functional Interface còn được gọi là SAM Interface (Single Abstract Method Interface) vì nó chỉ chứa một phương thức trừu tượng duy nhất. Java SE 8 API đã định nghĩa sẵn rất nhiều Functional Interface.
Có thể tự định nghĩa Functional Interface không? @FunctionalInterface
là gì? Các quy tắc để định nghĩa một Functional Interface là gì?
Có, chúng ta hoàn toàn có thể định nghĩa các Functional Interface của riêng mình. Chúng ta sử dụng annotation @FunctionalInterface
của Java 8 để đánh dấu một interface là Functional Interface.
Các quy tắc cần tuân theo như sau:
- Định nghĩa một interface với một phương thức trừu tượng duy nhất.
- Không được định nghĩa nhiều hơn một phương thức trừu tượng.
- Sử dụng annotation
@FunctionalInterface
trong định nghĩa interface. - Có thể định nghĩa bao nhiêu phương thức khác cũng được chẳng hạn như
default method
hoặcstatic method
. - Nếu chúng ta ghi đè một phương thức của lớp
java.lang.Object
thành một phương thức trừu tượng, nó sẽ không được tính là một phương thức trừu tượng.
Annotation @FunctionalInterface
có bắt buộc không? Tại sao cần Functional Interface?
Không, annotation @FunctionalInterface
không bắt buộc. Nếu không muốn, chúng ta có thể bỏ qua annotation này. Tuy nhiên, nếu chúng ta sử dụng nó trong định nghĩa Functional Interface, trình biên dịch Java sẽ bắt buộc interface đó chỉ được có duy nhất một phương thức trừu tượng. Điều này giúp tránh các lỗi vô tình thêm phương thức trừu tượng thứ hai vào interface.
Tại sao chúng ta cần Functional Interface? Vì kiểu của một biểu thức Lambda trong Java 8 chính là một Functional Interface. Bất cứ khi nào sử dụng biểu thức Lambda, tức là chúng ta đang làm việc với các Functional Interface.
Khi nào chúng ta nên sử dụng Stream API của Java 8? Tại sao chúng ta cần sử dụng Java Stream API trong các dự án của mình?
Việc sử dụng Java 8 Stream API sẽ mang lại rất nhiều lợi ích nếu dự án Java của chúng ta cần thực hiện các thao tác sau đây:
- Khi muốn thực hiện các thao tác giống như trên cơ sở dữ liệu (ví dụ:
groupby
,orderby
). - Khi muốn thực hiện các thao tác lười biếng (lazy).
- Khi muốn viết mã theo phong cách lập trình hàm (Functional Programming).
- Khi muốn thực hiện các thao tác song song (Parallel Operations).
- Khi muốn sử dụng phép duyệt trong (Internal Iteration).
- Khi muốn thực hiện các kỹ thuật chuỗi ống (Pipelining).
- Khi mong muốn đạt được hiệu suất xử lý tốt hơn.
Giải thích sự khác biệt giữa Collection API và Stream API?
# | Collection API | Stream API |
---|---|---|
1 | Có từ Java 1.2. | Được giới thiệu trong Java SE 8. |
2 | Dùng để lưu trữ dữ liệu (một tập hợp các đối tượng). | Dùng để tính toán trên dữ liệu (thực hiện tính toán trên một tập hợp các đối tượng). |
3 | Chúng ta có thể sử dụng cả Spliterator và Iterator để duyệt qua các phần tử. Ngoài ra, có thể dùng phương thức forEach để thực hiện một hành động đối với từng phần tử trong stream đó. |
Không thể dùng Iterator hay Spliterator trực tiếp. |
4 | Dùng để lưu trữ một số lượng phần tử không giới hạn. | Dùng để xử lý các phần tử từ một nguồn (ví dụ: một Collection). |
5 | Thường sử dụng khái niệm duyệt ngoài (External Iteration), ví dụ như dùng Iterator . |
Sử dụng khái niệm duyệt trong (Internal Iteration), ví dụ như dùng forEach . |
6 | Đối tượng Collection được khởi tạo sẵn (Eagerly). | Đối tượng Stream được khởi tạo một cách lười biếng. |
7 | Chúng ta thêm các phần tử vào Collection sau khi chúng được tính toán hoàn chỉnh. | Chúng ta có thể thêm phần tử vào đối tượng Stream mà không cần tính toán trước. Nghĩa là các đối tượng Stream được tính toán theo nhu cầu. |
8 | Chúng ta có thể duyệt và sử dụng các phần tử từ một Collection nhiều lần. | Chúng ta chỉ có thể duyệt và sử dụng các phần tử từ một Stream một lần duy nhất. |
Spliterator trong Java SE 8 là gì? Sự khác biệt giữa Iterator và Spliterator?
Spliterator là viết tắt của Splitable Iterator (Bộ lặp có thể phân tách). Đây là một tính năng mới được Tập đoàn Oracle giới thiệu trong Java SE 8. Giống như Iterator
và ListIterator
, Spliterator
cũng là một trong những giao diện của Iterator.
# | Spliterator | Iterator |
---|---|---|
1 | Được giới thiệu trong Java SE 8. | Có từ Java 1.2. |
2 | Là Iterator có thể phân tách. | Là Iterator không thể phân tách. |
3 | Được sử dụng trong Stream API. | Được sử dụng cho Collection API. |
4 | Sử dụng khái niệm duyệt trong. | Sử dụng khái niệm duyệt ngoài. |
5 | Có thể dùng để duyệt Stream theo cả thứ tự tuần tự (Sequential) và song song. | Chỉ có thể dùng để duyệt Collection theo thứ tự tuần tự. |
6 | Lấy được bằng cách gọi phương thức spliterator() trên một đối tượng Stream. |
Lấy được bằng cách gọi phương thức iterator() trên một đối tượng Collection. |
7 | Phương thức quan trọng: tryAdvance() |
Các phương thức quan trọng: next() , hasNext() |
Optional trong Java 8 là gì? Công dụng và lợi ích của Optional?
Optional là một lớp final
được giới thiệu trong Java SE 8 và được định nghĩa trong gói java.util
. Lớp này được dùng để biểu diễn các giá trị tùy chọn, tức là có thể tồn tại hoặc không tồn tại. Một đối tượng Optional
có thể chứa một giá trị hoặc không chứa gì cả. Nếu nó chứa giá trị, ta có thể truy xuất giá trị đó; nếu không, ta sẽ không nhận được gì.
Optional
được xem như một tập hợp có giới hạn (bounded collection) , tức là nó chứa tối đa một phần tử. Đây là một giải pháp thay thế cho việc sử dụng giá trị null
.
Ưu điểm chính của Optional:
- Giúp tránh việc kiểm tra null trong mã nguồn.
- Giúp tránh ngoại lệ NullPointerException (lỗi tham chiếu đến đối tượng null).
Suy luận kiểu là gì? Nó có sẵn trong các phiên bản Java cũ như Java 7 trở về trước không, hay chỉ có trong Java SE 8?
Suy luận kiểu có nghĩa là trình biên dịch tự xác định Kiểu (Type) tại thời điểm biên dịch.
Đây không phải là một tính năng hoàn toàn mới trong Java SE 8. Nó đã có mặt trong Java 7 và cả các phiên bản trước đó.
- Trước Java 7: Chúng ta có thể cùng khám phá mảng trong Java. Khai báo một mảng kiểu
String
với các giá trị như minh họa dưới đây:String str[] = { "Java 7", "Java 8", "Java 9" };
Ở đây, chúng ta đã gán các giá trị chuỗi ở vế phải nhưng không định nghĩa kiểu của chúng. Trình biên dịch Java tự động suy luận kiểu và tạo ra một mảng
String
. - Java 7: Oracle đã giới thiệu tính năng mới được gọi là toán tử kim cương (Diamond Operator) trong Java SE 7 để tránh định nghĩa kiểu không cần thiết trong Generics.
Map<String,List<Customer>> customerInfoByCity = new HashMap<>();
Ở đây, chúng ta không cần định nghĩa lại thông tin kiểu ở vế phải, chỉ cần dùng toán tử kim cương
<>
. - Java SE 8: Oracle đã tăng cường khái niệm Suy luận kiểu trong Java SE 8. Chúng ta sử dụng nó để định nghĩa các biểu thức Lambda, hàm, tham chiếu phương thức, v.v.
ToIntBiFunction<Integer,Integer> add = (a,b) -> a + b;
Ở đây, trình biên dịch Java quan sát định nghĩa kiểu ở vế trái (
ToIntBiFunction<Integer,Integer>
) và tự động xác định kiểu của các tham sốa
vàb
trong biểu thức Lambda làInteger
.
Trên đây là các câu trả lời về các câu hỏi phỏng vấn liên quan đến Java SE 8. Chúng ta sẽ tiếp tục thảo luận thêm về chủ đề này ở những bài viết tiếp theo. Nếu bạn thấy bài viết hữu ích hoặc có bất kỳ góp ý/thắc mắc nào, vui lòng để lại bình luận bên dưới.