JavaRush /Java 博客 /Random-ZH /喝咖啡休息#155。Java 中十大函数

喝咖啡休息#155。Java 中十大函数

已在 Random-ZH 群组中发布

Java 中十大函数

来源: DZone 本文列出了开发人员在日常工作中经常使用的十个 Java 编程功能。 喝咖啡休息#155。 Java 中十大函数 - 1

1.集合工厂方法

集合是编程中最常用的功能之一。它们被用作我们存储对象并传递对象的容器。集合还用于排序、搜索和重复对象,使程序员的工作变得更加轻松。它们有几个基本接口,如List、Set、Map,以及几个实现。对于许多开发人员来说,创建集合和地图的传统方式似乎很冗长。这就是为什么 Java 9 引入了几个简洁的工厂方法。 列表
List countries = List.of("Bangladesh", "Canada", "United States", "Tuvalu");
Set countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
地图
Map countriesByPopulation = Map.of("Bangladesh", 164_689_383,
                                                            "Canada", 37_742_154,
                                                            "United States", 331_002_651,
                                                            "Tuvalu", 11_792);
当我们想要创建不可变的容器时,工厂方法非常有用。但如果您要创建可变集合,建议使用传统方法。

2. 局部类型推断

Java 10 添加了局部变量的类型推断。在此之前,开发人员在声明和初始化对象时必须指定两次类型。很累。看下面的例子:
Map> properties = new HashMap<>();
此处指示双方的信息类型。如果我们将它定义在一个地方,那么代码阅读器和Java编译器就会很容易地理解它一定是一个Map类型。局部类型推断就是这样做的。这是一个例子:
var properties = new HashMap>();
现在一切都只写一次,代码看起来也没有差多少。当我们调用一个方法并将结果存储在一个变量中时,代码变得更短。例子:
var properties = getProperties();
并进一步:
var countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
尽管局部类型推断似乎是一个方便的功能,但有些人批评它。一些开发人员认为这会降低可读性。这比简洁更重要。

3. 高级开关表达式

传统的 switch 语句从一开始就存在于 Java 中,让人想起当时的 C 和 C++。这很好,但随着语言的发展,这个运算符直到 Java 14 才为我们提供任何改进。当然,它也有一些缺点。最臭名昭著的是失败:为了解决这个问题,开发人员使用了break语句,这些语句主要是样板代码。然而,Java 14 引入了 switch 语句的改进版本,具有更大的函数列表。现在我们不再需要添加break语句,这解决了失败问题。此外,switch 语句可以返回一个值,这意味着我们可以将其用作表达式并将其分配给变量。
int day = 5;
String result = switch (day) {
    case 1, 2, 3, 4, 5 -> "Weekday";
    case 6, 7 -> "Weekend";
    default -> "Unexpected value: " + day;
};

4. 记录

尽管 Records 是 Java 16 中引入的一个相对较新的功能,但许多开发人员发现它非常有用,主要是由于不可变对象的创建。通常我们需要在程序中使用数据对象来存储值或将值从一种方法传递到另一种方法。例如,一个用于传输x、y、z坐标的类,我们将其编写如下:
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 + ']';
    }

}
这门课似乎太冗长了。在条目的帮助下,所有这些代码都可以替换为更简洁的版本:
package ca.bazlur.playground;

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

5.可选

方法是我们定义条件的契约。我们指定参数的类型以及返回类型。然后我们期望当该方法被调用时,它将按照约定行事。但是,我们经常会从方法中得到 null,而不是指定类型的值。这是错误的。为了解决这个问题,发起者通常使用 if 条件测试该值,无论该值是否为 null。例子:
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;
    }
}
看上面的代码。findName方法应该返回String,但它返回 null。发起者现在必须首先检查空值来处理该问题。如果发起者忘记这样做,那么我们最终将得到一个 NullPointerException。另一方面,如果方法签名指示不返回的可能性,那么这将解决所有混乱。这就是Optional可以帮助我们的地方。
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();
    }
}
在这里,我们使用Optional选项 重写了findName方法,以不返回任何值。这会提前提醒程序员并修复问题。

6.Java 日期时间 API

每个开发人员都对计算日期和时间感到不同程度的困惑。这并不夸张。这主要是由于缺乏用于处理日期和时间的良好 Java API。现在这个问题已经不再重要,因为Java 8在java.time包中引入了一组优秀的API,它解决了与日期和时间相关的所有问题。java.time 包有许多接口和类,可以消除大多数问题,包括时区。该包中最常用的类是:
  • 本地日期
  • 当地时间
  • 本地日期时间
  • 期间
  • 时期
  • 分区日期时间
使用 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());
    }
}
使用 LocalTime 类计算时间的示例:
LocalTime time = LocalTime.of(20, 30);
int hour = time.getHour();
int minute = time.getMinute();
time = time.withSecond(6);
time = time.plusMinutes(3);
添加时区:
ZoneId zone = ZoneId.of("Canada/Eastern");
LocalDate localDate = LocalDate.of(2022, Month.APRIL, 4);
ZonedDateTime zonedDateTime = date.atStartOfDay(zone);

7.空指针异常

每个开发人员都讨厌 NullPointerException。当 StackTrace 不提供有关问题到底是什么的有用信息时,这可能会特别困难。为了演示这一点,让我们看一下示例代码:
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
}
看看这篇文章中的基本方法。我们看到接下来会抛出一个 NullPointerException。如果我们在Java 14之前的版本中运行并编译代码,我们将得到以下StackTrace:
Exception in thread "main" java.lang.NullPointerException
at com.bazlur.Main.getLengthOfUsersName(Main.java:11)
at com.bazlur.Main.main(Main.java:7)
这里关于NullPointerException发生的 地点和原因的信息很少。但在Java 14及之后的版本中,我们在StackTrace中获取了更多的信息,这非常方便。在 Java 14 中我们将看到:
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. 完整的未来

我们一行一行地编写程序,并且它们通常是逐行执行的。但有时我们需要并行执行以使程序更快。为此,我们通常使用 Java 线程。Java 线程编程并不总是关于并行编程。相反,它使我们能够编写多个独立的程序模块,这些模块将独立执行,甚至通常异步执行。然而,线程编程是相当困难的,尤其是对于初学者来说。这就是为什么 Java 8 提供了更简单的 API,允许您异步执行程序的一部分。让我们看一个例子。假设我们需要调用三个 REST API,然后合并结果。我们可以一一称呼它们。如果每个消息大约需要 200 毫秒,那么接收它们的总时间将需要 600 毫秒。如果我们可以并行运行它们怎么办?由于现代处理器通常是多核的,因此它们可以轻松处理三个不同处理器上的三个剩余调用。使用 CompletableFuture 我们可以轻松做到这一点。
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. Lambda 表达式

Lambda 表达式可能是 Java 语言最强大的功能。他们改变了我们编写代码的方式。lambda 表达式就像一个可以接受参数并返回值的匿名函数。我们可以将一个函数分配给一个变量并将其作为参数传递给一个方法,并且该方法可以返回它。他有身体。与该方法唯一的区别是没有名称。表达方式简短而简洁。它们通常不包含大量样板代码。让我们看一个示例,我们需要列出目录中扩展名为 .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");
    }
});
如果你仔细观察这段代码,我们已经将匿名内部类list()传递给了方法。在内部类中我们放置了过滤文件的逻辑。本质上,我们感兴趣的是这部分逻辑,而不是逻辑周围的模式。lambda 表达式允许我们删除整个模板,我们可以编写我们感兴趣的代码。这是一个例子:
var directory = new File("./src/main/java/ca/bazlur/playground");
String[] list = directory.list((dir, name) -> name.endsWith(".java"));
当然,这只是一个示例;lambda 表达式还有许多其他好处。

10. 流API

在我们的日常工作中,常见的任务之一就是处理数据集。它有一些常见的操作,例如过滤、转换和收集结果。在 Java 8 之前,此类操作本质上是命令式的。我们必须根据我们的意图(即我们想要实现的目标)以及我们希望如何做到这一点来编写代码。随着 lambda 表达式和 Stream API 的发明,我们现在可以以声明方式编写数据处理函数。我们只需要表明我们的意图,我们不需要写下我们是如何得到结果的。下面是一个例子:我们有一个书籍列表,我们想要找到所有 Java 书籍的名称,以逗号分隔并排序。
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(", "));
}
上面的代码简单、可读、简洁。但下面您可以看到替代的命令式代码:
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();
}
尽管两种方法返回相同的值,但我们可以清楚地看到差异。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION