JavaRush /Java-Blog /Random-DE /Von 8 bis 13: eine vollständige Übersicht über die Java-V...

Von 8 bis 13: eine vollständige Übersicht über die Java-Versionen. Teil 1

Veröffentlicht in der Gruppe Random-DE
Kätzchen, hallo zusammen)) Heute sind wir also im Jahr 2020 und es bleibt nur noch sehr wenig bis zur Veröffentlichung von Java 14. Sie sollten mit der fertigen Version am 17. März rechnen, wir werden im Nachhinein analysieren, was dort frisch und interessant ist, aber heute möchte ich mein Gedächtnis über frühere Java-Versionen auffrischen. Was haben sie uns Neues gebracht? Werfen wir einen Blick darauf. Beginnen wir die Überprüfung mit Java 8, da es immer noch recht relevant ist und in den meisten Projekten verwendet wird. Von 8 bis 13: eine vollständige Übersicht über die Java-Versionen.  Teil 1 - 1Früher wurden alle drei bis fünf Jahre neue Versionen veröffentlicht, doch in letzter Zeit hat Oracle einen anderen Ansatz gewählt: „alle sechs Monate neues Java“. Und so sehen wir alle sechs Monate die Veröffentlichung von Funktionen. Ob es gut oder schlecht ist, jeder sieht es anders. Das gefällt mir zum Beispiel nicht so gut, da neue Versionen nicht viele neue Funktionen haben, aber gleichzeitig wachsen Versionen wie Pilze nach dem Regen. Ich habe ein paar Mal über ein Projekt mit Java 8 geblinzelt, und Java 16 wurde bereits veröffentlicht (aber wenn es selten herauskommt, häufen sich neue Funktionen, und am Ende wird dieses Ereignis lange erwartet, wie ein Feiertag: Alle diskutieren darüber neue Leckereien und man kommt nicht daran vorbei). Also lasst uns anfangen!

Java 8

Funktionsschnittstelle

Was ist das? Eine funktionale Schnittstelle ist eine Schnittstelle, die eine nicht implementierte (abstrakte) Methode enthält. @FunctionalInterface ist eine optionale Annotation, die über einer solchen Schnittstelle platziert wird. Wird benötigt, um zu prüfen, ob es die Anforderungen einer funktionalen Schnittstelle erfüllt (mit nur einer abstrakten Methode). Aber wie immer haben wir einige Einschränkungen: Standard- und statische Methoden fallen nicht unter diese Anforderungen. Daher kann es mehrere solcher Methoden + eine abstrakte geben, und die Schnittstelle ist funktionsfähig. Es kann auch Methoden der Object-Klasse enthalten, die keinen Einfluss auf die Definition der Schnittstelle als funktional haben. Ich füge noch ein paar Worte zu Standard- und statischen Methoden hinzu:
  1. Mit Methoden mit dem Standardmodifikator können Sie Schnittstellen neue Methoden hinzufügen, ohne deren vorhandene Implementierung zu beschädigen.

    public interface Something {
      default void someMethod {
          System.out.println("Some text......");
      }
    }

    Ja, ja, wir fügen der Schnittstelle die implementierte Methode hinzu, und wenn Sie diese Methode implementieren, können Sie sie nicht überschreiben, sondern als geerbte Methode verwenden. Wenn eine Klasse jedoch zwei Schnittstellen mit einer bestimmten Methode implementiert, tritt ein Kompilierungsfehler auf. Wenn sie Schnittstellen implementiert und eine Klasse mit einer bestimmten identischen Methode erbt, überlappt die Methode der übergeordneten Klasse die Schnittstellenmethoden und die Ausnahme funktioniert nicht.

  2. Statische Methoden in einer Schnittstelle funktionieren genauso wie statische Methoden in einer Klasse. Vergessen Sie nicht: Sie können keine statischen Methoden erben, genauso wenig wie Sie eine statische Methode von einer Nachkommenklasse aufrufen können.

Also noch ein paar Worte zu funktionalen Schnittstellen und dann geht es weiter. Hier sind die Hauptlisten der FIs (der Rest sind ihre Varianten):

    Prädikat – nimmt einen Wert T als Argument und gibt einen booleschen Wert zurück.

    Beispiel:boolean someMethod(T t);

  • Verbraucher – akzeptiert ein Argument vom Typ T und gibt nichts zurück (void).

    Beispiel:void someMethod(T t);

  • Lieferant – nimmt nichts als Eingabe, gibt aber einen Wert T zurück.

    Beispiel:T someMethod();

  • Funktion – nimmt einen Parameter vom Typ T als Eingabe und gibt einen Wert vom Typ R zurück.

    Beispiel:R someMethod(T t);

  • UnaryOperator – akzeptiert ein T-Argument und gibt einen Wert vom Typ T zurück.

    Beispiel:T someMethod(T t);

Strom

Streams sind eine Möglichkeit, Datenstrukturen in einem funktionalen Stil zu handhaben. In der Regel handelt es sich dabei um Sammlungen (Sie können sie jedoch auch in anderen, weniger häufigen Situationen verwenden). In einer verständlicheren Sprache ist Stream ein Datenstrom, den wir so verarbeiten, als würden wir mit allen Daten gleichzeitig arbeiten, und nicht mit roher Gewalt, wie bei „for-each“. Schauen wir uns ein kleines Beispiel an. Nehmen wir an, wir haben eine Reihe von Zahlen, die wir filtern möchten (weniger als 50), um 5 erhöhen und die ersten 4 der verbleibenden Zahlen an die Konsole ausgeben möchten. Wie hätten wir das früher gemacht:
List<Integer> list = Arrays.asList(46, 34, 24, 93, 91, 1, 34, 94);

int count = 0;

for (int x : list) {

  if (x >= 50) continue;

  x += 5;

  count++;

  if (count > 4) break;

  System.out.print(x);

}
Es scheint nicht viel Code zu geben und die Logik ist bereits etwas verwirrend. Mal sehen, wie es anhand des Streams aussehen wird:
Stream.of(46, 34, 24, 93, 91, 1, 34, 94)

      .filter(x -> x < 50)

      .map(x -> x + 5)

      .limit(4)

      .forEach(System.out::print);
Streams vereinfachen das Leben erheblich, indem sie die Menge an Code reduzieren und ihn besser lesbar machen. Für diejenigen, die sich eingehender mit diesem Thema befassen möchten, gibt es hier einen guten (ich würde sogar sagen ausgezeichneten) Artikel zu diesem Thema .

Lambda

Das vielleicht wichtigste und am längsten erwartete Feature ist das Erscheinen von Lambdas. Was ist Lambda? Dabei handelt es sich um einen Codeblock, der an verschiedene Stellen weitergegeben werden kann, sodass er später so oft wie nötig ausgeführt werden kann. Klingt ziemlich verwirrend, nicht wahr? Einfach ausgedrückt können Sie mit Lambdas eine Methode einer funktionalen Schnittstelle implementieren (eine Art Implementierung einer anonymen Klasse):
Runnable runnable = () -> { System.out.println("I'm running !");};

new Thread(runnable).start();
Wir haben die run()-Methode schnell und ohne unnötigen bürokratischen Aufwand implementiert. Und ja: Runnable ist eine funktionale Schnittstelle. Ich verwende auch Lambdas, wenn ich mit Streams arbeite (wie in den Beispielen mit Streams oben). Wir werden nicht zu tief gehen, da man ziemlich tief eintauchen kann, werde ich ein paar Links hinterlassen, damit Leute, die im Herzen immer noch Bagger sind, tiefer graben können:

für jede

Java 8 verfügt über ein neues foreach, das mit einem Datenstrom wie einem Stream funktioniert. Hier ist ein Beispiel:
List<Integer> someList = Arrays.asList(1, 3, 5, 7, 9);

someList.forEach(x -> System.out.println(x));
(analog zu someList.stream().foreach(…))

Methodenreferenz

Referenzmethoden sind eine neue, nützliche Syntax, die darauf ausgelegt ist, auf vorhandene Methoden oder Konstruktoren von Java-Klassen oder -Objekten über :: zu verweisen. Methodenverweise gibt es in vier Typen:
  1. Link zum Designer:

    SomeObject obj = SomeObject::new

  2. Statische Methodenreferenz:

    SomeObject::someStaticMethod

  3. Ein Verweis auf eine nicht statische Methode eines Objekts eines bestimmten Typs:

    SomeObject::someMethod

  4. Ein Verweis auf eine reguläre (nicht statische) Methode eines bestimmten Objekts

    obj::someMethod

In Streams werden häufig Methodenreferenzen anstelle von Lambdas verwendet (Referenzmethoden sind schneller als Lambdas, aber schlechter lesbar).
someList.stream()

        .map(String::toUpperCase)

      .forEach(System.out::println);
Für diejenigen, die weitere Informationen zu Referenzmethoden wünschen:

API-Zeit

Es gibt eine neue Bibliothek zum Arbeiten mit Datums- und Uhrzeitangaben – java.time. Von 8 bis 13: eine vollständige Übersicht über die Java-Versionen.  Teil 1 - 2Die neue API ähnelt jeder Joda-Time. Die wichtigsten Abschnitte dieser API sind:
  • LocalDate ist ein bestimmtes Datum, zum Beispiel der 09.01.2010;
  • LocalTime – Zeit unter Berücksichtigung der Zeitzone – 19:45:55 (analog zu LocalDate);
  • LocalDateTime – Kombination LocalDate + LocalTime – 2020-01-04 15:37:47;
  • ZoneId – stellt Zeitzonen dar;
  • Uhr – mit diesem Typ können Sie auf die aktuelle Uhrzeit und das aktuelle Datum zugreifen.
Hier sind ein paar wirklich interessante Artikel zu diesem Thema:

Optional

Dies ist eine neue Klasse im Paket java.util , ein Wert-Wrapper, dessen Trick darin besteht, dass er auch sicher null enthalten kann . Empfangen optional: Wenn wir nullOptional<String> someOptional = Optional.of("Something"); in Optional.of übergeben , erhalten wir unsere bevorzugte NullPointerException . Für solche Fälle verwenden sie: - Bei dieser Methode müssen Sie keine Angst vor Null haben. Als nächstes erstellen Sie ein anfänglich leeres. Optional: Um zu überprüfen, ob es leer ist, verwenden Sie: gibt uns true oder false zurück. Eine bestimmte Aktion ausführen, wenn ein Wert vorhanden ist, und nichts tun, wenn kein Wert vorhanden ist: Eine umgekehrte Methode, die den übergebenen Wert zurückgibt, wenn Optional leer ist (eine Art Backup-Plan): Sie können sehr, sehr lange fortfahren ( Glücklicherweise hat Optional mit beiden großzügigen Händen Methoden hinzugefügt, aber wir werden uns nicht näher damit befassen. Es ist besser für mich, ein paar Links für den Anfang zu hinterlassen: Optional<String> someOptional = Optional.ofNullable("Something");Optional<String> someOptional = Optional.empty();someOptional.isPresent();someOptional.ifPresent(System.out::println);System.out.println(someOptional.orElse("Some default content")); Wir haben die bekanntesten Neuerungen in Java 8 besprochen – das ist noch nicht alles. Wenn Sie mehr wissen möchten, habe ich Folgendes für Sie hinterlassen:

Java 9

Am 21. September 2017 erblickte die Welt JDK 9. Dieses Java 9 verfügt über zahlreiche Funktionen. Obwohl es keine neuen Sprachkonzepte gibt, werden die neuen APIs und Diagnosebefehle für Entwickler auf jeden Fall interessant sein. Von 8 bis 13: eine vollständige Übersicht über die Java-Versionen.  Teil 1 - 4

JShell (REPL – Read-Eval-Print-Schleife)

Hierbei handelt es sich um eine Java-Implementierung einer interaktiven Konsole, die zum Testen der Funktionalität und zur Verwendung verschiedener Konstrukte in der Konsole verwendet wird, wie z. B. Schnittstellen, Klassen, Aufzählungen, Operatoren usw. Um JShell zu starten, müssen Sie lediglich jshell in das Terminal schreiben. Dann können wir schreiben, was immer unsere Fantasie zulässt: Von 8 bis 13: eine vollständige Übersicht über die Java-Versionen.  Teil 1 - 5Mit JShell können Sie Methoden der obersten Ebene erstellen und diese innerhalb derselben Sitzung verwenden. Die Methoden funktionieren genauso wie statische Methoden, mit der Ausnahme, dass das Schlüsselwort static weggelassen werden kann. Weitere Informationen finden Sie im Java 9. REPL (JShell)-Handbuch .

Privat

Ab Version 9 von Java haben wir die Möglichkeit, private Methoden in Schnittstellen zu verwenden (Standard- und statische Methoden, da wir andere aufgrund unzureichenden Zugriffs einfach nicht überschreiben können). private static void someMethod(){} try-with-resources Die Fähigkeit zur Behandlung von Try-With-Resources-Ausnahmen wurde verbessert:
BufferedReader reader = new BufferedReader(new FileReader("....."));
  try (reader2) {
  ....
}

Modularität ( Puzzle )

Ein Modul ist eine Gruppe verwandter Pakete und Ressourcen zusammen mit einer neuen Moduldeskriptordatei. Dieser Ansatz wird verwendet, um die Kopplung von Code zu lockern. Die lose Kopplung ist ein Schlüsselfaktor für die Wartbarkeit und Erweiterbarkeit des Codes. Modularität wird auf verschiedenen Ebenen umgesetzt:
  1. Programmiersprache.
  2. Virtuelle Maschine.
  3. Standard-Java-API.
JDK 9 enthält 92 Module: Wir können sie verwenden oder unsere eigenen erstellen. Hier sind ein paar Links für einen tieferen Einblick:

Unveränderliche Sammlung

In Java 9 wurde es möglich, eine Sammlung mit einer Zeile zu erstellen und zu füllen und sie gleichzeitig unveränderlich zu machen (zuvor mussten wir zum Erstellen einer unveränderlichen Sammlung eine Sammlung erstellen, sie mit Daten füllen und eine Methode aufrufen, z. B. Collections.unmodifiableList). Ein Beispiel für eine solche Kreation: List someList = List.of("first","second","third");

Weitere Neuerungen:

  • erweitert Optional (neue Methoden hinzugefügt);
  • Die Schnittstellen ProcessHandle und ProcessHandle schienen die Aktionen des Betriebssystems zu steuern.
  • G1 – Standard-Garbage Collector;
  • HTTP-Client mit Unterstützung sowohl für HTTP/2-Protokolle als auch für WebSocket;
  • erweiterter Stream;
  • Reactive Streams API-Framework hinzugefügt (für reaktive Programmierung);
Für ein umfassenderes Eintauchen in Java 9 empfehle ich Ihnen die Lektüre:

Java 10

Sechs Monate nach der Veröffentlichung von Java 9, im März 2018 (ich erinnere mich daran wie gestern), kam Java 10 auf den Markt. Von 8 bis 13: eine vollständige Übersicht über die Java-Versionen.  Teil 1 - 6

var

Jetzt müssen wir keinen Datentyp mehr angeben. Wir markieren die Nachricht als var und der Compiler bestimmt den Typ der Nachricht anhand des Typs des rechts vorhandenen Initialisierers. Diese Funktion ist nur für lokale Variablen mit einem Initialisierer verfügbar: Sie kann nicht für Methodenargumente, Rückgabetypen usw. verwendet werden, da es keinen Initialisierer gibt, mit dem der Typ definiert werden kann. Beispielvariable (für Typ String):
var message = "Some message…..";
System.out.println(message);
var ist kein Schlüsselwort: Es handelt sich im Wesentlichen um einen reservierten Typnamen, genau wie int . Der Vorteil von var ist groß: Typdeklarationen nehmen viel Aufmerksamkeit in Anspruch, ohne einen Nutzen zu bringen, und diese Funktion spart Zeit. Wenn aber eine Variable aus einer langen Methodenkette gewonnen wird, wird der Code gleichzeitig schlechter lesbar, da sofort unklar ist, was für ein Objekt sich dort befindet. Für diejenigen gedacht, die sich mit dieser Funktionalität besser vertraut machen möchten:

JIT-Compiler (GraalVM)

Lassen Sie mich ohne weitere Umschweife daran erinnern, dass beim Ausführen des Befehls javac eine Java-Anwendung aus Java-Code in JVM-Bytecode kompiliert wird, der die binäre Darstellung der Anwendung darstellt. Ein normaler Computerprozessor kann den JVM-Bytecode jedoch nicht einfach ausführen. Damit Ihr JVM-Programm funktioniert, benötigen Sie einen weiteren Compiler für diesen Bytecode, der in Maschinencode umgewandelt wird, den der Prozessor bereits verwenden kann. Im Vergleich zu Javac ist dieser Compiler wesentlich komplexer, erzeugt aber auch Maschinencode höherer Qualität. Derzeit enthält OpenJDK die virtuelle HotSpot-Maschine, die wiederum über zwei Haupt-JIT-Compiler verfügt. Der erste, C1 ( Client-Compiler ), ist für einen schnelleren Betrieb konzipiert, leidet jedoch unter der Codeoptimierung. Der zweite ist C2 (Server-Compiler). Die Ausführungsgeschwindigkeit leidet, aber der Code ist optimierter. Wann kommt welches zum Einsatz? C1 eignet sich hervorragend für Desktop-Anwendungen, bei denen lange JIT-Pausen unerwünscht sind, und C2 eignet sich hervorragend für Serverprogramme mit langer Laufzeit, bei denen es durchaus erträglich ist, mehr Zeit für die Kompilierung aufzuwenden. Bei der mehrstufigen Kompilierung erfolgt die Kompilierung zunächst über C1 und das Ergebnis über C2 (wird für eine bessere Optimierung verwendet). GraalVM ist ein Projekt, das HotSpot vollständig ersetzen soll. Wir können uns Graal als mehrere verwandte Projekte vorstellen: einen neuen JIT-Compiler für HotSpot und eine neue polyglotte virtuelle Maschine. Die Besonderheit dieses JIT-Compilers besteht darin, dass er in Java geschrieben ist. Der Vorteil des Graal-Compilers ist die Sicherheit, d. h. keine Abstürze, sondern Ausnahmen und keine Speicherverluste. Außerdem verfügen wir über eine gute IDE-Unterstützung und können Debugger, Profiler oder andere praktische Tools verwenden. Darüber hinaus ist der Compiler möglicherweise unabhängig von HotSpot und kann eine schnellere JIT-kompilierte Version von sich selbst erstellen. Für Bagger:

Parallel G1

Der G1-Garbage Collector ist zweifellos cool, aber er hat auch eine Schwachstelle: Er führt einen vollständigen Single-Thread-GC-Zyklus durch. In einer Zeit, in der Sie die gesamte Leistungsfähigkeit der Hardware benötigen, um ungenutzte Objekte zu finden, sind wir auf einen einzigen Thread beschränkt. Java 10 hat dieses Problem behoben. Jetzt funktioniert der GC mit allen Ressourcen, die wir ihm hinzufügen (d. h. er wird multithreaded). Um dies zu erreichen, haben die Sprachentwickler die Isolierung der Hauptquellen von GC verbessert und so eine schöne, saubere Schnittstelle für GC geschaffen. Die Entwickler dieser Niedlichkeit, OpenJDK, mussten den Dump im Code gezielt bereinigen, um nicht nur die Erstellung neuer GCs so weit wie möglich zu vereinfachen, sondern auch eine schnelle Deaktivierung unnötiger GCs aus der Assembly zu ermöglichen. Eines der Hauptkriterien für den Erfolg ist, dass es nach all diesen Verbesserungen zu keinem Einbruch der Arbeitsgeschwindigkeit kommt. Schauen wir uns auch an: Weitere Neuerungen:
  1. Eine saubere Garbage-Collector-Schnittstelle wird eingeführt. Dies verbessert die Isolierung des Quellcodes von verschiedenen Garbage Collectors und ermöglicht die schnelle und problemlose Integration alternativer Collectors.
  2. Kombinieren von JDK-Quellen in einem Repository;
  3. Sammlungen haben eine neue Methode erhalten – copyOf (Collection) , die eine unveränderliche Kopie dieser Sammlung zurückgibt;
  4. Optional (und seine Varianten) hat eine neue Methode .orElseThrow() ;
  5. Von nun an wissen JVMs, dass sie in einem Docker-Container ausgeführt werden, und rufen die Container-spezifische Konfiguration ab, anstatt das Betriebssystem selbst abzufragen.
Hier sind einige weitere Materialien für eine detailliertere Einführung in Java 10: Früher war ich sehr verwirrt darüber, dass einige Java-Versionen 1.x hießen. Ich möchte es klarstellen: Java-Versionen vor 9 hatten einfach ein anderes Namensschema. Java 8 kann beispielsweise auch 1.8 , Java 5 - 1.5 usw. heißen. Und jetzt sehen wir, dass sich mit dem Übergang zu Releases von Java 9 auch das Benennungsschema geändert hat und Java-Versionen nicht mehr mit 1.x vorangestellt werden . Dies ist das Ende des ersten Teils: Wir haben die neuen interessanten Funktionen von Java 8-10 besprochen. Lassen Sie uns unsere Bekanntschaft mit dem Neuesten im nächsten Beitrag fortsetzen .
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION