JavaRush /Blog Java /Random-VI /Nghỉ giải lao #155. 10 hàm hàng đầu trong Java

Nghỉ giải lao #155. 10 hàm hàng đầu trong Java

Xuất bản trong nhóm

10 hàm hàng đầu trong Java

Nguồn: DZone Bài viết này liệt kê 10 tính năng lập trình Java thường được các nhà phát triển sử dụng trong công việc hàng ngày của họ. Nghỉ giải lao #155.  10 hàm hàng đầu trong Java - 1

1. Phương pháp thu gom tại nhà máy

Bộ sưu tập là một trong những tính năng được sử dụng phổ biến nhất trong lập trình. Chúng được sử dụng như một vật chứa để chúng ta lưu trữ đồ vật và chuyển chúng đi. Các bộ sưu tập cũng được sử dụng để sắp xếp, tìm kiếm và lặp lại các đối tượng, giúp công việc của lập trình viên trở nên dễ dàng hơn nhiều. Chúng có một số giao diện cơ bản như Danh sách, Bộ, Bản đồ, cũng như một số cách triển khai. Cách tạo Bộ sưu tập và Bản đồ truyền thống có vẻ dài dòng đối với nhiều nhà phát triển. Đó là lý do tại sao Java 9 đã giới thiệu một số phương thức xuất xưởng ngắn gọn. Danh sách :
List countries = List.of("Bangladesh", "Canada", "United States", "Tuvalu");
Bộ :
Set countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
Bản đồ :
Map countriesByPopulation = Map.of("Bangladesh", 164_689_383,
                                                            "Canada", 37_742_154,
                                                            "United States", 331_002_651,
                                                            "Tuvalu", 11_792);
Factory Method rất hữu ích khi chúng ta muốn tạo ra các container bất biến. Nhưng nếu bạn định tạo các bộ sưu tập có thể thay đổi, bạn nên sử dụng phương pháp truyền thống.

2. Suy luận kiểu cục bộ

Java 10 đã thêm suy luận kiểu cho các biến cục bộ. Trước đó, các nhà phát triển phải chỉ định loại hai lần khi khai báo và khởi tạo một đối tượng. Nó rất mệt mỏi. Hãy xem ví dụ sau:
Map> properties = new HashMap<>();
Loại thông tin của cả hai bên được chỉ định ở đây. Nếu chúng ta định nghĩa nó ở một nơi thì trình đọc mã và trình biên dịch Java sẽ dễ dàng hiểu rằng nó phải là loại Map. Suy luận kiểu cục bộ thực hiện điều đó. Đây là một ví dụ:
var properties = new HashMap>();
Bây giờ mọi thứ chỉ được viết một lần và mã trông không tệ hơn nhiều. Và khi chúng ta gọi một phương thức và lưu trữ kết quả vào một biến, mã càng trở nên ngắn hơn. Ví dụ:
var properties = getProperties();
Và xa hơn:
var countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
Mặc dù suy luận kiểu cục bộ có vẻ như là một tính năng tiện lợi nhưng một số người lại chỉ trích nó. Một số nhà phát triển cho rằng điều này làm giảm khả năng đọc. Và điều này quan trọng hơn sự ngắn gọn.

3. Biểu thức chuyển đổi nâng cao

Câu lệnh switch truyền thống đã có trong Java ngay từ đầu và gợi nhớ đến C và C++ hồi đó. Điều này không sao, nhưng khi ngôn ngữ phát triển, toán tử này không cung cấp cho chúng tôi bất kỳ cải tiến nào cho đến Java 14. Tất nhiên, nó có một số nhược điểm. Nổi tiếng nhất là lỗi thất bại : Để giải quyết vấn đề này, các nhà phát triển đã sử dụng các câu lệnh break, phần lớn là mã soạn sẵn. Tuy nhiên, Java 14 đã giới thiệu một phiên bản cải tiến của câu lệnh switch với danh sách hàm lớn hơn nhiều. Bây giờ chúng ta không cần thêm câu lệnh break nữa và điều này đã giải quyết được vấn đề lỗi. Ngoài ra, câu lệnh switch có thể trả về một giá trị, nghĩa là chúng ta có thể sử dụng nó làm biểu thức và gán nó cho một biến.
int day = 5;
String result = switch (day) {
    case 1, 2, 3, 4, 5 -> "Weekday";
    case 6, 7 -> "Weekend";
    default -> "Unexpected value: " + day;
};

4. Hồ sơ

