Sự phát triển của ngôn ngữ Java (Phần 1)
Bạn đã bao giờ tự hỏi ngôn ngữ Java đã phát triển như thế nào trong những năm qua chưa? Trong bài viết này, chúng ta sẽ cùng nhau khám phá những tính năng chính mà Java đã mang lại qua các phiên bản, giúp cuộc sống của lập trình viên trở nên dễ dàng hơn, cho phép viết mã nguồn sạch hơn, ngắn gọn và đơn giản hơn.
Bắt đầu với bước nhảy vọt đầu tiên: Từ Java 4 đến Java 5/6
Tôi đã có cơ hội làm việc với Java 4 trong khi Java 5/6 đã xuất hiện. Thời điểm đó, không có sự chuyển đổi tự động giữa các kiểu dữ liệu nguyên thủy và kiểu đối tượng.
java
List lista = new ArrayList();
// Thêm số: cần tạo đối tượng một cách rõ ràng
lista.add(new Integer(10));
lista.add(new Integer(20));
// Lấy số: cần ép kiểu + gọi intValue()
Integer obj = (Integer) lista.get(0);
int valor = obj.intValue();
Từ Java 5 trở đi, cuộc sống của chúng ta đã cải thiện rất nhiều với Autoboxing/Unboxing và Generics.
java
List<Integer> nums = new ArrayList<>();
nums.add(10); // autoboxing
int x = nums.get(0); // unboxing
Generics giúp loại bỏ việc ép kiểu thủ công và làm cho mã nguồn an toàn hơn.
Bên cạnh đó, chúng ta cũng đã được giới thiệu về Enum, Varargs, Annotations, for-each và Static import.
Trong hạng mục chất lượng mã nguồn, tính năng nổi bật nhất của Java 6 là "Pluggable Annotation Processing Tool (APT)". Nó đã mở đường cho các framework như Lombok và JPA tự động tạo mã nguồn.
Java 7: Một phiên bản không thể quên
Chúng ta đã nhận được "Diamond Operator (<>)", "Multi-catch" và "Try-with-resources (ARM – Automatic Resource Management)".
Diamond Operator (<>) cho phép chúng ta tránh lặp lại generics khi tạo đối tượng.
java
// Trước đây
Map<String, List<Integer>> mapa = new HashMap<String, List<Integer>>();
// Java 7
Map<String, List<Integer>> mapa = new HashMap<>();
Multi-catch cho phép xử lý nhiều ngoại lệ khác nhau trong một khối duy nhất.
java
try {
metodoArriscado();
} catch (IOException | SQLException e) {
e.printStackTrace();
}
Try-with-resources giúp đóng tự động các tài nguyên thực hiện AutoCloseable. (Ai chưa từng quên đóng một file?)
java
// Trước đây
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("dados.txt"));
System.out.println(br.readLine());
} finally {
if (br != null) br.close();
}
// Java 7
try (BufferedReader br = new BufferedReader(new FileReader("dados.txt"))) {
System.out.println(br.readLine());
} // tự động đóng
Java 7 cũng đã mang lại tính năng sử dụng Strings trong switch (cuối cùng!).
Bước nhảy vọt thứ hai: Java 8
Tại bước nhảy này, những điểm nổi bật nhất là "Lambda", "Streams API", "Optional" và "java.time". Chúng ta cũng đã có Default Methods trong Interfaces — một tính năng hữu ích để phát triển APIs mà không làm hỏng khả năng tương thích, mặc dù việc sử dụng nó vẫn gây tranh cãi giữa các lập trình viên.
Chúng ta bắt đầu với "java.time". Được lấy cảm hứng từ Joda-Time, tính năng này ra đời để giải quyết vấn đề lịch sử của các lớp ngày tháng cũ.
Bạn đã bao giờ cố gắng cộng thêm X ngày vào một Date chưa?
java
import java.util.Calendar;
import java.util.Date;
public class ExemploAntesJava8 {
public static void main(String[] args) {
// Ngày hiện tại
Date hoje = new Date();
System.out.println("Hôm nay: " + hoje);
// Sử dụng Calendar để cộng 18 ngày
Calendar cal = Calendar.getInstance();
cal.setTime(hoje);
cal.add(Calendar.DAY_OF_MONTH, 18);
Date daqui18dias = cal.getTime();
System.out.println("Sau 18 ngày: " + daqui18dias);
}
}
Mỗi lần sử dụng Calendar, tôi phải nhớ rằng tháng được đánh số từ 0 đến 11, không phải từ 1 đến 12.
java
import java.util.Calendar;
import java.util.Date;
public class ExemploAntesJava8 {
public static void main(String[] args) {
// Lưu ý điều này!
Calendar cal2 = Calendar.getInstance();
cal2.set(2025, Calendar.SEPTEMBER, 19); // Tháng Chín = 8
}
}
java.time mang đến một mô hình hiện đại, bất biến và rõ ràng hơn với các lớp bất biến và an toàn với thread.
java
import java.time.LocalDate;
public class ExemploComJavaTime {
public static void main(String[] args) {
// Ngày hiện tại
LocalDate hoje = LocalDate.now();
System.out.println("Hôm nay: " + hoje);
// Cộng 18 ngày
LocalDate daqui18dias = hoje.plusDays(18);
System.out.println("Sau 18 ngày: " + daqui18dias);
}
}
Streams API đã thay đổi cách chúng ta thực hiện vòng lặp trong Java.
java
// Trước đây
List<String> nomes = Arrays.asList("Ana", "Bruno", "Alberto", "Carla");
int cont = 0;
for (String n : nomes) {
if (n.startsWith("A")) cont++;
}
// Java 8
long cont = nomes.stream()
.filter(n -> n.startsWith("A"))
.count();
Optional xuất hiện để xử lý một cách an toàn các giá trị có thể bị thiếu.
java
// Trước đây (kiểm tra null theo chuỗi)
String cidade = null;
if (usuario != null && usuario.getEndereco() != null) {
cidade = usuario.getEndereco().getCidade();
}
// Java 8 (Optional)
String cidade = Optional.ofNullable(usuario)
.map(Usuario::getEndereco)
.map(Endereco::getCidade)
.orElse("KHÔNG BIẾT");
Cuối cùng, Lambda! Trong quá khứ, không có lambdas, chúng ta cần các lớp ẩn danh để triển khai các hành vi đơn giản.
java
List<String> nomes = Arrays.asList("Carlos", "Ana", "Bruno");
// Trước đây
Collections.sort(nomes, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Sử dụng Java 8 (lambda)
nomes.sort((a, b) -> a.compareTo(b));
// Còn ngắn hơn với method reference
nomes.sort(String::compareTo);
Kết luận
Bạn đã thích bài viết này chưa? Hãy theo dõi tôi để không bỏ lỡ phần tiếp theo, nơi chúng ta sẽ khám phá những gì đã xuất hiện từ Java 9 trở đi. Hãy theo dõi để cập nhật nhé!