JavaRush /Java-Blog /Random-DE /Kaffeepause Nr. 155. Top 10 Funktionen in Java

Kaffeepause Nr. 155. Top 10 Funktionen in Java

Veröffentlicht in der Gruppe Random-DE

Top 10 Funktionen in Java

Quelle: DZone Dieser Artikel listet zehn Java-Programmierfunktionen auf, die Entwickler häufig in ihrer täglichen Arbeit verwenden. Kaffeepause Nr. 155.  Top 10 Funktionen in Java - 1

1. Collection-Factory-Methode

Sammlungen sind eine der am häufigsten verwendeten Funktionen in der Programmierung. Sie dienen uns als Behälter, in dem wir Gegenstände aufbewahren und weitergeben. Sammlungen werden auch zum Sortieren, Suchen und Wiederholen von Objekten verwendet, was das Leben eines Programmierers erheblich erleichtert. Sie verfügen über mehrere grundlegende Schnittstellen wie List, Set, Map sowie mehrere Implementierungen. Die traditionelle Art, Sammlungen und Karten zu erstellen, kann vielen Entwicklern wortreich erscheinen. Aus diesem Grund wurden in Java 9 mehrere prägnante Factory-Methoden eingeführt. Aufführen :
List countries = List.of("Bangladesh", "Canada", "United States", "Tuvalu");
Satz :
Set countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
Karte :
Map countriesByPopulation = Map.of("Bangladesh", 164_689_383,
                                                            "Canada", 37_742_154,
                                                            "United States", 331_002_651,
                                                            "Tuvalu", 11_792);
Die Factory-Methode ist sehr nützlich, wenn wir unveränderliche Container erstellen möchten. Wenn Sie jedoch veränderliche Sammlungen erstellen möchten, empfiehlt es sich, den traditionellen Ansatz zu verwenden.

2. Lokale Typinferenz

Java 10 hat Typinferenz für lokale Variablen hinzugefügt. Zuvor mussten Entwickler beim Deklarieren und Initialisieren eines Objekts zweimal Typen angeben. Es war sehr anstrengend. Schauen Sie sich das folgende Beispiel an:
Map> properties = new HashMap<>();
Hier wird die Art der beidseitigen Information angegeben. Wenn wir es an einer Stelle definieren, können der Codeleser und der Java-Compiler leicht verstehen, dass es sich um einen Map-Typ handeln muss. Genau das macht die lokale Typinferenz. Hier ist ein Beispiel:
var properties = new HashMap>();
Jetzt wird alles nur einmal geschrieben und der Code sieht nicht viel schlechter aus. Und wenn wir eine Methode aufrufen und das Ergebnis in einer Variablen speichern, wird der Code noch kürzer. Beispiel:
var properties = getProperties();
Und weiter:
var countries = Set.of("Bangladesh", "Canada", "United States", "Tuvalu");
Obwohl die lokale Typinferenz eine praktische Funktion zu sein scheint, kritisieren einige Leute sie. Einige Entwickler argumentieren, dass dies die Lesbarkeit beeinträchtigt. Und das ist wichtiger als die Kürze.

3. Erweiterte Schalterausdrücke

Die traditionelle switch-Anweisung gibt es in Java schon seit den Anfängen und erinnert an C und C++ damals. Das war in Ordnung, aber als sich die Sprache weiterentwickelte, brachte uns dieser Operator bis Java 14 keine Verbesserungen. Natürlich hatte er einige Nachteile. Am berüchtigtsten war der Fall -Through: Um dieses Problem zu lösen, verwendeten Entwickler Break-Anweisungen, bei denen es sich größtenteils um Standardcode handelt. Mit Java 14 wurde jedoch eine verbesserte Version der Switch-Anweisung mit einer viel größeren Funktionsliste eingeführt. Jetzt müssen wir keine Break-Anweisungen mehr hinzufügen und dies löst das Fehlerproblem. Darüber hinaus kann eine Switch-Anweisung einen Wert zurückgeben, was bedeutet, dass wir ihn als Ausdruck verwenden und einer Variablen zuweisen können.
int day = 5;
String result = switch (day) {
    case 1, 2, 3, 4, 5 -> "Weekday";
    case 6, 7 -> "Weekend";
    default -> "Unexpected value: " + day;
};

4. Aufzeichnungen

Obwohl Records eine relativ neue Funktion ist, die in Java 16 eingeführt wurde, finden viele Entwickler sie sehr nützlich, vor allem aufgrund der Erstellung unveränderlicher Objekte. Oft benötigen wir in unserem Programm Datenobjekte, um Werte von einer Methode an eine andere zu speichern oder weiterzugeben. Zum Beispiel eine Klasse zur Übertragung von x-, y- und z-Koordinaten, die wir wie folgt schreiben:
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 + ']';
    }

}
Die Klasse scheint zu ausführlich zu sein. Mit Hilfe von Einträgen kann dieser gesamte Code durch eine prägnantere Version ersetzt werden:
package ca.bazlur.playground;

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

5.Optional

Eine Methode ist ein Vertrag, in dem wir Bedingungen definieren. Wir geben die Parameter mit ihrem Typ sowie dem Rückgabetyp an. Wir erwarten dann, dass sich die Methode beim Aufruf vertragsgemäß verhält. Allerdings erhalten wir am Ende oft null von einer Methode anstelle eines Werts des angegebenen Typs. Das ist ein Fehler. Um dieses Problem zu beheben, testet der Initiator den Wert normalerweise mit einer if-Bedingung, unabhängig davon, ob der Wert null ist oder nicht. Beispiel:
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;
    }
}
Schauen Sie sich den obigen Code an. Die Methode findName soll einen String zurückgeben , gibt aber null zurück. Der Initiator muss nun zunächst nach Nullen suchen, um das Problem zu beheben. Wenn der Initiator dies vergisst, erhalten wir am Ende eine NullPointerException . Wenn andererseits die Methodensignatur die Möglichkeit einer Nichtrückgabe angibt, würde dies alle Verwirrung beseitigen. Und hier kann uns Optional helfen .
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();
    }
}
Hier haben wir die Methode findName mit einer optionalen Option umgeschrieben, um keinen Wert zurückzugeben. Dies alarmiert Programmierer im Voraus und behebt das Problem.

6. Java Date Time API

Jeder Entwickler ist in gewissem Maße mit der Berechnung von Datum und Uhrzeit verwirrt. Das ist keine Übertreibung. Dies war hauptsächlich auf das Fehlen einer guten Java-API für die Arbeit mit Datums- und Uhrzeitangaben zurückzuführen. Jetzt ist dieses Problem nicht mehr relevant, da Java 8 im Paket java.time einen hervorragenden Satz APIs eingeführt hat, der alle Probleme im Zusammenhang mit Datum und Uhrzeit löst. Das Paket java.time verfügt über viele Schnittstellen und Klassen, die die meisten Probleme, einschließlich Zeitzonen, beseitigen. Die am häufigsten verwendeten Klassen in diesem Paket sind:
  • Lokales Datum
  • Ortszeit
  • LocalDateTime
  • Dauer
  • Zeitraum
  • ZonedDateTime
Ein Beispiel für die Verwendung von Klassen aus dem Paket 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());
    }
}
Ein Beispiel für die Verwendung der LocalTime-Klasse zur Zeitberechnung:
LocalTime time = LocalTime.of(20, 30);
int hour = time.getHour();
int minute = time.getMinute();
time = time.withSecond(6);
time = time.plusMinutes(3);
Zeitzone hinzufügen:
ZoneId zone = ZoneId.of("Canada/Eastern");
LocalDate localDate = LocalDate.of(2022, Month.APRIL, 4);
ZonedDateTime zonedDateTime = date.atStartOfDay(zone);

7.NullPointerException

Jeder Entwickler hasst NullPointerException. Besonders schwierig kann es sein, wenn StackTrace keine nützlichen Informationen darüber liefert, wo genau das Problem liegt. Um dies zu demonstrieren, werfen wir einen Blick auf den Beispielcode:
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
}
Schauen Sie sich die grundlegende Methode in dieser Passage an. Wir sehen, dass als nächstes eine NullPointerException geworfen wird . Wenn wir den Code in einer Version vor Java 14 ausführen und kompilieren, erhalten wir den folgenden StackTrace:
Exception in thread "main" java.lang.NullPointerException
at com.bazlur.Main.getLengthOfUsersName(Main.java:11)
at com.bazlur.Main.main(Main.java:7)
Hier gibt es nur sehr wenige Informationen darüber, wo und warum die NullPointerException aufgetreten ist . Aber in Java 14 und späteren Versionen erhalten wir in StackTrace viel mehr Informationen, was sehr praktisch ist. In Java 14 werden wir sehen:
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. Abschließbare Zukunft

Wir schreiben Programme Zeile für Zeile und sie werden normalerweise Zeile für Zeile ausgeführt. Aber es gibt Zeiten, in denen wir eine parallele Ausführung benötigen, um das Programm schneller zu machen. Hierfür nutzen wir üblicherweise Java Thread. Bei der Java-Thread-Programmierung geht es nicht immer um parallele Programmierung. Stattdessen haben wir die Möglichkeit, mehrere unabhängige Programmmodule zusammenzustellen, die unabhängig und oft sogar asynchron ausgeführt werden. Allerdings ist die Thread-Programmierung insbesondere für Anfänger recht schwierig. Aus diesem Grund bietet Java 8 eine einfachere API, die es Ihnen ermöglicht, Teile eines Programms asynchron auszuführen. Sehen wir uns ein Beispiel an. Nehmen wir an, wir müssen drei REST-APIs aufrufen und dann die Ergebnisse kombinieren. Wir können sie einzeln aufrufen. Wenn jeder von ihnen etwa 200 Millisekunden benötigt, beträgt die Gesamtzeit für den Empfang 600 Millisekunden. Was wäre, wenn wir sie parallel betreiben könnten? Da moderne Prozessoren typischerweise Multi-Core-Prozessoren sind, können sie problemlos drei Rest-Aufrufe auf drei verschiedenen Prozessoren verarbeiten. Mit CompletableFuture können wir dies einfach tun.
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-Ausdrücke

Lambda-Ausdrücke sind möglicherweise die leistungsstärkste Funktion der Java-Sprache. Sie haben die Art und Weise verändert, wie wir Code schreiben. Ein Lambda-Ausdruck ist wie eine anonyme Funktion, die Argumente annehmen und einen Wert zurückgeben kann. Wir können einer Variablen eine Funktion zuweisen und sie als Argumente an eine Methode übergeben, und die Methode kann sie zurückgeben. Er hat einen Körper. Der einzige Unterschied zur Methode besteht darin, dass es keinen Namen gibt. Ausdrücke sind kurz und prägnant. Sie enthalten normalerweise nicht viel Boilerplate-Code. Sehen wir uns ein Beispiel an, in dem wir alle Dateien in einem Verzeichnis mit der Erweiterung .java auflisten müssen.
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");
    }
});
Wenn Sie sich diesen Codeabschnitt genau ansehen, haben wir die anonyme innere Klasse list() an die Methode übergeben . Und in der inneren Klasse haben wir die Logik zum Filtern von Dateien platziert. Im Wesentlichen interessiert uns dieser Teil der Logik, nicht das Muster rund um die Logik. Mit dem Lambda-Ausdruck können wir die gesamte Vorlage entfernen und den Code schreiben, der uns interessiert. Hier ist ein Beispiel:
var directory = new File("./src/main/java/ca/bazlur/playground");
String[] list = directory.list((dir, name) -> name.endsWith(".java"));
Dies ist natürlich nur ein Beispiel; Lambda-Ausdrücke haben viele weitere Vorteile.

10. Stream-API

In unserer täglichen Arbeit gehört die Verarbeitung eines Datensatzes zu den häufigsten Aufgaben. Es verfügt über mehrere allgemeine Vorgänge wie Filtern, Transformieren und Sammeln von Ergebnissen. Vor Java 8 waren solche Operationen zwingender Natur. Wir mussten Code für unsere Absicht schreiben (d. h. was wir erreichen wollten) und wie wir es tun wollten. Mit der Erfindung des Lambda-Ausdrucks und der Stream-API können wir nun Datenverarbeitungsfunktionen deklarativ schreiben. Wir geben nur unsere Absicht an und müssen nicht aufschreiben, wie wir zum Ergebnis kommen. Hier ein Beispiel: Wir haben eine Liste von Büchern und möchten alle Namen der Java-Bücher finden, durch Kommas getrennt und sortiert.
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(", "));
}
Der obige Code ist einfach, lesbar und prägnant. Aber unten sehen Sie einen alternativen Imperativcode:
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();
}
Obwohl beide Methoden den gleichen Wert zurückgeben, können wir den Unterschied deutlich erkennen.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION