- Sản phẩm & Dịch vụSản phẩm & Dịch vụ
- Giải phápGiải pháp
- Bảng giáBảng giá
- Công tyCông ty
- Tài liệuTài liệu
vi
vi
Chuyên mục với các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.
Technical Writer
I have over 5 years of experience writing technical documentation for tech products, making them accessible and user-friendly. My focus is always on providing clear and precise information. @#@ Tôi đã có hơn 5 năm kinh nghiệm viết tài liệu kỹ thuật cho các sản phẩm công nghệ, giúp người dùng dễ dàng tiếp cận và sử dụng. Tôi luôn tập trung vào việc cung cấp thông tin chính xác và dễ hiểu.
Thảo luận (0)
Đăng nhập để thảo luận
Nếu bạn đã theo dõi các bài viết trước của tôi về các câu hỏi phỏng vấn Java SE 8, hẳn bạn đã nắm được những khái niệm cơ bản.

Hôm nay, chúng ta sẽ cùng đi sâu vào các khía cạnh quan trọng khác của Java SE 8, từ khái niệm Internal Iteration mạnh mẽ, những cải tiến đột phá trong Interface với Default và Static Methods, đến sự thay đổi lớn trong API Xử lý Ngày và Giờ, và cả cách Java 8 giải quyết vấn đề kế thừa đa hình (Multiple Inheritance) phức tạp.
Trước Java 8, chúng ta quen thuộc với khái niệm External Iteration, nơi chúng ta tự kiểm soát vòng lặp (ví dụ: for-each loop) để duyệt qua các phần tử của một đối tượng tập hợp như Collections hay Arrays. Tuy nhiên, Java 8 đã giới thiệu một tính năng đột phá: Internal Iteration
Internal Iteration có nghĩa là Java API tự mình duyệt qua từng phần tử của một đối tượng tập hợp một cách nội bộ. Thay vì ứng dụng của chúng ta tự điều khiển quá trình lặp từng phần tử, chúng ta yêu cầu Java API thực hiện công việc này một cách nội bộ. Điều này mở ra cánh cửa cho phong cách lập trình khai báo (Declarative Programming) và tận dụng hiệu quả khả năng xử lý song song.
| Số TT | External Iteration | Internal Iteration |
|---|---|---|
| 1 | Có sẵn trước cả Java 8. | Được giới thiệu trong Java SE 8. |
| 2 | Duyệt các phần tử của đối tượng tập hợp từ bên ngoài. | Duyệt các phần tử của đối tượng tập hợp từ bên trong. |
| 3 | Duyệt phần tử bằng vòng lặp for-each và các Iterator như Enumeration, Iterator, ListIterator. | Duyệt phần tử bằng API của Java như phương thức forEach. |
| 4 | Chỉ duyệt phần tử theo thứ tự tuần tự (Sequential) và theo thứ tự (In-order). | Không bắt buộc phải duyệt theo thứ tự tuần tự. |
| 5 | Theo hướng tiếp cận Lập trình Hướng đối tượng (OOP) – kiểu Imperative (chỉ định rõ cách làm). | Theo hướng tiếp cận Lập trình Hàm (Functional Programming) – kiểu Declarative (chỉ nói cần làm gì). |
| 6 | Không tách biệt rõ ràng trách nhiệm: vừa định nghĩa cái gì cần làm vừa định nghĩa cách làm như thế nào. | Chỉ định nghĩa cái gì cần làm, không cần quan tâm làm thế nào. Java API sẽ xử lý phần “cách làm”. |
| 7 | Code khó đọc hơn. | Code dễ đọc hơn. |
External Iteration, dù quen thuộc, lại có những nhược điểm đáng kể:
Ngược lại, Internal Iteration mang lại nhiều lợi ích vượt trội:
Tuy nhiên, không có gì là hoàn hảo. Internal Iteration cũng có một nhược điểm lớn:
Chính vì thế, External Iteration vẫn giữ một lợi thế quan trọng:
Chúng ta cần hiểu rõ các tình huống để lựa chọn loại lặp phù hợp:
| Số TT | Stream Intermediate Operations | Stream Terminal Operations |
|---|---|---|
| 1 | Các toán tử trung gian không được thực thi cho đến khi chúng được nối với một toán tử cuối. | Các toán tử cuối được thực thi độc lập, không cần sự hỗ trợ từ các toán tử khác. |
| 2 | Kết quả của toán tử trung gian là một Stream khác. | Kết quả của toán tử cuối không phải là Stream, mà là một giá trị/đối tượng khác. |
| 3 | Toán tử trung gian được thực thi theo kiểu Lười biếng (Lazy Evaluation). | Toán tử cuối được thực thi theo kiểu Tức thì (Eager Evaluation). |
| 4 | Có thể nối (chain) nhiều toán tử trung gian trong một Stream. | Không thể nối thêm toán tử cuối. |
| 5 | Có thể dùng nhiều toán tử trung gian trong một câu lệnh. | Chỉ có thể dùng một toán tử cuối trong một câu lệnh. |
Trước Java 7, việc triển khai phương thức trong Interface là điều không thể – Interface chỉ chứa các khai báo phương thức abstract và các hằng số. Nhưng Java SE 8 đã thay đổi điều này, mang đến hai khái niệm mới cho phép chúng ta cung cấp triển khai phương thức ngay trong Interface:
Một Default Method là một phương thức được triển khai trực tiếp trong Interface bằng từ khóa default. Đây là một tính năng mới được giới thiệu trong Java SE 8.
Chúng ta cần Default Method vì những lý do sau:
Interface mà không làm hỏng các lớp đã triển khai Interface đó. Các lớp này không bắt buộc phải triển khai phương thức default mới.Một Static Method trong Interface là một phương thức tiện ích (Utility method) hoặc phương thức trợ giúp (Helper method), gắn liền với Interface chứ không phải bất kỳ đối tượng nào của Interface đó. Chúng ta gọi chúng trực tiếp thông qua tên Interface.
Chúng ta cần Static Methods vì những lý do sau:
Interface vào cùng một nơi, thay vì phải tạo một lớp tiện ích riêng biệt (ví dụ: Collections, Arrays cho các phiên bản Java cũ hơn).Interface đó.Lập trình hàm (Functional Programming – FP) và Lập trình hướng đối tượng (Object-Oriented Programming – OOP) là hai mô hình lập trình chủ đạo, với những triết lý khác nhau:
Java 8 đã tích hợp nhiều tính năng của lập trình hàm như Lambda Expressions và Stream API, cho phép chúng ta viết code khai báo và tận dụng lợi thế của lập trình song song một cách dễ dàng hơn, mặc dù bản chất Java vẫn là một ngôn ngữ OOP.
Old Java Date API (trước Java SE 8) bao gồm các lớp như java.util.Date, java.util.Calendar, java.text.SimpleDateFormat… API này có nhiều vấn đề và nhược điểm:
java.util.Date là mutable (có thể thay đổi) và không an toàn cho luồng (not Thread-Safe), dẫn đến các lỗi tiềm ẩn trong môi trường đa luồng.java.text.SimpleDateFormat cũng không an toàn cho luồng, gây ra vấn đề khi định dạng ngày trong môi trường đồng thời.Java SE 8’s Date and Time API (thuộc gói java.time) đã khắc phục những vấn đề này, mang lại nhiều lợi ích vượt trội:
Chúng ta cần Java 8’s Date and Time API để phát triển các ứng dụng Java có hiệu năng cao, an toàn cho luồng (Thread-Safe) và có khả năng mở rộng .
Java 8’s Date and Time API giải quyết tất cả các vấn đề của Old Java Date API bằng cách tuân thủ các nguyên tắc bất biến và an toàn luồng . Các lớp như LocalDate, LocalTime, LocalDateTime, ZonedDateTime đều là bất biến. Khi bạn thực hiện một phép toán (ví dụ: cộng thêm ngày), thay vì thay đổi đối tượng hiện có, chúng sẽ trả về một đối tượng mới, đảm bảo tính an toàn cho luồng và đơn giản hóa việc lập trình.
| Số TT | API Date cũ của Java | API Date and Time trong Java 8 |
|---|---|---|
| 1 | Có sẵn trước cả Java 8. | Được giới thiệu trong Java SE 8. |
| 2 | Không an toàn trong môi trường đa luồng (Not Thread Safe). | An toàn trong môi trường đa luồng (Thread Safe). |
| 3 | API có thể thay đổi (Mutable). | API bất biến (Immutable). |
| 4 | Hiệu năng kém. | Hiệu năng tốt hơn. |
| 5 | Khó đọc, ít rõ ràng. | Dễ đọc, rõ ràng hơn. |
| 6 | Không được khuyến khích sử dụng vì đã bị lỗi thời (deprecated). | Luôn được khuyến khích sử dụng. |
| 7 | Không thể mở rộng. | Dễ mở rộng. |
| 8 | Giá trị tháng được định nghĩa từ 0 đến 11, nghĩa là Tháng 1 = 0. | Giá trị tháng được định nghĩa từ 1 đến 12, nghĩa là Tháng 1 = 1. |
| 9 | Là API cũ. | Là API mới. |
Kế thừa đa hình (Multiple Inheritance) có nghĩa là một lớp có thể kế thừa hoặc mở rộng các đặc tính và tính năng từ nhiều hơn một lớp cha.
Trong Java 7 hoặc các phiên bản trước, Kế thừa đa hình từ các lớp cụ thể là không thể, vì Java tuân theo quy tắc “một lớp chỉ có thể kế thừa một và chỉ một lớp hoặc lớp trừu tượng (abstract class)”. Tuy nhiên, việc cung cấp Đa triển khai (Multiple Implementation) thông qua Interface luôn được hỗ trợ, vì Java cho phép một lớp triển khai bất kỳ số lượng Interface nào.
Với việc giới thiệu Default Methods trong Interface ở Java 8, Java đã gián tiếp hỗ trợ một dạng Kế thừa đa hình thông qua triển khai phương thức trong Interface. Điều này có nghĩa là, một lớp có thể kế thừa các triển khai mặc định từ nhiều Interface khác nhau.
default là gì? Java 8 giải quyết vấn đề này như thế nào?Diamond Problem phát sinh khi một lớp triển khai nhiều Interface, và các Interface đó lại có cùng một phương thức default.

Hãy xem ví dụ sau:
interface A {
default void display() {
System.out.println("A");
}
}
interface B extends A {
default void display() {
System.out.println("B");
}
}
interface C extends A {
default void display() {
System.out.println("C");
}
}
class D implements B, C {
}
Trong đoạn mã trên, lớp D sẽ báo lỗi biên dịch: “Duplicate default methods named display with the parameters () and () are inherited from the types C and B”. Điều này xảy ra vì trình biên dịch Java sẽ bối rối không biết nên sử dụng phiên bản display() nào cho lớp D, do D kế thừa phương thức display() từ cả Interface B và C.
Để giải quyết vấn đề này, Java 8 đã cung cấp giải pháp như sau:
class D implements B, C {
@Override
public void display() {
B.super.display(); // Explicitly call default method from interface B
}
}
Với B.super.display();, chúng ta chỉ định rõ ràng phiên bản phương thức default mà chúng ta muốn sử dụng. Nếu bạn muốn sử dụng phương thức default của Interface C, bạn chỉ cần thay đổi thành C.super.display();
Chúng ta đã cùng nhau khám phá sâu hơn về Java SE 8, đặc biệt là những thay đổi quan trọng trong cách chúng ta xử lý việc lặp dữ liệu với Internal Iteration, sức mạnh và tính linh hoạt mà Default và Static Methods mang lại cho Interface, cải tiến vượt bậc của Date and Time API, và cách Java 8 khéo léo giải quyết vấn đề Diamond Problem trong kế thừa đa hình.
Hãy tiếp tục thực hành, áp dụng chúng vào các dự án thực tế để củng cố kiến thức. Java vẫn đang không ngừng phát triển, và việc liên tục cập nhật là chìa khóa để trở thành một chuyên gia.