Mặc dù Records là một tính năng tương đối mới được giới thiệu trong Java 16 nhưng nhiều nhà phát triển thấy nó rất hữu ích, chủ yếu là do việc tạo ra các đối tượng bất biến. Thông thường chúng ta cần các đối tượng dữ liệu trong chương trình của mình để lưu trữ hoặc truyền các giá trị từ phương thức này sang phương thức khác. Ví dụ: một lớp để truyền tọa độ x, y và z, chúng ta sẽ viết như sau:
package ca.bazlur.playground;

import java.util.Objects;

public final class Point {
    private final int x;
    private final int y;
    private final int z;

    public Point(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public int x() {
        return x;
    }

    public int y() {
        return y;
    }

    public int z() {
        return z;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj == null || obj.getClass() != this.getClass()) return false;
        var that = (Point) obj;
        return this.x == that.x &&
                this.y == that.y &&
                this.z == that.z;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y, z);
    }

    @Override
    public String toString() {
        return "Point[" +
                "x=" + x + ", " +
                "y=" + y + ", " +
                "z=" + z + ']';
    }

}
Lớp học có vẻ dài dòng quá. Với sự trợ giúp của các mục nhập, tất cả mã này có thể được thay thế bằng một phiên bản ngắn gọn hơn:
package ca.bazlur.playground;

public record Point(int x, int y, int z) {
}

5.Tùy chọn

Một phương thức là một hợp đồng trong đó chúng ta xác định các điều kiện. Chúng tôi chỉ định các tham số với loại của chúng, cũng như loại trả về. Sau đó, chúng tôi mong đợi rằng khi phương thức được gọi, nó sẽ hoạt động theo hợp đồng. Tuy nhiên, chúng ta thường nhận được null từ một phương thức thay vì giá trị của loại đã chỉ định. Đây là sai lầm. Để giải quyết vấn đề này, trình khởi tạo thường kiểm tra giá trị bằng điều kiện if, bất kể giá trị đó có rỗng hay không. Ví dụ:
public class Playground {

    public static void main(String[] args) {
        String name = findName();
        if (name != null) {
            System.out.println("Length of the name : " + name.length());
        }
    }

    public static String findName() {
        return null;
    }
}
Nhìn vào đoạn mã trên. Phương thức findName lẽ ra sẽ trả về một String , nhưng nó lại trả về null. Người khởi xướng bây giờ phải kiểm tra các giá trị rỗng trước để giải quyết vấn đề. Nếu người khởi tạo quên làm điều này thì cuối cùng chúng ta sẽ nhận được NullPointerException . Mặt khác, nếu chữ ký phương thức chỉ ra khả năng không trả lại, thì điều này sẽ giải quyết được mọi nhầm lẫn. Và đây là lúc Tùy chọn có thể giúp chúng tôi .
import java.util.Optional;

public class Playground {

    public static void main(String[] args) {
        Optional optionalName = findName();
        optionalName.ifPresent(name -> {
            System.out.println("Length of the name : " + name.length());
        });
    }

    public static Optional findName() {
        return Optional.empty();
    }
}
Ở đây chúng tôi đã viết lại phương thức findName với tùy chọn Tùy chọn để không trả về bất kỳ giá trị nào. Điều này cảnh báo trước cho các lập trình viên và khắc phục sự cố.

6. API ngày giờ Java

Mọi nhà phát triển đều bối rối ở mức độ này hay mức độ khác với việc tính toán ngày và giờ. Đây không phải là một cường điệu. Điều này chủ yếu là do thiếu API Java tốt để làm việc với ngày và giờ. Bây giờ vấn đề này không còn phù hợp nữa, vì Java 8 đã giới thiệu một bộ API tuyệt vời trong gói java.time, giải quyết mọi vấn đề liên quan đến ngày và giờ. Gói java.time có nhiều giao diện và lớp giúp loại bỏ hầu hết các vấn đề, bao gồm cả múi giờ. Các lớp được sử dụng phổ biến nhất trong gói này là:
  • Ngày địa phương
  • Giờ địa phương
  • Ngày giờ địa phương
  • Khoảng thời gian
  • Giai đoạn
  • Ngày giờ được khoanh vùng
Một ví dụ về việc sử dụng các lớp từ gói java.time:
import java.time.LocalDate;
import java.time.Month;

public class Playground3 {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2022, Month.APRIL, 4);
        System.out.println("year = " + date.getYear());
        System.out.println("month = " + date.getMonth());
        System.out.println("DayOfMonth = " + date.getDayOfMonth());
        System.out.println("DayOfWeek = " + date.getDayOfWeek());
        System.out.println("isLeapYear = " + date.isLeapYear());
    }
}
Một ví dụ về việc sử dụng lớp LocalTime để tính thời gian:
LocalTime time = LocalTime.of(20, 30);
int hour = time.getHour();
int minute = time.getMinute();
time = time.withSecond(6);
time = time.plusMinutes(3);
Thêm múi giờ:
ZoneId zone = ZoneId.of("Canada/Eastern");
LocalDate localDate = LocalDate.of(2022, Month.APRIL, 4);
ZonedDateTime zonedDateTime = date.atStartOfDay(zone);

7.NullPointerException

Mọi nhà phát triển đều ghét NullPointerException. Điều này có thể đặc biệt khó khăn khi StackTrace không cung cấp thông tin hữu ích về vấn đề chính xác là gì. Để chứng minh điều này, chúng ta hãy xem mã mẫu:
package com.bazlur;

public class Main {

    public static void main(String[] args) {
        User user = null;
        getLengthOfUsersName(user);
    }

    public static void getLengthOfUsersName(User user) {
        System.out.println("Length of first name: " + user.getName().getFirstName());
    }
}

class User {
    private Name name;
    private String email;

    public User(Name name, String email) {
        this.name = name;
        this.email = email;
    }

   //getter
   //setter
}

class Name {
    private String firstName;
    private String lastName;

    public Name(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

   //getter
   //setter
}
Hãy nhìn vào phương pháp cơ bản trong đoạn văn này. Chúng tôi thấy rằng NullPointerException sẽ được ném ra tiếp theo . Nếu chúng tôi chạy và biên dịch mã trong một phiên bản trước Java 14, chúng tôi sẽ nhận được StackTrace sau:
Exception in thread "main" java.lang.NullPointerException
at com.bazlur.Main.getLengthOfUsersName(Main.java:11)
at com.bazlur.Main.main(Main.java:7)
Có rất ít thông tin ở đây về vị trí và lý do ngoại lệ NullPointerException xảy ra . Nhưng trong Java 14 và các phiên bản mới hơn, chúng tôi nhận được nhiều thông tin hơn trong StackTrace, rất tiện lợi. Trong Java 14 chúng ta sẽ thấy:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "ca.bazlur.playground.User.getName()" because "user" is null
at ca.bazlur.playground.Main.getLengthOfUsersName(Main.java:12)
at ca.bazlur.playground.Main.main(Main.java:8)

8. Tương lai hoàn chỉnh

Chúng ta viết chương trình từng dòng một và chúng thường được thực thi từng dòng một. Nhưng đôi khi chúng ta cần thực thi song song để chương trình nhanh hơn. Đối với điều này, chúng tôi thường sử dụng Java Thread. Lập trình luồng Java không phải lúc nào cũng là lập trình song song. Thay vào đó, nó cho chúng ta khả năng soạn thảo một số mô-đun chương trình độc lập sẽ thực thi độc lập và thậm chí thường không đồng bộ. Tuy nhiên, lập trình thread khá khó khăn, đặc biệt đối với người mới bắt đầu. Đây là lý do tại sao Java 8 cung cấp một API đơn giản hơn cho phép bạn thực thi một phần chương trình một cách không đồng bộ. Hãy xem một ví dụ. Giả sử chúng ta cần gọi ba API REST và sau đó kết hợp các kết quả. Chúng ta có thể gọi từng cái một. Nếu mỗi trong số chúng mất khoảng 200 mili giây thì tổng thời gian để nhận chúng sẽ mất 600 mili giây. Điều gì sẽ xảy ra nếu chúng ta có thể chạy chúng song song? Vì các bộ xử lý hiện đại thường có nhiều lõi nên chúng có thể dễ dàng xử lý ba lệnh gọi còn lại trên ba bộ xử lý khác nhau. Sử dụng CompleteableFuture chúng ta có thể thực hiện việc này một cách dễ dàng.
package ca.bazlur.playground;

import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class SocialMediaService {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        var service = new SocialMediaService();

        var start = Instant.now();
        var posts = service.fetchAllPost().get();
        var duration = Duration.between(start, Instant.now());

        System.out.println("Total time taken: " + duration.toMillis());
    }

    public CompletableFuture> fetchAllPost() {
        var facebook = CompletableFuture.supplyAsync(this::fetchPostFromFacebook);
        var linkedIn = CompletableFuture.supplyAsync(this::fetchPostFromLinkedIn);
        var twitter = CompletableFuture.supplyAsync(this::fetchPostFromTwitter);

        var futures = List.of(facebook, linkedIn, twitter);

        return CompletableFuture.allOf(futures.toArray(futures.toArray(new CompletableFuture[0])))
                .thenApply(future -> futures.stream()
                        .map(CompletableFuture::join)
                        .toList());
    }
    private String fetchPostFromTwitter() {
        sleep(200);
        return "Twitter";
    }

    private String fetchPostFromLinkedIn() {
        sleep(200);
        return "LinkedIn";
    }

    private String fetchPostFromFacebook() {
        sleep(200);
        return "Facebook";
    }

    private void sleep(int millis) {
        try {
            TimeUnit.MILLISECONDS.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

9. Biểu thức Lambda

Biểu thức Lambda có lẽ là tính năng mạnh mẽ nhất của ngôn ngữ Java. Họ đã thay đổi cách chúng ta viết mã. Biểu thức lambda giống như một hàm ẩn danh có thể nhận đối số và trả về một giá trị. Chúng ta có thể gán một hàm cho một biến và chuyển nó làm đối số cho một phương thức và phương thức đó có thể trả về biến đó. Anh ấy có một cơ thể. Sự khác biệt duy nhất so với phương pháp này là không có tên. Biểu thức ngắn gọn và súc tích. Chúng thường không chứa nhiều mã soạn sẵn. Hãy xem một ví dụ trong đó chúng ta cần liệt kê tất cả các tệp trong một thư mục có phần mở rộng .java.
var directory = new File("./src/main/java/ca/bazlur/playground");
String[] list = directory.list(new FilenameFilter() {
    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(".java");
    }
});
Nếu bạn xem kỹ đoạn mã này, chúng ta sẽ chuyển lớp bên trong ẩn danh list () cho phương thức . Và trong lớp bên trong, chúng tôi đã đặt logic để lọc các tệp. Về cơ bản, chúng ta quan tâm đến phần logic này chứ không phải mô hình xung quanh logic. Biểu thức lambda cho phép chúng tôi xóa toàn bộ mẫu và chúng tôi có thể viết mã mà chúng tôi quan tâm. Đây là một ví dụ:
var directory = new File("./src/main/java/ca/bazlur/playground");
String[] list = directory.list((dir, name) -> name.endsWith(".java"));
Tất nhiên, đây chỉ là một ví dụ; biểu thức lambda còn có nhiều lợi ích khác.

10. API truyền phát

Trong công việc hàng ngày của chúng tôi, một trong những nhiệm vụ phổ biến là xử lý tập dữ liệu. Nó có một số hoạt động phổ biến như lọc, chuyển đổi và thu thập kết quả. Trước Java 8, các hoạt động như vậy về bản chất là bắt buộc. Chúng tôi phải viết mã cho mục đích của mình (tức là những gì chúng tôi muốn đạt được) và cách chúng tôi muốn thực hiện điều đó. Với việc phát minh ra biểu thức lambda và API Stream, giờ đây chúng ta có thể viết các hàm xử lý dữ liệu theo cách khai báo. Chúng tôi chỉ cho biết ý định của mình và không cần phải viết ra kết quả đạt được như thế nào. Đây là một ví dụ: Chúng tôi có một danh sách các cuốn sách và chúng tôi muốn tìm tất cả tên của các cuốn sách Java, được phân tách bằng dấu phẩy và được sắp xếp.
public static String getJavaBooks(List books) {
    return books.stream()
            .filter(book -> Objects.equals(book.language(), "Java"))
            .sorted(Comparator.comparing(Book::price))
            .map(Book::name)
            .collect(Collectors.joining(", "));
}
Đoạn mã trên rất đơn giản, dễ đọc và ngắn gọn. Nhưng bên dưới bạn có thể thấy một mã mệnh lệnh thay thế:
public static String getJavaBooksImperatively(List books) {
    var filteredBook = new ArrayList();
    for (Book book : books) {
        if (Objects.equals(book.language(), "Java")){
            filteredBook.add(book);
        }
    }
    filteredBook.sort(new Comparator() {
        @Override
        public int compare(Book o1, Book o2) {
            return Integer.compare(o1.price(), o2.price());
        }
    });

    var joiner = new StringJoiner(",");
    for (Book book : filteredBook) {
        joiner.add(book.name());
    }

    return joiner.toString();
}
Mặc dù cả hai phương thức đều trả về cùng một giá trị nhưng chúng ta có thể thấy rõ sự khác biệt.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION