Vorwort: Java 8 wurde seit einiger Zeit veröffentlicht, und alle Anzeichen zeigen, dass Java 8 eine wesentliche Änderung der Verteilung darstellt. Es gibt bereits viele Artikel zu Java Code -Geeks, die neue Funktionen von Java 8 einführen, z. Dieser Artikel bezieht sich auch auf einige andere Informationen, wie z. B.: 15 muss Java 8 -Tutorials und die dunkle Seite von Java 8 lesen. Dieser Artikel hat die obigen Informationen zusammengestellt und in ein Referenzlehrbuch über die neuen Funktionen von Java 8 zusammengestellt. Ich hoffe, Sie werden etwas gewinnen.
1. Einführung
Es besteht kein Zweifel, dass Java 8 die wichtigste Version von Java seit Java 5 ist (veröffentlicht im Jahr 2004). Diese Version enthält mehr als ein Dutzend neue Funktionen in Sprachen, Compilern, Bibliotheken, Tools und JVM. In diesem Artikel lernen wir diese neuen Funktionen und verwenden praktische Beispiele, um zu veranschaulichen, welche Szenarien für die Verwendung geeignet sind.
Dieses Tutorial enthält verschiedene Arten von Problemen, mit denen Java -Entwickler häufig konfrontiert sind:
Sprache
Compiler
Bibliothek
Werkzeug
Laufzeit (JVM)
2. Neue Merkmale der Java -Sprache
Java 8 ist eine Hauptversion von Java. Einige Leute glauben, dass diese neuen Funktionen, obwohl sie von Java -Entwicklern erwartet werden, aber auch viel Anstrengung erfordert, um zu lernen. In diesem Abschnitt werden wir die meisten neuen Funktionen von Java 8 vorstellen.
2.1 Lambda -Ausdrücke und funktionelle Schnittstellen
Lambda -Ausdrücke (auch als Schließungen bezeichnet) sind die größten und am meisten erwarteten Sprachänderungen in Java 8. Es ermöglicht es uns, Funktionen als Parameter an eine Methode zu übergeben oder den Code selbst als Daten zu verarbeiten: Funktionelle Entwickler sind mit diesen Konzepten sehr vertraut. Viele Sprachen auf JVM -Plattformen (Groovy, Scala usw.) haben seit ihrer Geburt Lambda -Ausdrücke unterstützt, aber Java -Entwickler haben keine andere Wahl, als anonyme interne Klassen anstelle von Lambda -Ausdrücken zu verwenden.
Das Design von Lambda brauchte viel Zeit und viele Community -Bemühungen und fand schließlich eine Kompromiss -Implementierungslösung, die eine einfache und kompakte Sprachstruktur erreichen konnte. Der einfachste Lambda -Ausdruck kann aus einer von Kommas getrennten Parameterliste, -> Symbol und Anweisungsblock bestehen, zum Beispiel:
Arrays.aSlist ("A", "B", "D") .foreach (e -> System.out.println (e));
Im obigen Code wird der Parametertyp E vom Compiler -Denken abgeleitet, und Sie können auch explizit den Parametertyp angeben, zum Beispiel:
Arrays.aSlist ("A", "B", "D") .foreach ((String E) -> System.out.println (e));
Wenn ein Lambda -Ausdruck einen komplexeren Anweisung -Block erfordert, können Sie den Anweisungsblock mit lockigen Klammern beischließen, ähnlich wie der Körper einer Funktion in Java, zum Beispiel:
Arrays.aslist ("a", "b", "d") .foreach (e -> {system.out.print (e); System.out.print (e);});Lambda -Ausdrücke können sich auf Klassenmitglieder und lokale Variablen beziehen (die diese Variablen implizit in die endgültige Umwandlung umwandeln), beispielsweise die folgenden zwei Codeblöcke haben genau den gleichen Effekt:
String separator = ","; arrays.aslist ("a", "b", "d") .foreach ((String e) -> System.out.print (e + separator));Und
Final String separator = ","; Arrays.aSlist ("A", "B", "D") .foreach ((String E) -> System.out.print (E + Separator));Lambda -Ausdrücke haben Rückgabewerte, und die Art der Rückgabewerte wird auch durch den Compiler -Inferenz abgeleitet. Wenn der Anweisungsblock im Lambda -Ausdruck nur eine Zeile hat, müssen Sie die Return -Anweisung nicht verwenden. Die folgenden zwei Codeausschnitte haben den gleichen Effekt:
Arrays.aSlist ("a", "b", "d") .sort ((e1, e2) -> e1.comPareto (e2));Und
Arrays.aSlist ("a", "b", "d") .sort ((e1, e2) -> {int result = e1.comPareto (e2); Rückgabeergebnis;});Um bestehende Funktionen gut mit Lambda -Ausdrücken kompatibel zu machen, berücksichtigten Lambda -Designer viele Methoden, sodass sie das Konzept der Funktionsoberfläche entwickelten. Die Funktionsschnittstelle bezieht sich auf eine Schnittstelle mit nur einer Funktion, und eine solche Schnittstelle kann implizit in einen Lambda -Ausdruck konvertiert werden. java.lang.runnable und java.util.concurrent.callable sind die besten Beispiele für funktionale Schnittstellen. In der Praxis sind funktionelle Schnittstellen sehr zerbrechlich: Solange ein Entwickler der Schnittstelle eine Funktion hinzufügt, ist die Schnittstelle keine funktionale Schnittstelle mehr, was zum Kompilierungsversagen führt. Um diese Verwundbarkeit dieser Codeebene zu überwinden und ausdrücklich anzugeben, dass eine Schnittstelle eine funktionale Schnittstelle ist, bietet Java 8 eine spezielle Annotation @FunctionAnInterface (alle verwandten Schnittstellen in der Java -Bibliothek haben diese Annotation bereits), um eine einfache Definition einer funktionalen Schnittstelle zu geben:
@FunctionalInterface Public Interface Functional {void methode ();}Eine Sache zu beachten ist jedoch, dass die Standardmethode und die statische Methode die Definition der funktionalen Schnittstelle nicht zerstören, sodass der folgende Code legal ist.
@FunctionalInterface public interface functionalLtefaultMethods {void methode (); Standard void defaultMethod () {}}Lambda -Ausdrücke als das größte Verkaufsargument von Java 8 können mehr Entwickler dazu bringen, sich der JVM -Plattform anzuschließen und das Konzept der funktionalen Programmierung in reiner Java -Programmierung zu nutzen. Wenn Sie mehr über Lambda -Ausdrücke erfahren müssen, können Sie sich auf die offizielle Dokumentation beziehen.
2.2 Standard- und statische Methoden von Schnittstellen
Java 8 verwendet zwei neue Konzepte, um die Bedeutung einer Schnittstelle zu erweitern: die Standardmethode und die statische Methode. Die Standardmethode macht die Schnittstelle ein wenig ähnlich wie bei den Merkmalen, aber die zu erreichenden Ziele sind unterschiedlich. Mit der Standardmethode können Entwickler vorhandene Schnittstellen neue Methoden hinzufügen, ohne die binäre Kompatibilität zu brechen, dh keine Klassen, die die Schnittstelle implementieren, um die neu hinzugefügte Methode gleichzeitig zu implementieren.
Der Unterschied zwischen einer Standardmethode und einer abstrakten Methode besteht darin, dass eine abstrakte Methode implementiert werden muss, während eine Standardmethode nicht der Fall ist. Die Standardmethoden der Schnittstelle werden von der Schnittstellen -Implementierungsklasse vererbt oder überschrieben. Der Beispielcode lautet wie folgt:
private interface verflüssigen {// Schnittstellen Erlauben Sie jetzt Standardmethoden, der Implementierer kann sie oder // möglicherweise nicht implementieren (überschreiben). Standard -Zeichenfolge notrequired () {return "Standardimplementierung"; }} private statische Klasse standardableAmpl implementiert defaulierbar {} private statische Klasse übersagte implementiert implementiert defaulierbar {@Override public String NotRequired () {return "überschriebene Implementierung";}}Die veraltbare Schnittstelle verwendet das Schlüsselwort Standard, um eine Standardmethode zu definieren, die Notrequired (). Die Standard -Klasse implementiert diese Schnittstelle und erbt standardmäßig die Standardmethoden in dieser Schnittstelle. Die überragende Klasse implementiert auch diese Schnittstelle, überschreibt jedoch die Standardmethoden der Schnittstelle und liefert eine andere Implementierung.
Ein weiteres interessantes Merkmal von Java 8 ist, dass statische Methoden in Schnittstellen definiert werden können. Der Beispielcode lautet wie folgt:
private interface fabauableFactory {// Schnittstellen ermöglichen nun statische Methoden statische Verzugsabfuhr (Lieferant <versiedelbar> Lieferant) {return lieferant.get ();}}Der folgende Code -Snippet integriert die Nutzungsszenarien von Standardmethoden und statischen Methoden:
public static void main (String [] args) {defaulaable default = offaulableFactory.create (defaultableImpl :: new); system.out.println (Standard.Die Ausgabe dieses Codes lautet wie folgt:
Standardimplementierung
Überschrieben Implementierung
Da die Implementierung der Standardmethode auf dem JVM Unterstützung auf der Bytecode -Ebene bietet, ist sie sehr effizient. Die Standardmethode ermöglicht verbesserte Schnittstellen, ohne das vorhandene Vererbungssystem zu brechen. Die Anwendung dieser Funktion in der offiziellen Bibliothek lautet: Hinzufügen neuer Methoden zur Schnittstelle java.util.collection, wie z.
Obwohl Standardmethoden so viele Vorteile haben, sollten sie in der tatsächlichen Entwicklung mit Vorsicht verwendet werden: In komplexen Vererbungssystemen können Standardmethoden Unklarheiten und Kompilierungsfehler verursachen. Wenn Sie weitere Details wissen möchten, lesen Sie bitte die offizielle Dokumentation.
2.3 Methodenreferenz
Die Methodenreferenz ermöglicht es Entwicklern, vorhandene Methoden, Java -Klassenkonstruktoren oder Instanzobjekte direkt zu referenzieren. Methodenreferenzen und Lambda -Ausdrücke werden in Verbindung miteinander verwendet, wodurch der Konstruktor der Java -Klasse ohne viele komplexe Vorlagencode kompakt und präzise aussieht.
In Simons Beispiel ist die CAR -Klasse ein Beispiel für verschiedene Methodenreferenzen, mit der die Leser zwischen vier Arten von Methodenreferenzen unterschieden werden können.
public static class Car {public static Car Create (endgültiger Lieferant <Car> Lieferant) {return lieferant.get ();} public static void kollegen (endgültiges Auto) {System.out.println ("Collided" + car.toString (); {System.out.println ("repariert" + this.toString ());}}Die Art der Referenz für die erste Methode ist eine Konstruktorreferenz, die Syntax ist Klasse :: Neu oder allgemeiner Form: Klasse <T> :: New. Hinweis: Dieser Konstruktor hat keine Parameter.
Final Car Car = Car.create (Car :: New); endgültige Liste <Car> cars = arrays.aSlist (Car);
Die Art der Referenz der zweiten Methode ist eine statische Methodenreferenz, und die Syntax ist Klasse :: static_method. HINWEIS: Diese Methode akzeptiert einen Parameter des Autotyps.
cars.foreach (Auto :: Collide);
Die dritte Methode bezieht sich auf den Typ, der auf eine Mitgliedsmethode einer bestimmten Klasse bezeichnet wird, und die Syntax ist Klasse :: Methode. Beachten Sie, dass diese Methode den Parameter nicht definiert:
cars.foreach (Auto :: Reparatur);
Die vierte Methode Referenced -Typ ist eine Referenz auf eine Mitgliedsmethode eines Instanzobjekts, und die Syntax ist eine Instanz :: Methode. HINWEIS: Diese Methode akzeptiert einen Parameter des Autotyps:
endgültige Autopolizei = Car.create (Car :: neu); cars.foreach (Polizei :: Follow);
Führen Sie das obige Beispiel aus und Sie können die folgende Ausgabe in der Konsole sehen (die CAR -Instanz kann unterschiedlich sein):
Collided com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d repariert com.javacodegeeks.java8.method.references com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d
Wenn Sie detailliertere Inhalte verstehen und erfahren möchten, können Sie sich auf die offizielle Dokumentation beziehen
2.4 Kommentare wiederholen
Seit der Einführung von Anmerkungen in Java 5 ist diese Funktion sehr beliebt geworden und wurde in verschiedenen Rahmenbedingungen und Projekten häufig verwendet. Anmerkungen haben jedoch eine große Einschränkung: Die gleiche Annotation kann nicht mehrmals an derselben Stelle verwendet werden. Java 8 bricht diese Einschränkung aus und führt das Konzept der wiederholten Anmerkungen ein, sodass die gleiche Annotation mehrmals an derselben Stelle verwendet werden kann.
Das Definieren von wiederholten Anmerkungen in Java 8 unter Verwendung von @repeatable Annotation ist eigentlich keine Verbesserung auf Sprachebene, sondern ein Trick des Compilers, und die zugrunde liegende Technologie ist immer noch dieselbe. Sie können den folgenden Code verwenden, um zu erklären:
Paket com.javacodegeeks.java8.repeatable.Annotations; Import Java.lang.Annotation.Elementtype; importieren java.lang.annotation.repeatable; importieren java.lang.annotation.retention; Import Java.lang.annotation.RetentionPolicy; importieren java.lang.annotation.target; öffentliche Klasse RepeatingAnnotations {@target (elementtype.type) @retention (retentionPolicy.runtime) public @Interface filter {filter [] value ();} @target (elementtype.typ) @retention (retentionPolicy.runtime) @repeatable (filters.typ) @Interface @Interface @Interface | @Filter ("filter1") @filter ("filter2") public interface filterable {} public static void main (String [] args) {für (filter filter: filterable.class.getAnnotationsBytype (filter.class)) {System.out.println (filter.value);Wie wir sehen können, verwendet die Filterklasse hier die Annotation @repeatable (filter.class), und Filter ist ein Container, der Filteranmerkungen speichert. Der Compiler versucht, diese Details von Entwicklern zu blockieren. Auf diese Weise kann die filterbare Schnittstelle mit zwei Filteranmerkungen kommentiert werden (hier werden keine Informationen zu Filtern erwähnt).
Darüber hinaus bietet die Reflexions -API eine neue Methode: getAnnotationsByType (), die zum Beispiel doppelte Anmerkungen eines bestimmten Typs zurückgeben kann
Filterable.class.getannoation (filter.class) gibt zwei Filterinstanzen zurück, und die Ausgabe des Inhalts in die Konsole lautet wie folgt:
filter1
filter2
Wenn Sie mehr wissen möchten, können Sie sich auf die offizielle Dokumentation beziehen.
2.5 Bessere Typinferenz
Der Java 8 -Compiler hat sich in der Typinferenz stark verbessert. In vielen Szenarien kann der Compiler den Datentyp eines bestimmten Parameters ableiten, wodurch der Code prägnanter wird. Der Beispielcode lautet wie folgt:
Paket com.javacodegeeks.java8.Type.inference; public class value <t> {public static <T> T defaultValue () {return null; } public t getOrdEfault (T -Wert, T defaultValue) {return (value! = null)? Wert: defaultValue;}}Der folgende Code ist eine Anwendung des Typwerts <string>:
Paket com.javacodegeeks.java8.Type.inference; public class typinference {public static void main (String [] args) {endgültig value <string> value = new Value <> (); value.getOrdefault ("22", value.defaultValue ());}}Der Typ des Parameterwerts.DefaultValue () wird vom Compiler abgeleitet und muss nicht explizit angegeben werden. In Java 7 hat dieser Code einen Kompilierungsfehler, es sei denn, der Wert. <string> defaultValue () wird verwendet.
2.6 Erweitern Sie die Anwendungsszenarien von Anmerkungen
Java 8 erweitert die Anwendungsszenarien von Anmerkungen. Jetzt können Annotationen für fast jedes Element verwendet werden: lokale Variablen, Schnittstellentypen, Superklassen und Schnittstellenimplementierungsklassen sowie auch für Ausnahmendefinitionen von Funktionen. Hier sind einige Beispiele:
Paket com.javacodegeeks.java8.Annotations; Import Java.lang.Annotation.Elementtype; importieren java.lang.annotation.retention; Import Java.lang.annotation.RetentionPolicy; importieren java.lang.annotation.target; Import Java.util.ArrayList; Import Java.util.Collection; public class Annotations { @Retention( RetentionPolicy.RUNTIME ) @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } ) public @interface NonEmpty { } public static class Holder< @NonEmpty T > extends @NonEmpty Object { public void method() throws @NonEmpty Exception { }} @SuppressWarnings ("unbenutzt") public static void main (String [] args) {Final Holder <string> Holder = new @NonEmpty Holder <string> (); @NonEmpty Collection <@NonEmpty String> Strings = New ArrayList <> (); }}Elementtype.type_user und elementtype.type_parameter sind zwei neue Anmerkungen zu Java 8 hinzugefügt, um die Nutzungsszenarien von Annotationen zu beschreiben. Java -Sprache
Yan nahm auch entsprechende Änderungen vor, um diese neu hinzugefügten Notizen zu identifizieren.
3.. Neue Funktionen des Java -Compilers
3.1 Parametername
Um die Parameternamen von Methoden in Java -Programmen zur Laufzeit zu erhalten, müssen ältere Generationen von Java -Programmierern unterschiedliche Methoden wie Paranamer Liberary verwenden. Java 8 normalisiert diese Funktion schließlich mit Unterstützung auf Sprachebene (unter Verwendung der Reflexions -API und der Parameter.getName () -Methode) und der Bytecode -Ebene (unter Verwendung des neuen Javac -Compilers und des Parameters -Parameters).
Paket com.javacodegeeks.java8.parameter.names; import Java.lang.reflect.Method; importieren java.lang.reflect.parameter; public class parameTernames {public static void main (String [] args) löst Ausnahme aus {method method = parameTernames.class.getMethod ("main", string []. Klasse); für (endgültiger Parameter Parameter: method.getParameters ()) {System.out.println ("Parameter:" + parameter.getName ());}}}In Java 8 wird diese Funktion standardmäßig ausgeschaltet. Wenn Sie also den obigen Code ohne den Parameters -Parameter -Parameter kompilieren und ausführen, werden die folgenden Ergebnisse ausgegeben:
Parameter: Arg0
Wenn der Parameters -Parameter verwendet wird, wird das folgende Ergebnis ausgegeben (korrektes Ergebnis):
Parameter: args
Wenn Sie Maven für die Projektverwaltung verwenden, können Sie den Parameters-Parameter-Parameter im Maven-Compiler-Plugin-Compiler-Konfigurationselement konfigurieren:
<plugin> <GroupId> org.apache.maven.plugins </GroupId> <artifactId> maven-compiler-plugin </artifactId> <version> 3.1 </Version> <configuration> <CompilerArgument> -Parameters </compilerArgument> <quellungen> 1,8 </Quelle> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> 1.8 </garga> </configuration> </Quelle> <gargets> <Garget> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> <gargets> 1.8 </tarnargument> </scrite> <garget>
4. Neue Funktionen der offiziellen Bibliothek von Java
Java 8 hat viele neue Werkzeugkurse (Datums-/Zeitklassen) hinzugefügt und vorhandene Werkzeugkurse erweitert, um die moderne gleichzeitige Programmierung, funktionale Programmierung usw. zu unterstützen.
4.1 Optional
Der häufigste Fehler in Java -Anwendungen sind Nullwertausnahmen. Vor Java 8 stellte Google Guava die Optionalsklasse zur Lösung von NullPointerexception vor, wodurch der Quellcode von verschiedenen Nullprüfungen kontaminiert wird, damit Entwickler einen saubereren Code schreiben können. Java 8 fügt auch der offiziellen Bibliothek optional hinzu.
Optional ist einfach einfach: Speichern Sie einen Wert von Typ T oder NULL. Es bietet einige nützliche Schnittstellen, um eine explizite Nullüberprüfung zu vermeiden. Weitere Informationen finden Sie in der offiziellen Dokumentation Java 8.
Schauen wir uns als Nächstes ein Beispiel für die Verwendung optional an: einen Wert, der leer ist, oder einen Wert eines bestimmten Typs:
Optional <string> fullname = optional.ofnullable (null); System.out.println ("Vollständiger Name ist gesetzt?" System.out.println ("Vollständiger Name:" + fullname.orelseget (() -> "[keine]")); System.out.println (fullname.map (s -> "Hey" + S + "!") .Orelse ("Hey Fremder!"));Wenn die optionale Instanz einen Nicht-Null-Wert enthält, gibt die ISPresent () -Methode true zurück, andernfalls gibt sie false zurück. OrelseGet () -Methode, und die optionale Instanz hält NULL, sie kann den Standardwert akzeptieren, der durch einen Lambda -Ausdruck generiert wird. Die MAP () -Methode kann den Wert der vorhandenen optionalen Instanz in einen neuen Wert umwandeln. Die orelse () -Methode ähnelt der Methode orelseget (), gibt jedoch den bestandenen Standardwert zurück, wenn NULL gedrückt wird.
Die Ausgabeergebnisse des obigen Codes sind wie folgt:
Vollständiger Name ist festgelegt? Falsch Vollname: [Keine] Hey Fremder!
Schauen wir uns ein anderes einfaches Beispiel an:
Optional <string> firstName = optional.of ("tom"); System.out.println ("Vorname ist gesetzt?" + FirstName.ISpresent ()); System.out.println ("Vorname:" + firstname.orelseget (() -> "[keine]"); System.out.println (FirstName.Map (S -> "Hey" + S + "!") .Orelse ("Hey Stranger!")); System.out.println ();Die Ausgabe dieses Beispiels ist:
Vorname ist gesetzt? Wahrer Vorname: Tom Hey Tom!
Wenn Sie weitere Details wissen möchten, lesen Sie bitte die offizielle Dokumentation.
4.2 Streams
Die neu hinzugefügte Stream -API (java.util.stream) führt die funktionale Programmierung der generierten Umgebung in die Java -Bibliothek ein. Dies ist bei weitem die größte Verbesserung der Java -Bibliotheken, sodass Entwickler effizientere, prägnantere und kompakte Code schreiben können.
Die Steam -API vereinfacht die Sammlungsoperationen erheblich (wir werden später mehr als nur Sammlungen sehen). Schauen wir uns zunächst diese Klasse namens Task an:
öffentliche Klasse Streams {private Enum Status {offen, geschlossen}; private statische endgültige Klassenaufgabe {privater endgültiger Status; private endgültige Ganzzahlpunkte; Task (endgültiger Status, endgültige Ganzzahlpunkte) {this.status = Status; this.points = points;} public Integer getPoints () {return points;} öffentlicher Status getStatus () {return status;} @Override public String toString () {return string.format ("[ %s, %d]", Status, Punkte);}}}}}}}}}}}}}}}}}Die Task-Klasse hat ein Bruchkonzept (oder die Pseudo-Komplexität), und es gibt zwei andere Zustände: offen oder geschlossen. Nehmen wir nun an, es gibt eine Aufgabensammlung:
endgültige Sammlung <Task> tasks = arrays.aslist (neue Aufgabe (Status.Open, 5), neue Aufgabe (Status.Open, 13), neue Aufgabe (Status.closed, 8));
Schauen wir uns zunächst eine Frage an: Wie viele offene Staatspunkte gibt es in dieser Aufgabensammlung? Um dieses Problem zu lösen, müssen Sie vor Java 8 die Aufgabensammlung anschließen, um die Aufgabensammlung zu überschreiten. In Java 8 können Sie jedoch Steams verwenden, um sie zu lösen: Fügen Sie eine Liste einer Reihe von Elementen ein und unterstützen die sequentielle und parallele Verarbeitung.
// Berechnen Sie die Gesamtpunkte aller aktiven Aufgaben unter Verwendung von sum () endgültiger langer Gesamtpointsofopentasks = tasks.stream (). Filter (Task -Task.getStatus () == Status.open).
Die Konsolenausgabe für das Ausführen dieser Methode lautet:
Gesamtpunkte: 18
Es gibt viele Wissenspunkte, über die es wert ist, hier zu sprechen. Erstens werden die festgelegten Aufgaben in eine Steam -Darstellung umgewandelt. Zweitens filtert der Filterbetrieb an den Dampfaufgaben alle geschlossenen Aufgaben; Drittens wandelt der Maptoint -Vorgang den Aufgabenstrom in eine Ganzzahlsammlung um, basierend auf der Aufgabe :: Getpoints -Methode jeder Taskinstanz; Schließlich wird die Summe nach der Summenmethode berechnet, um das Endergebnis zu erhalten.
Bevor Sie das nächste Beispiel lernen, müssen Sie sich einige Wissenspunkte über Dampfs erinnern (klicken Sie hier, um weitere Informationen zu erhalten). Operationen über Dampf können in Zwischenoperationen und verspäteten Operationen unterteilt werden.
Zwischenvorgänge geben einen neuen Dampf zurück. Durch die Durchführung eines Zwischenbetriebs (z. B. Filter) wird der tatsächliche Filterbetrieb nicht durchgeführt, sondern einen neuen Dampf erstellen und die Elemente, die die Bedingungen im ursprünglichen Dampf erfüllen, in den neu erstellten Dampf einfügen.
Verspätete Operationen (wie z. B. für Each oder Summe) durchqueren den Dampf und erzielen Ergebnisse oder begleitende Ergebnisse. Nach der Ausführung der verspäteten Operationen wurde die Dampfverarbeitungslinie verarbeitet und kann nicht verwendet werden. In fast allen Fällen durchqueren verspätete Operationen den Dampf sofort.
Ein weiterer Wert von Steam ist die kreative Unterstützung für die parallele Verarbeitung. Für die obige Aufgabenerfassung können wir den folgenden Code verwenden, um die Summe der Punkte aller Aufgaben zu berechnen:
// Gesamtpunkte aller Aufgaben Finale doppelte Gesamtpunkte = tasks.stream (). Parallel (). Map (Aufgabe -> task.getPoints ()) // oder map (Task :: getPoints) .Reduce (0, Integer :: sum);
Hier verwenden wir die parallele Methode, um alle Aufgaben parallel zu verarbeiten und das Endergebnis mithilfe der Reduzierung der Methode zu berechnen. Die Konsolenausgabe ist wie folgt:
Gesamtpunkte (alle Aufgaben): 26,0
Für eine Sammlung ist es oft notwendig, Elemente in sie gemäß bestimmten Bedingungen zu gruppieren. Diese Art von Aufgabe kann mit der von Steam bereitgestellten API schnell abgeschlossen werden. Der Code ist wie folgt:
// Gruppenaufgaben nach ihrem Status Final Map <Status <Status, List <Task>> map = tasks.stream (). Sammeln (collectors.groupingBy (Task :: getStatus)); System.out.println (MAP);
Die Ausgabe der Konsole ist wie folgt:
{Geschlossen = [[geschlossen, 8]], öffnen = [[offen, 5], [offen, 13]]}
Die letzte Beispielfrage zur Sammlung von Aufgaben lautet: Wie berechnet man den Anteil der Punkte jeder Aufgabe in der Sammlung in der Sammlung. Der spezifische Verarbeitungscode ist wie folgt:
// Berechnen Sie das Gewicht der einzelnen Aufgaben (als Prozentsatz der Gesamtpunkte) endgültiger Sammlung <string> result = tasks.stream () // Stream <string> .Maptoint (Task :: Getpoints) // intstream .aSlongstream () // Longstream. )) // longstream .maptoObj (Prozentsatz -> Prozent + "%") // Stream <string> .Collect (sammel.tolist ()); // list <string> system.out.println (Ergebnis);
Die Ergebnisse der Konsolenausgabe lauten wie folgt:
[19%, 50%, 30%]
Wie bereits erwähnt, kann die Steam -API nicht nur auf Java -Sammlungen reagieren, sondern traditionelle IO -Operationen (lesen Sie Daten aus einer Datei oder einer Netzwerklinie für Linie) von der Dampfverarbeitung profitieren. Hier ist ein kleines Beispiel:
Final Path Path = New Datei (Dateiname) .Topath (); try (stream <string> line = Dateien.lines (Pfad, StandardCharets.utf_8)) {line.onclose (() -> System.out.println ("Done!") .foreach (system.out :: println);};};};};Stream -Methode Onclose gibt einen äquivalenten Stream mit einem zusätzlichen Griff zurück. Dieser Handle wird ausgeführt, wenn die Close () -Methode des Streams aufgerufen wird. Die Stream -API, Lambda -Ausdrücke und Methodenreferenzen, die durch Interface -Standardmethoden und statische Methoden unterstützt werden, sind die Reaktion von Java 8 auf das moderne Paradigma der Softwareentwicklung.
4.3 Datum/Uhrzeit API (JSR 310)
Java 8 führt eine neue Datum-Zeit-API (JSR 310) ein, um die Zeit und die Daten zu verbessern. Zeit- und Datumsmanagement war für Java -Entwickler immer das schmerzhafteste Problem. java.util.date und später java.util.calendar haben dieses Problem nicht gelöst (noch mehr von den Entwicklern verwirrt).
Aus den oben genannten Gründen wurde die Joda-Time-Bibliothek der Drittanbieter geboren, die die Zeitmanagement-API von Java ersetzen kann. Die neue Zeit- und Datumsmanagement-API in Java 8 ist tief durch Joda-Zeit beeinflusst und hat einen Großteil der Essenz der Joda-Zeit aufgenommen. Das neue Java.Time -Paket enthält alle Klassen über Datum, Uhrzeit, Zeitzone, Instant (ähnlich bis Datum, aber genau wie Nanosekunden), Dauer (Dauer) und Taktoperationen. Die neu gestaltete API berücksichtigt ernsthaft die Invarianz dieser Klassen (Vorlesungen, die von java.util.calendar gelernt wurden), und gibt ein neues Objekt zurück, wenn eine Instanz geändert werden muss.
Schauen wir uns die Schlüsselklassen und ihre jeweiligen Nutzungsbeispiele im Java -Time -Paket an. Erstens verwendet die Clock -Klasse eine Zeitzone, um die aktuelle Zeit und Datum der aktuellen Nanosekunden zurückzugeben. Clock kann System.CurrentTimemillis () und TimeZone.getDefault () ersetzen.
// Die Systemuhr als UTC -Offset Final Taktuhr = clock.SystemUtc (); System.out.println (clock.instant ()); System.out.println (clock.millis ()) erhalten;
Die Ausgabe dieses Beispiels ist:
2014-04-12t15: 19: 29.282z 1397315969360
Zweitens, konzentrieren Sie sich auf Lokaldate- und Lokalzeitklassen. LocalDate enthält nur das Datumsteil im ISO-8601-Kalendersystem. Lokalzeit enthält nur den Zeitteil im Kalendersystem. Objekte beider Klassen können mit Taktobjekten erstellt werden.
// Erhalten Sie das lokale Datum und die lokale Zeit endgültig localDate Datum = localDate.now (); Final localDate Datumfromclock = localDate.now (Uhr); System.out.println (Datumfromclock); // Erhalten Sie das lokale Datum und die lokale Zeit endgültige lokale Zeit = localTime.now (); Final Local Time TimeFromClock = LocalTime.Now (Uhr); System.out.println (Zeit); System.out.println (Zeitfromclock);
Die Ausgabeergebnisse des obigen Beispiels sind wie folgt:
2014-04-12 2014-04-12 11: 25: 54.568 15: 25: 54.568
Die LocalDatetime-Klasse enthält Informationen zu LocalDate und LocalTime, enthält jedoch keine Zeitzoneninformationen im ISO-8601-Kalendersystem. Hier sind einige Beispiele zu LocalDate und LocalTime:
// Erhalten Sie das lokale Datum/die Uhrzeit endgültig localDateTime datetime = localDatetime.now (); endgültig localDateTime datetimeFromClock = localDateTime.now (Uhr); System.out.println (DateTime); System.out.println (DateTimeFromClock);
Die Ausgabeergebnisse des obigen Beispiels sind wie folgt:
2014-04-12t11: 37: 52.309 2014-04-12t15: 37: 52.309
Wenn Sie Daten/Zeitinformationen für eine bestimmte Zeitzone benötigen, können Sie ZonedAtetime verwenden, die das Datum und die Uhrzeit des ISO-8601-Datumssystems enthält und Zeitzoneninformationen enthält. Hier sind einige Beispiele für die Verwendung verschiedener Zeitzonen:
// Erhalten Sie das Zonendatum/die Zeit endgültige zoneddatetime zonedDatetime = zonedDatetime.now (); endgültige zonedDatetime zonedDatetimeFromClock = zonedDatetime.now (Uhr); endgültige zoneddatetime zonedDateTimeFromzone = zonedDatetime.now (zoneId.of ("America/los_angeles")); System.out.println (ZonedDatetime); System.out.println (ZonedDateTimeFromClock); System.out.println (ZonedDateTimeFromClock); System.out.println (zonedDateTimeFromzone);Die Ausgabe dieses Beispiels ist:
2014-04-12t11: 47: 01.017-04: 00 [Amerika/New_york] 2014-04-12t15: 47: 01.017Z 2014-04-12t08: 47: 01.017-07: 00 [America/los_angeles]]
Schauen wir uns schließlich die Dauerklasse an, die die Zeit bis Sekunden und Nanosekunden hält. Dies erleichtert die Berechnung des Unterschieds zwischen zwei Daten. Der Beispielcode lautet wie folgt:
// Dauer zwischen zwei Daten endgültig localDatetime von = localDatetime.of (2014, Monat.April, 16, 0, 0, 0); Final LocalDatetime to = localDateTime.of (2015, Monat, Monat, 16, 23, 59, 59); endgültige Dauer = Dauer. duration.todays ());
Dieses Beispiel wird verwendet, um die Anzahl der Tage und Stunden zwischen dem 16. April 2014 und dem 16. April 2015 zu berechnen, und die Ausgabe ist wie folgt:
Dauer in Tagen: 365 Dauer in Stunden: 8783
Der allgemeine Eindruck von Java 8s neuem Datum und Uhrzeit ist relativ positiv, teilweise aufgrund der positiven Auswirkungen der Joda-Zeit und teilweise, weil der Beamte schließlich die Bedürfnisse der Entwickler zuhörte. Wenn Sie weitere Details wissen möchten, können Sie sich auf die offizielle Dokumentation beziehen.
4.4 Nashorn JavaScript Engine
Java 8 bietet die neue JavaScript -Engine von Nashorn, mit der wir JS -Anwendungen auf der JVM entwickeln und ausführen können. Nashorn JavaScript Engine ist eine weitere Implementierungsversion von javax.script.scriptengine. Diese Art von Skript -Engine folgt den gleichen Regeln und ermöglicht es, Java und JavaScript interaktiv zu verwenden. Der Beispielcode lautet wie folgt:
ScripTenGineManager Manager = new ScriptenGineManager (); scripTengine Engine = Manager.GetEnbyName ("javaScript"); System.out.println (Engine.GetClass (). GetName ()); System.out.Out.println ("Ergebnis:" + Engine.eval ("Funktion f () {{{{{};Die Ausgabe dieses Codes lautet wie folgt:
jdk.nashorn.api.Scripting.nashornscripteng -Ergebnisse: 2
4.5 Basis64
Die offizielle Java 8-Bibliothek wurde die Unterstützung für die Base64-Codierung hinzugefügt, sodass Base64-Codierung ohne Verwendung einer Drittanbieterbibliothek durchgeführt werden kann. Der Beispielcode lautet wie folgt:
Paket com.javacodegeeks.java8.base64; import Java.nio.charset.StandardCharets; import Java.util.base64; public class base64s {public static void main (String [] args) {Final String text = "Base64 Endlich in Java 8!"; Final String coded = base64.getEcoder (). codetoString (text.getBytes (Standardcharets.utf_8)); System.out.println (codiert); Final String decoded = new String (Basis64.getDeCoder (). Decode (codiert), Standardcharets.utf_8); System.out.println (decodiert);}}Die Ausgabe dieses Beispiels ist wie folgt:
Qmfzzty0Igzpbmfsbhkgaw4gsmf2ysa4iq ==
Base64 Endlich in Java 8!
Der neue Base64API unterstützt auch die Codierung und Dekodierung von URLs und Minen.
(Base64.geturlencoder () / Base64.geturldeCoder (), Base64.getMimeCoder () / Base64.getMimedEcoder ()).
4.6 Parallele Arrays
Die Java8 -Version hat viele neue Methoden hinzugefügt, um die parallele Array -Verarbeitung zu unterstützen. Die wichtigste Methode ist Parallelsort (), die das Array-Sortieren von Array-Maschinen erheblich beschleunigen kann. Das folgende Beispiel zeigt die Methode der Parallelexxxx -Serie:
Paket com.javacodegeeks.java8.Parallel.Arrays; Import Java.util.Arrays; Import Java.util.Concurrent.ThreadLocalRandom; public class parallelArrays {public static void main (String [] args) {long [] arrayoflong = new long [20000]; Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt( 1000000 ) );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();Arrays.parallelSort( arrayOfLong );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();}}上述这些代码使用parallelSetAll()方法生成20000个随机数,然后使用parallelSort()方法进行排序。这个程序会输出乱序数组和排序数组的前10个元素。上述例子的代码输出的结果是:
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 Sorted: 39 220 263 268 325 607 655 678 723 793
4.7 并发性
基于新增的lambda表达式和steam特性,为Java 8中为java.util.concurrent.ConcurrentHashMap类添加了新的方法来支持聚焦操作;另外,也为java.util.concurrentForkJoinPool类添加了新的方法来支持通用线程池操作(更多内容可以参考我们的并发编程课程)。
Java 8还添加了新的java.util.concurrent.locks.StampedLock类,用于支持基于容量的锁――该锁有三个模型用于支持读写操作(可以把这个锁当做是java.util.concurrent.locks.ReadWriteLock的替代者)。
在java.util.concurrent.atomic包中也新增了不少工具类,列举如下:
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
5. 新的Java工具
Java 8提供了一些新的命令行工具,这部分会讲解一些对开发者最有用的工具。
5.1 Nashorn引擎:jjs
jjs是一个基于标准Nashorn引擎的命令行工具,可以接受js源码并执行。例如,我们写一个func.js文件,内容如下:
function f() { return 1; }; print( f() + 1 );可以在命令行中执行这个命令:jjs func.js,控制台输出结果是:
2
如果需要了解细节,可以参考官方文档。
5.2 类依赖分析器:jdeps
jdeps是一个相当棒的命令行工具,它可以展示包层级和类层级的Java类依赖关系,它以.class文件、目录或者Jar文件为输入,然后会把依赖关系输出到控制台。
我们可以利用jedps分析下Spring Framework库,为了让结果少一点,仅仅分析一个JAR文件:org.springframework.core-3.0.5.RELEASE.jar。
jdeps org.springframework.core-3.0.5.RELEASE.jar
这个命令会输出很多结果,我们仅看下其中的一部分:依赖关系按照包分组,如果在classpath上找不到依赖,则显示”not found”.
org.springframework.core-3.0.5.RELEASE.jar -> C:/Program Files/Java/jdk1.8.0/jre/lib/rt.jarorg.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)-> java.io -> java.lang -> java.lang.annotation -> java.lang.ref -> java.lang.reflect -> java.util -> java.util.concurrent -> org.apache.commons.logging not found-> org.springframework.asm not found-> org.springframework.asm.commons not foundorg.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)-> java.lang -> java.lang.annotation -> java.lang.reflect -> java.util
更多的细节可以参考官方文档。
6. JVM的新特性
使用Metaspace(JEP 122)代替持久代(PermGen space)。在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。
7. Schlussfolgerung
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。