In dieser dreiteiligen Serie haben wir uns mit allen wichtigen Java 8-Funktionen beschäftigt, die Sie heute in Ihren Android-Projekten verwenden können.
In Cleaner Code Mit Lambda Expressions haben wir uns darauf konzentriert, Boilerplate mithilfe von Lambda-Ausdrücken aus Ihren Projekten zu entfernen. In Default und Static Methods haben wir gesehen, wie diese Lambda-Ausdrücke durch die Kombination mit Methodenverweisen präziser gestaltet werden können. Außerdem haben wir Repeating Annotations und das Deklarieren nicht abstrakter Methoden in Ihren Schnittstellen mit Standard- und statischen Schnittstellenmethoden behandelt.
In diesem letzten Post werden wir uns mit Typanmerkungen, funktionalen Schnittstellen und dem funktionalen Ansatz für die Datenverarbeitung mit der neuen Stream-API von Java 8 befassen.
Ich zeige Ihnen auch, wie Sie mit den Bibliotheken Joda-Time und ThreeTenABP auf zusätzliche Java 8-Funktionen zugreifen können, die derzeit nicht von der Android-Plattform unterstützt werden.
Anmerkungen helfen Ihnen beim Schreiben von Code, der robuster und weniger fehleranfällig ist, indem Sie Codeüberprüfungswerkzeuge wie Lint über die Fehler informieren, auf die sie achten sollten. Diese Überprüfungstools werden Sie dann warnen, wenn ein Code nicht den in diesen Anmerkungen festgelegten Regeln entspricht.
Annotationen sind keine neue Funktion (sie stammen eigentlich aus Java 5.0), aber in früheren Versionen von Java war es nur möglich, Annotationen auf Deklarationen anzuwenden.
Mit dem Release von Java 8 können Sie Anmerkungen jetzt überall verwenden, wo Sie einen Typ verwendet haben, einschließlich Methodenempfänger. Ausdrücke zur Erstellung von Klasseninstanzen; die Implementierung von Schnittstellen; Generika und Arrays; die Spezifikation von wirft
und implementiert
Klauseln; und gießen.
Obwohl Java 8 die Möglichkeit bietet, Annotationen an mehr Standorten als je zuvor zu verwenden, stellt es frustrierend dar, dass es keine typspezifischen Annotationen bereitstellt.
Die Anmerkungs-Support-Bibliothek von Android bietet Zugriff auf einige zusätzliche Anmerkungen, z @Nullable
, @NonNull
, und Anmerkungen zum Überprüfen von Ressourcentypen wie z @DrawableRes
, @DimenRes
, @ ColorRes
, und @StringRes
. Möglicherweise möchten Sie jedoch auch ein statisches Analysetool eines Drittanbieters verwenden, z. B. das Checker Framework, das gemeinsam mit der JSR 308-Spezifikation (Spezifikation zu Annotations on Java Types) entwickelt wurde. Dieses Framework bietet einen eigenen Satz von Annotationen, die auf Typen angewendet werden können, sowie eine Reihe von "Checker" (Annotation-Prozessoren), die sich in den Kompilierungsprozess einbinden und für jede im Checker-Framework enthaltene Typ-Annotation spezifische "Prüfungen" durchführen.
Da sich die Typanmerkungen nicht auf den Laufzeitbetrieb auswirken, können Sie die Typanmerkungen von Java 8 in Ihren Projekten verwenden, während sie mit früheren Java-Versionen abwärtskompatibel bleiben.
Die Stream-API bietet einen alternativen "Pipes-and-Filter" -Ansatz für die Sammlungsverarbeitung.
Vor Java 8 haben Sie Sammlungen manuell bearbeitet, typischerweise indem Sie die Sammlung durchlaufen und die einzelnen Elemente nacheinander bearbeiten. Diese explizite Schleife erfordert viel Boilerplate. Außerdem ist es schwierig, die Struktur der Schleifenschleife zu erfassen, bis Sie den Schleifenkörper erreichen.
Mit der Stream-API können Sie Daten effizienter verarbeiten, indem Sie diese Daten einmalig ausführen, unabhängig davon, welche Datenmenge Sie verarbeiten oder ob Sie mehrere Berechnungen durchführen.
In Java 8 jede Klasse, die implementiert java.util.Collection
hat ein Strom
Methode, die ihre Instanzen in konvertieren kann Strom
Objekte. Zum Beispiel, wenn Sie eine haben Array
:
String [] myArray = neuer String [] "A", "B", "C", "D";
Dann können Sie es mit folgendem Befehl in einen Stream konvertieren:
StrommyStream = Arrays.stream (myArray);
Die Stream-API verarbeitet Daten, indem sie Werte aus einer Quelle über eine Reihe von Rechenschritten, die als a bezeichnet werden, mit sich führen Stream-Pipeline. Eine Stream-Pipeline besteht aus den folgenden Elementen:
Sammlung
, Array- oder Generatorfunktion.Stream.filter ()
bei einer Datenquelle wird lediglich die Stream-Pipeline eingerichtet; Es erfolgt keine Filterung, bis Sie den Terminalbetrieb aufrufen. Dadurch ist es möglich, mehrere Operationen aneinander zu reihen und dann alle diese Berechnungen in einem einzigen Durchgang der Daten durchzuführen. Zwischenoperationen erzeugen einen neuen Stream (zum Beispiel), Filter
erzeugt einen Stream, der die gefilterten Elemente enthält) ohne Ändern der Datenquelle, sodass Sie die Originaldaten an anderer Stelle in Ihrem Projekt verwenden oder mehrere Streams aus derselben Quelle erstellen können.Stream.vorJeder
. Wenn Sie die Terminaloperation aufrufen, werden alle Ihre Zwischenoperationen ausgeführt und erzeugen einen neuen Stream. Ein Stream kann keine Elemente speichern. Sobald Sie eine Terminaloperation aufrufen, wird dieser Stream als "verbraucht" betrachtet und ist nicht mehr verwendbar. Wenn Sie die Elemente eines Streams erneut aufrufen möchten, müssen Sie einen neuen Stream aus der ursprünglichen Datenquelle generieren.Es gibt verschiedene Möglichkeiten, einen Stream aus einer Datenquelle abzurufen, einschließlich:
Stream.of ()
Erzeugt einen Stream aus einzelnen Werten:
StromStrom = Strom von ("A", "B", "C");
IntStream.range ()
Erzeugt einen Stream aus einer Reihe von Zahlen:
IntStream i = IntStream.range (0, 20);
Stream.iterate ()
Erzeugt einen Stream durch wiederholtes Anwenden eines Operators auf jedes Element. Zum Beispiel erstellen wir hier einen Stream, in dem jedes Element um eins ansteigt:
Stroms = Stream.iterate (0, n -> n + 1);
Es gibt eine Vielzahl von Operationen, die Sie verwenden können, um Berechnungen im Funktionsstil für Ihre Streams durchzuführen. In diesem Abschnitt werde ich nur einige der am häufigsten verwendeten Stream-Operationen behandeln.
Das Karte()
Die Operation verwendet einen Lambda-Ausdruck als einziges Argument und wandelt mit diesem Ausdruck den Wert oder den Typ jedes Elements im Stream um. Zum Beispiel gibt uns der folgende neue Stream, wo jeder String
wurde in Großbuchstaben umgewandelt:
StrommyNewStream = myStream.map (s -> s.toUpperCase ());
Durch diesen Vorgang wird die Größe eines Streams begrenzt. Wenn Sie beispielsweise einen neuen Stream mit maximal fünf Werten erstellen möchten, verwenden Sie Folgendes:
Listenumber_string = numbers.stream () .limit (5)
Das filter (Prädikat
Mit dieser Operation können Sie Filterkriterien mithilfe eines Lambda-Ausdrucks definieren. Dieser Lambda-Ausdruck Muss Gibt einen booleschen Wert zurück, der bestimmt, ob jedes Element in den resultierenden Stream aufgenommen werden soll. Wenn Sie beispielsweise ein String-Array haben und Strings aus weniger als drei Zeichen herausfiltern möchten, verwenden Sie Folgendes:
Arrays.stream (meinArray) .filter (s -> s.length ()> 3) .forEach (System.out :: println);
Diese Operation sortiert die Elemente eines Streams. Das Folgende gibt beispielsweise einen Datenstrom von Zahlen in aufsteigender Reihenfolge zurück:
Listelist = Arrays.asList (10, 11, 8, 9, 22); list.stream () .sorted () .forEach (System.out :: println);
Alle Stream-Vorgänge können seriell oder parallel ausgeführt werden. Die Streams sind jedoch sequenziell, sofern Sie nicht ausdrücklich etwas anderes angeben. Das Folgende verarbeitet beispielsweise jedes Element einzeln:
Stream.of ("a", "b", "c", "d", "e") .forEach (System.out :: print);
Um einen Stream parallel auszuführen, müssen Sie diesen Stream explizit als parallel markieren parallel()
Methode:
Stream.of ("a", "b", "c", "d", "e") .parallel () .forEach (System.out :: print);
Parallele Streams verwenden unter der Haube das Fork / Join-Framework, sodass die Anzahl der verfügbaren Threads immer der Anzahl der verfügbaren Kerne in der CPU entspricht.
Der Nachteil paralleler Streams besteht darin, dass bei jeder Ausführung des Codes verschiedene Kerne involviert sein können, so dass Sie normalerweise bei jeder Ausführung eine andere Ausgabe erhalten. Verwenden Sie daher parallele Streams nur, wenn die Verarbeitungsreihenfolge unwichtig ist, und vermeiden Sie parallele Streams, wenn Sie auftragsbasierte Vorgänge ausführen, wie z findFirst ()
.
Sie sammeln die Ergebnisse aus einem Stream mithilfe einer Terminaloperation immer das letzte Element in einer Kette von Stream-Methoden und gibt immer etwas anderes als einen Stream zurück.
Es gibt verschiedene Arten von Terminaloperationen, die verschiedene Datentypen zurückgeben. In diesem Abschnitt werden zwei der am häufigsten verwendeten Terminaloperationen beschrieben.
Das Sammeln
Operation sammelt alle verarbeiteten Elemente in einem Container, z. B. a Liste
oder einstellen
. Java 8 bietet a Sammler
Dienstprogrammklasse, so dass Sie sich keine Sorgen um die Implementierung von machen müssen Sammler
Schnittstelle sowie Fabriken für viele gängige Sammler, einschließlich auflisten()
, toSet ()
, und toCollection ()
.
Der folgende Code erzeugt eine Liste
Nur rote Formen enthalten:
Shapes.stream () .filter (s -> s.getColor (). equals ("red")) .collect (Collectors.toList ());
Alternativ können Sie diese gefilterten Elemente in einem sammeln einstellen
:
.sammeln (Collectors.toSet ());
Das für jeden()
Dieser Vorgang führt für jedes Element des Streams eine Aktion aus, sodass dieses Stream-API einer for-each-Anweisung entspricht.
Wenn du einen hättest Artikel
Liste, dann könnten Sie verwenden für jeden
um alle darin enthaltenen Elemente zu drucken Liste
:
items.forEach (item-> System.out.println (item));
Im obigen Beispiel verwenden wir einen Lambda-Ausdruck. Es ist also möglich, dieselbe Arbeit mit weniger Methoden und einer Methodenreferenz auszuführen:
items.forEach (System.out :: println);
Eine funktionale Schnittstelle ist eine Schnittstelle, die genau eine abstrakte Methode enthält, die als funktionale Methode bezeichnet wird.
Das Konzept von Schnittstellen mit einer Methode ist nicht neu-Lauffähig
, Vergleicher
, Abrufbar
, und OnClickListener
sind alle Beispiele für diese Art von Schnittstelle, obwohl sie in früheren Java-Versionen als Single Abstract Method Interfaces (SAM-Schnittstellen) bezeichnet wurden..
Dies ist mehr als eine einfache Namensänderung, da sich die Arbeitsweise mit funktionalen Schnittstellen (oder SAM-Schnittstellen) in Java 8 im Vergleich zu früheren Versionen erheblich unterscheidet.
Vor Java 8 instanziierten Sie normalerweise eine funktionale Schnittstelle mit einer umfangreichen anonymen Klassenimplementierung. Zum Beispiel erstellen wir hier eine Instanz von Lauffähig
mit einer anonymen Klasse:
Runable r = new Runable () @Override public void run () System.out.println ("Mein lauffähig"); ;
Wie wir bereits im ersten Teil gesehen haben, können Sie bei einer Schnittstelle mit einer Methode diese Schnittstelle mit einem Lambda-Ausdruck anstelle einer anonymen Klasse instanziieren. Jetzt können wir diese Regel aktualisieren: Sie können instanziieren funktionale Schnittstellen, mit einem Lambda-Ausdruck. Zum Beispiel:
Runable r = () -> System.out.println ("My Runable");
Java 8 führt auch a ein @Funktionsschnittstelle
Anmerkung, mit der Sie eine Schnittstelle als funktionale Schnittstelle markieren können:
@FunctionalInterface öffentliche Schnittstelle MyFuncInterface public void doSomething ();
Um die Rückwärtskompatibilität mit früheren Java-Versionen zu gewährleisten, können Sie die @Funktionsschnittstelle
Anmerkung ist optional. Es wird jedoch empfohlen, sicherzustellen, dass Sie Ihre funktionalen Schnittstellen korrekt implementieren.
Wenn Sie versuchen, zwei oder mehr Methoden in einer als. Markierten Schnittstelle zu implementieren @Funktionsschnittstelle
, Der Compiler beschwert sich dann, dass er mehrere nicht überschreibende abstrakte Methoden entdeckt hat. Zum Beispiel wird Folgendes nicht kompiliert:
@FunctionalInterface öffentliche Schnittstelle MyFuncInterface void doSomething (); // Eine zweite abstrakte Methode definieren // void doSomethingElse ();
Und wenn Sie versuchen, eine @Funktionsschnittstelle
Schnittstelle, die null Methoden enthält, dann werden Sie auf eine Keine Zielmethode gefunden Error.
Funktionale Schnittstellen müssen genau eine abstrakte Methode enthalten. Da Standard- und statische Methoden jedoch keinen Körper haben, werden sie als nicht abstrakt betrachtet. Das bedeutet, dass Sie mehrere Standard- und statische Methoden in eine Schnittstelle aufnehmen können, markieren Sie diese als @Funktionsschnittstelle
, und es wird immer noch kompilieren.
Java 8 fügte außerdem ein Paket java.util.function hinzu, das viele funktionale Schnittstellen enthält. Es lohnt sich, sich mit den neuen funktionalen Schnittstellen vertraut zu machen, damit Sie genau wissen, was sofort verfügbar ist.
Das Arbeiten mit Datum und Uhrzeit in Java war noch nie besonders einfach, da viele APIs wichtige Funktionen wie Zeitzoneninformationen weglassen.
Java 8 hat eine neue Datums- und Uhrzeit-API (JSR-310) eingeführt, mit der diese Probleme behoben werden sollen. Leider wird diese API zum Zeitpunkt des Schreibens nicht auf der Android-Plattform unterstützt. Sie können jedoch heute einige der neuen Datums- und Uhrzeitfunktionen in Ihren Android-Projekten verwenden, indem Sie eine Bibliothek eines Drittanbieters verwenden.
In diesem letzten Abschnitt werde ich Ihnen zeigen, wie Sie zwei beliebte Bibliotheken von Drittanbietern einrichten und verwenden, mit denen die Datums- und Zeit-API von Java 8 unter Android verwendet werden kann.
ThreeTen Android Backport (auch als ThreeTenABP bekannt) ist eine Anpassung des beliebten ThreeTen-Backport-Projekts, das eine Implementierung von JSR-310 für Java 6.0 und Java 7.0 bietet. ThreeTenABP ermöglicht den Zugriff auf alle Date- und Time-API-Klassen (wenn auch mit einem anderen Paketnamen), ohne dass eine große Anzahl von Methoden zu Ihren Android-Projekten hinzugefügt werden muss.
Um diese Bibliothek zu verwenden, öffnen Sie Ihre Modulebene build.gradle Datei und fügen Sie ThreeTenABP als Projektabhängigkeit hinzu:
Abhängigkeiten // Fügen Sie die folgende Zeile hinzu: // compile 'com.jakewharton.threetenabp: threetenabp: 1.0.5'
Sie müssen dann die ThreeTenABP-Importanweisung hinzufügen:
import com.jakewharton.threetenabp.AndroidThreeTen;
Und initialisieren Sie die Zeitzoneninformationen in Ihrem Application.onCreate ()
Methode:
@Override public void onCreate () super.onCreate (); AndroidThreeTen.init (this);
ThreeTenABP enthält zwei Klassen, die zwei Arten von Zeit- und Datumsinformationen anzeigen:
LocalDateTime
, die speichert eine Uhrzeit und ein Datum im Format 2017-10-16T13: 17: 57.138 ZonedDateTime
, Das ist zeitzonenabhängig und speichert Datums- und Uhrzeitinformationen im folgenden Format: 2011-12-03T10: 15: 30 + 01: 00 [Europa / Paris] Um Ihnen eine Vorstellung davon zu geben, wie Sie diese Bibliothek zum Abrufen von Datums- und Uhrzeitinformationen verwenden, verwenden Sie die LocalDateTime
Klasse zur Anzeige des aktuellen Datums und der aktuellen Uhrzeit:
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.jakewharton.threetenabp.AndroidThreeTen; import android.widget.TextView; import org.threeten.bp.LocalDateTime; public class MainActivity erweitert AppCompatActivity @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); AndroidThreeTen.init (getApplication ()); setContentView (R.layout.activity_main); TextView textView = new TextView (this); textView.setText ("Time:" + LocalDateTime.now ()); setContentView (textView);
Dies ist nicht die benutzerfreundlichste Art, Datum und Uhrzeit anzuzeigen! Um diese Rohdaten in etwas lesbareres zu parsen, können Sie das verwenden DateTimeFormatter
klassifizieren und auf einen der folgenden Werte setzen:
BASIC_ISO_DATE
. Formatiert das Datum als 2017-1016 + 01.00 ISO_LOCAL_DATE
. Formatiert das Datum als 2017-10-16 ISO_LOCAL_TIME
. Formatiert die Uhrzeit als 14: 58: 43.242 ISO_LOCAL_DATE_TIME
. Formatiert das Datum und die Uhrzeit als 2017-10-16T14: 58: 09.616 ISO_OFFSET_DATE
. Formatiert das Datum als 2017-10-16 + 01.00 Uhr ISO_OFFSET_TIME
. Formatiert die Uhrzeit als 14: 58: 56.218 + 01: 00 ISO_OFFSET_DATE_TIME
. Formatiert Datum und Uhrzeit als 2017-10-16T14: 5836.758 + 01: 00 ISO_ZONED_DATE_TIME
. Formatiert Datum und Uhrzeit als 2017-10-16T14: 58: 51.324 + 01: 00 Uhr (Europa / London) ISO_INSTANT
. Formatiert Datum und Uhrzeit als 2017-10-16T13: 52: 45.246Z ISO_DATE
. Formatiert das Datum als 2017-10-16 + 01: 00 Uhr ISO_TIME
. Formatiert die Uhrzeit als 14: 58: 40.945 + 01: 00 ISO_DATE_TIME
. Formatiert Datum und Uhrzeit als 2017-10-16T14: 55: 32.263 + 01: 00 (Europa / London) ISO_ORDINAL_DATE
. Formatiert das Datum als 2017-289 + 01: 00 Uhr ISO_WEEK_DATE
. Formatiert das Datum als 2017-W42-1 + 01: 00 RFC_1123_DATE_TIME
. Formatiert Datum und Uhrzeit als Mo, 16 OKT 2017 14: 58: 43 + 01: 00 Uhr Hier aktualisieren wir unsere App, um Datum und Uhrzeit mit anzuzeigen DateTimeFormatter.ISO_DATE
Formatierung:
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.jakewharton.threetenabp.AndroidThreeTen; import android.widget.TextView; // Die DateTimeFormatter-Importanweisung hinzufügen // import org.threeten.bp.format.DateTimeFormatter; import org.threeten.bp.ZonedDateTime; public class MainActivity erweitert AppCompatActivity @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); AndroidThreeTen.init (getApplication ()); setContentView (R.layout.activity_main); TextView textView = new TextView (this); DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE; String formattedZonedDate = formatter.format (ZonedDateTime.now ()); textView.setText ("Time:" + formatattedZonedDate); setContentView (textView);
Um diese Informationen in einem anderen Format anzuzeigen, ersetzen Sie sie einfach DateTimeFormatter.ISO_DATE
für einen anderen Wert. Zum Beispiel:
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
Vor Java 8 galt die Joda-Time-Bibliothek als Standardbibliothek für die Verarbeitung von Datum und Uhrzeit in Java, bis zu dem Punkt, an dem die neue Datums- und Zeit-API von Java 8 tatsächlich "stark auf die Erfahrungen aus dem Joda-Time-Projekt zurückgreift"
Während die Joda-Time-Website den Benutzern empfiehlt, so schnell wie möglich auf Java 8 Date and Time zu migrieren, da Android diese API derzeit nicht unterstützt, ist Joda-Time immer noch eine praktikable Option für die Android-Entwicklung. Beachten Sie jedoch, dass Joda-Time über eine umfangreiche API verfügt und Zeitzoneninformationen mithilfe einer JAR-Ressource lädt. Beides kann sich auf die Leistung Ihrer App auswirken.
Um mit der Joda-Time-Bibliothek zu arbeiten, öffnen Sie Ihre Modulebene build.gradle Datei und fügen Sie Folgendes hinzu:
Abhängigkeiten kompiliere 'joda-time: joda-time: 2.9.9'…
Die Joda-Time-Bibliothek verfügt über sechs Hauptkurse für Datum und Uhrzeit:
Sofortig
: Stellt einen Punkt in der Zeitleiste dar; Zum Beispiel können Sie das aktuelle Datum und die aktuelle Uhrzeit abrufen, indem Sie anrufen Instant.now ()
.Terminzeit
: Ein allgemeiner Ersatz für JDKs Kalender
Klasse.LocalDate
: Ein Datum ohne Uhrzeit oder Bezug auf eine Zeitzone.Ortszeit
: Eine Uhrzeit ohne Datum oder Bezug zu einer Zeitzone, z. B. 14:00:00.LocalDateTime
: Ein lokales Datum und eine Uhrzeit, immer noch ohne Zeitzoneninformationen.ZonedDateTime
: Datum und Uhrzeit mit Zeitzone.Lassen Sie uns einen Blick darauf werfen, wie Sie Datum und Uhrzeit mit Joda-Time drucken. Im folgenden Beispiel werde ich Code aus unserem ThreeTenABP-Beispiel wiederverwenden. Um es interessanter zu machen, verwende ich auch withZone
um Datum und Uhrzeit in a umzuwandeln ZonedDateTime
Wert.
import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; public class MainActivity erweitert AppCompatActivity @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); DateTime heute = new DateTime (); // Ein neues Formatierungsprogramm zurückgeben (mit withZone) und die Zeitzone mithilfe von ZoneId angeben // DateTime todayNy = today.withZone (DateTimeZone.forID ("America / New_York")); TextView textView = new TextView (this); textView.setText ("Time:" + todayNy); setContentView (textView);
Eine vollständige Liste der unterstützten Zeitzonen finden Sie in den offiziellen Dokumenten von Joda-Time.
In diesem Beitrag haben wir untersucht, wie robuster Code mithilfe von Typanmerkungen erstellt werden kann. Außerdem wurde der Ansatz der Pipes-and-Filters-Methode für die Datenverarbeitung mit der neuen Stream-API von Java 8 untersucht.
Außerdem haben wir untersucht, wie sich die Schnittstellen in Java 8 entwickelt haben und wie sie in Kombination mit anderen Funktionen verwendet werden können, die wir in dieser Reihe untersucht haben, einschließlich Lambda-Ausdrücken und statischen Schnittstellenmethoden.
Zum Abschluss habe ich Ihnen gezeigt, wie Sie auf einige zusätzliche Java 8-Funktionen zugreifen können, die Android derzeit nicht standardmäßig unterstützt, indem Sie die Projekte Joda-Time und ThreeTenABP verwenden.
Weitere Informationen zur Java 8-Version finden Sie auf der Oracle-Website.
Und während Sie hier sind, lesen Sie einige unserer anderen Beiträge über Java 8 und Android-Entwicklung!