Préface: Java 8 est publié depuis un certain temps, et tous les signes indiquent que Java 8 est un changement majeur de distribution. Il existe déjà de nombreux articles sur les geeks de code Java qui introduisent de nouvelles fonctionnalités de Java 8, comme jouer avec Java 8 Lambdas et concurrence, Java 8 Date Heure API Tutoriel: LocalDatetime and Abstract Class contre Interface in the JDK 8 ERA. Cet article fait également référence à d'autres informations, telles que: 15 Doit lire les didacticiels Java 8 et le côté obscur de Java 8. Cet article a compilé les informations ci-dessus et l'a compilée dans un manuel de référence sur les nouvelles fonctionnalités de Java 8. J'espère que vous gagnerez quelque chose.
1. Introduction
Il ne fait aucun doute que Java 8 est la version la plus importante de Java depuis Java 5 (publiée en 2004). Cette version contient plus d'une douzaine de nouvelles fonctionnalités dans les langues, les compilateurs, les bibliothèques, les outils et JVM. Dans cet article, nous apprendrons ces nouvelles fonctionnalités et utiliserons des exemples pratiques pour illustrer quels scénarios conviennent à une utilisation.
Ce tutoriel contient plusieurs types de problèmes auxquels les développeurs Java sont souvent confrontés:
langue
Compilateur
Bibliothèque
outil
Runtime (JVM)
2. Nouvelles fonctionnalités de la langue java
Java 8 est une version majeure de Java. Certaines personnes croient que bien que ces nouvelles fonctionnalités soient attendues par les développeurs Java, cela nécessite également beaucoup d'efforts pour apprendre. Dans cette section, nous présenterons la plupart des nouvelles fonctionnalités de Java 8.
2.1 Expressions Lambda et interfaces fonctionnelles
Les expressions Lambda (également appelées fermetures) sont les changements de langage les plus importants et les plus attendus dans Java 8. Il nous permet de transmettre des fonctions comme des paramètres à une méthode, ou de traiter le code lui-même comme des données: les développeurs fonctionnels connaissent très bien ces concepts. De nombreuses langues sur les plates-formes JVM (Groovy, Scala, etc.) ont soutenu les expressions de Lambda depuis leur naissance, mais les développeurs Java n'ont d'autre choix que d'utiliser des classes internes anonymes au lieu des expressions de lambda.
La conception de Lambda a pris beaucoup de temps et beaucoup d'efforts communautaires, et a finalement trouvé une solution de mise en œuvre de compromis qui pourrait réaliser une structure linguistique simple et compacte. L'expression lambda la plus simple peut être composée d'une liste de paramètres séparée par des virgules, -> symbole et bloc de déclaration, par exemple:
Arrays.aslist ("a", "b", "d") .ForEach (e -> System.out.println (e));
Dans le code ci-dessus, le type de paramètre E est dérivé par le raisonnement du compilateur, et vous pouvez également spécifier explicitement le type de paramètre, par exemple:
Arrays.aslist ("a", "b", "d") .ForEach ((String e) -> System.out.println (e));
Si une expression de lambda nécessite un bloc de déclaration plus complexe, vous pouvez enfermer le bloc d'instruction avec des accolades bouclées, similaires au corps d'une fonction en Java, par exemple:
Arrays.aslist ("a", "b", "d") .ForEach (e -> {System.out.print (e); System.out.print (e);});Les expressions de Lambda peuvent faire référence aux membres de la classe et aux variables locales (qui convertiront implicitement ces variables en final), par exemple, les deux blocs de code suivants ont exactement le même effet:
String séparateur = ","; arrays.aslist ("a", "b", "d") .ForEach ((String e) -> System.out.print (e + séparateur));et
Final String séparateur = ","; arrays.aslist ("a", "b", "d") .ForEach ((String e) -> System.out.print (e + séparateur));Les expressions Lambda ont des valeurs de retour et le type de valeurs de retour est également dérivé par l'inférence du compilateur. Si le bloc d'instruction dans l'expression de Lambda n'a qu'une seule ligne, vous ne pouvez pas utiliser l'instruction RETOUR. Les deux extraits de code suivants ont le même effet:
Arrays.aslist ("a", "b", "d") .sort ((e1, e2) -> e1.compareto (e2));et
Arrays.aslist ("a", "b", "d") .sort ((e1, e2) -> {int result = e1.compareto (e2); return result;});Afin de rendre les fonctions existantes bien compatibles avec les expressions de lambda, les concepteurs de lambda ont considéré de nombreuses méthodes, ils ont donc trouvé le concept d'interface de fonction. L'interface de fonction fait référence à une interface avec une seule fonction, et une telle interface peut être implicitement convertie en expression de lambda. java.lang.runnable et java.util.concurrent. callable sont les meilleurs exemples d'interfaces fonctionnelles. En pratique, les interfaces fonctionnelles sont très fragiles: tant qu'un développeur ajoute une fonction à l'interface, l'interface n'est plus une interface fonctionnelle, entraînant une panne de compilation. Afin de surmonter cette vulnérabilité de niveau de code et indique explicitement qu'une interface est une interface fonctionnelle, Java 8 fournit une annotation spéciale @FunctionalInterface (toutes les interfaces associées dans la bibliothèque Java ont déjà cette annotation), pour donner une définition simple d'une interface fonctionnelle:
@FunctionalInterface Interface publique fonctionnelle {void méthode ();}Cependant, une chose à noter est que la méthode par défaut et la méthode statique ne détruiront pas la définition de l'interface fonctionnelle, donc le code suivant est légal.
@FunctionalInterface Interface publique functionaldefaultMethods {void method (); par défaut void defaultMethod () {}}Lambda Expressions, en tant que plus grand argument de vente de Java 8, a le potentiel d'attirer plus de développeurs pour rejoindre la plate-forme JVM et utiliser le concept de programmation fonctionnelle dans la programmation Java pure. Si vous avez besoin d'en savoir plus sur les expressions de Lambda, vous pouvez vous référer à la documentation officielle.
2.2 Méthodes par défaut et statiques d'interfaces
Java 8 utilise deux nouveaux concepts pour étendre la signification d'une interface: la méthode par défaut et la méthode statique. La méthode par défaut rend l'interface un peu similaire aux traits, mais les objectifs à atteindre sont différents. La méthode par défaut permet aux développeurs d'ajouter de nouvelles méthodes aux interfaces existantes sans casser la compatibilité binaire, c'est-à-dire ne pas forcer des classes qui implémentent l'interface pour implémenter la méthode nouvellement ajoutée en même temps.
La différence entre une méthode par défaut et une méthode abstraite est qu'une méthode abstraite doit être implémentée, contrairement à une méthode par défaut. Les méthodes par défaut fournies par l'interface seront héritées ou écrasées par la classe d'implémentation de l'interface. L'exemple de code est le suivant:
Interface privée Defaulable {// Les interfaces permettent désormais les méthodes par défaut, l'implémenteur peut ou // ne peut pas les implémenter (remplacer). String par défaut Notrequired () {return "Implémentation par défaut"; }} Classe statique privée DefaultableImplt implémente Defaulable {} Classe statique privée OverridableIMPl implémente Defaulable {@Override public String Notrequired () {return "Overridden Implementation";}}L'interface défautable utilise le mot-clé par défaut pour définir une méthode par défaut NotRequired (). La classe de défautable implémente cette interface et hérite des méthodes par défaut dans cette interface par défaut; La classe OverridableIMPL implémente également cette interface, mais remplace les méthodes par défaut de l'interface et fournit une implémentation différente.
Une autre caractéristique intéressante apportée par Java 8 est que les méthodes statiques peuvent être définies dans les interfaces. L'exemple de code est le suivant:
Interface privée DefaulableFactory {// Les interfaces permettent désormais à des méthodes statiques statiques déchaulables (fournisseur <faulable> fournisseur) {return fournisseur.get ();}}L'extrait de code suivant intègre les scénarios d'utilisation des méthodes par défaut et des méthodes statiques:
public static void main (String [] args) {Defaulaable Default = DefaulableFactory.Create (DefaultableImpl :: new); System.out.println (Defaultable.NotRequired ()); Defaulaable = DefaulableFactory.Create (OverdiablemplLa sortie de ce code est la suivante:
Implémentation par défaut
Implémentation remplacée
Étant donné que l'implémentation de la méthode par défaut sur le JVM fournit une prise en charge au niveau du bytecode, elle est très efficace. La méthode par défaut permet des interfaces améliorées sans casser le système d'héritage existant. L'application de cette fonctionnalité dans la bibliothèque officielle est: Ajoutez de nouvelles méthodes à l'interface java.util.collection, telles que Stream (), ParallelStream (), ForeEach () et DeVoIf (), etc.
Bien que les méthodes par défaut aient tellement d'avantages, elles doivent être utilisées avec prudence dans le développement réel: dans les systèmes d'héritage complexes, les méthodes par défaut peuvent provoquer des erreurs d'ambiguïté et de compilation. Si vous souhaitez en savoir plus, veuillez vous référer à la documentation officielle.
2.3 Référence de la méthode
La référence de la méthode permet aux développeurs de référencer directement les méthodes existantes, les constructeurs de classe Java ou les objets d'instance. Les références de méthode et les expressions de lambda sont utilisées en conjonction les unes avec les autres, ce qui rend le constructeur de la classe Java compacte et concise, sans beaucoup de code de modèle complexe.
Dans l'exemple de Simon, la classe de voiture est un exemple de différentes références de méthode, qui peuvent aider les lecteurs à distinguer quatre types de références de méthode.
Classe statique publique Car {public static Car Create (Final Fournisseur <CAR> Fournisseur) {return fournisseur.get ();} public static void collègue (finale Car Car) {System.out.prid Suivre (finale de la voiture " {System.out.println ("réparé" + this.toString ());}}Le type de référence pour la première méthode est une référence du constructeur, la syntaxe est class :: nouveau, ou plus général Form: Class <T> :: Nouveau. Remarque: Ce constructeur n'a pas de paramètres.
voiture finale Car = Car.Create (Car :: NOUVEAU); Liste finale <CAR> CARS = Arrays.aslist (Car);
Le type de référence de la deuxième méthode est une référence de méthode statique, et la syntaxe est class :: static_method. Remarque: Cette méthode accepte un paramètre de type de voiture.
caritres
La troisième méthode fait référence au type est une référence à une méthode membre d'une certaine classe, et la syntaxe est la méthode Class ::. Notez que cette méthode ne définit pas le paramètre:
cariturs.ForEach (voiture :: réparation);
Le type de quatrième méthode référencé est une référence à une méthode membre d'un objet d'instance, et la syntaxe est instance :: Méthode. Remarque: Cette méthode accepte un paramètre de type de voiture:
Police de voiture finale = Car.Create (Car :: NOUVEAU); CARS.FORIEACH (Police :: Suivre);
Exécutez l'exemple ci-dessus et vous pouvez voir la sortie suivante dans la console (l'instance de voiture peut être différente):
Collide com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d réparée com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d après le suivant le suivant le suivant le suivant le suivant le suivant le suivant le suivant le suivi de com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d
Si vous souhaitez comprendre et apprendre plus de contenu détaillé, vous pouvez vous référer à la documentation officielle
2.4 Répéter les commentaires
Depuis l'introduction des annotations dans Java 5, cette fonctionnalité est devenue très populaire et a été largement utilisée dans divers cadres et projets. Cependant, les annotations ont une grande limitation: la même annotation ne peut pas être utilisée plusieurs fois au même endroit. Java 8 brise cette limitation et introduit le concept d'annotations répétées, permettant à la même annotation d'être utilisée plusieurs fois au même endroit.
La définition d'annotations répétées dans Java 8 à l'aide de l'annotation @Repeatable n'est en fait pas une amélioration au niveau du langage, mais une astuce faite par le compilateur, et la technologie sous-jacente est toujours la même. Vous pouvez utiliser le code suivant pour expliquer:
package com.javacodegeeks.java8.repeatable.annotations; import java.lang.annotation.elementType; import java.lang.annotation.repeatable; import java.lang.annotation.retention; Importer java.lang.annotation.retentionPolicy; import java.lang.annotation.target; classe publique Reperingannotations {@target (elementType.Type) @retention (RetenderPolicy.Runtime) public @Interface Filters {Filter [] Value ();} @target (elementType.type) @retention (rétentionPolicy.Runtime) @repeAtable; @Filter ("filter1") @Filter ("Filter2") Interface publique Filterable {} public static void main (String [] args) {for (filter filter: filterable.class.getannotationsbytype (filter.class)) {system.out.println (filter.valie ());}}}}Comme nous pouvons le voir, la classe de filtre ici utilise l'annotation @repeatable (filters.class), et Filtres est un conteneur qui stocke les annotations de filtre. Le compilateur essaie de bloquer ces détails des développeurs. De cette façon, l'interface filtrable peut être annotée avec deux annotations de filtre (aucune information sur les filtres n'est mentionnée ici).
De plus, l'API de réflexion fournit une nouvelle méthode: getAnnotationsByType (), qui peut renvoyer des annotations en double d'un certain type, par exemple
Filterable.class.getannoation (filters.class) renverra deux instances de filtre, et la sortie du contenu à la console est la suivante:
filtre1
filtre2
Si vous voulez en savoir plus, vous pouvez vous référer à la documentation officielle.
2,5 Meilleur type d'inférence
Le compilateur Java 8 a apporté de grandes améliorations dans l'inférence du type. Dans de nombreux scénarios, le compilateur peut déduire le type de données d'un certain paramètre, ce qui rend le code plus concis. L'exemple de code est le suivant:
package com.javacodegeeks.java8.type.inférence; Valeur de classe publique <T> {public static <T> t defaultValue () {return null; } public t getorDefault (t valeur, t defaultValue) {return (valeur! = null)? Valeur: DefaultValue;}}Le code suivant est une application de la valeur de type <string>:
package com.javacodegeeks.java8.type.inférence; classe publique TypeInference {public static void main (String [] args) {final value <string> value = new Value <> (); value.getOrdefault ("22", value.defaultValue ());}}Le type de valeur de paramètre.DefaultValue () est dérivé par le compilateur et n'a pas besoin d'être explicitement spécifié. Dans Java 7, ce code aura une erreur de compilation à moins que la valeur. <string> defaultValue () est utilisée.
2.6 Élargir les scénarios d'application des annotations
Java 8 élargit les scénarios d'application des annotations. Maintenant, les annotations peuvent être utilisées sur presque tous les éléments: variables locales, types d'interface, superclasses et classes d'implémentation d'interface, et même sur les définitions d'exception des fonctions. Voici quelques exemples:
package com.javacodegeeks.java8.annotations; import java.lang.annotation.elementType; import java.lang.annotation.retention; Importer java.lang.annotation.retentionPolicy; import java.lang.annotation.target; import java.util.arraylist; import java.util.collection; Classe publique Annotations {@retention (RetentionPolicy.Runtime) @target ({elementType.type_use, elementType.type_Parameter}) public @Interface Nonmpty {} public static static Holder <@NonEmpty T> extension @Nonempty Object {public Vend Method () throws @NonEmpty exception {}} @SuppressWarnings ("inutilisé") public static void main (String [] args) {Final Holder <string> Holder = new @NonEmpty Holder <string> (); @NonEmpty Collection <@NonEmpty String> Strings = new ArrayList <> (); }}ElementType.type_user et elementType.type_parameter sont deux nouvelles annotations ajoutées à Java 8 pour décrire les scénarios d'utilisation des annotations. Langue java
Yan a également apporté des modifications correspondantes pour identifier ces notes nouvellement ajoutées.
3. Nouvelles fonctionnalités du compilateur Java
3.1 Nom du paramètre
Afin d'obtenir les noms de paramètres des méthodes dans les programmes Java à l'exécution, les générations plus anciennes de programmeurs Java doivent utiliser différentes méthodes, telles que le libéraire de paranamère. Java 8 normalise enfin cette fonctionnalité, avec le support au niveau du langage (en utilisant l'API de réflexion et la méthode paramètre.getName ()) et le niveau de bytecode (en utilisant le nouveau compilateur Javac et le paramètre -Parameters).
package com.javacodegeeks.java8.paramètre.Names; import java.lang.reflect.method; import java.lang.reflect.paramètre; Classe publique Paramètres de paramètres {public static void main (string [] args) lève l'exception {méthode méthode = parameternames.class.getMethod ("main", string []. class); pour (paramètre final Paramètre: méthode.getParameters ()) {System.out.println ("Paramètre:" + paramètre.getName ());}}}Dans Java 8, cette fonctionnalité est désactivée par défaut, donc si vous compilez le code ci-dessus sans le paramètre -Parameters et l'exécutez, les résultats suivants seront sortis:
Paramètre: Arg0
Si le paramètre -Parameters est utilisé, le résultat suivant sera sorti (résultat correct):
Paramètre: Args
Si vous utilisez Maven pour la gestion de projet, vous pouvez configurer le paramètre -Parameters dans l'élément de configuration du compilateur Maven-Compiler-Plugin:
<flugin> <proupId> org.apache.maven.plugins </prômId> <Artifactid> Maven-Compiler-Plugin </ Artifactid> <Dersion> 3.1 </ Version> <FIFIGRATION> <COMPILERARGUMENT> -PARAMETERS </ COMPILERARGUMENT> </FECER> 1.8 </ Source> <Target> 1.8 </ Target>
4. Nouvelles fonctionnalités de la bibliothèque officielle de Java
Java 8 a ajouté de nombreuses nouvelles classes d'outils (classes de date / heure) et étendues de classes d'outils existantes pour prendre en charge la programmation simultanée moderne, la programmation fonctionnelle, etc.
4.1 Facultatif
Le bogue le plus courant dans les applications Java est les exceptions de valeur nulle. Avant Java 8, Google Guava a introduit la classe optionnelle pour résoudre NullPointerException, évitant ainsi que le code source soit contaminé par diverses vérifications nulles, afin que les développeurs puissent écrire du code plus propre. Java 8 ajoute également en option à la bibliothèque officielle.
Facultatif est juste une chose facile: stocker une valeur de type T ou NULL. Il fournit des interfaces utiles pour éviter une vérification nul explicite, et vous pouvez vous référer à la documentation officielle de Java 8 pour plus de détails.
Ensuite, jetons un coup d'œil à un exemple d'utilisation facultatif: une valeur qui peut être vide ou une valeur d'un certain type:
Facultatif <string> fullName = optional.ofnullable (null); System.out.println ("Le nom complet est défini?" + FullName.ispresent ()); System.out.println ("Nom complet:" + fullName.OrelSeget (() -> "[Aucun]")); System.out.println (fullname.map (s -> "hey" + s + "!") .Orelse ("Hey Stranger!"));Si l'instance facultative contient une valeur non nulle, la méthode isPresent () renvoie true, sinon il renvoie false; Méthode OrelSeget (), et l'instance facultative maintient NULL, il peut accepter la valeur par défaut générée par une expression de lambda; La méthode map () peut convertir la valeur de l'instance optionnelle existante en une nouvelle valeur; La méthode orelse () est similaire à la méthode orelseget (), mais renvoie la valeur par défaut passée lors de la maintenance nul.
Les résultats de sortie du code ci-dessus sont les suivants:
Le nom complet est défini? Faux nom complet: [Aucun] Hey Stranger!
Jetons un coup d'œil à un autre exemple simple:
Facultatif <string> FirstName = Facultatal.of ("Tom"); System.out.println ("Le prénom est défini?" + FirstName.ispresent ()); System.out.println ("prénom:" + firstName.OrelSeget (() -> "[Aucun]")); System.out.println (FirstName.map (S -> "Hey" + S + "!") .Orelse ("Hey Stranger!")); System.out.println ();La sortie de cet exemple est:
Le prénom est défini? Vraiment prénom: Tom Hey Tom!
Si vous souhaitez en savoir plus, veuillez vous référer à la documentation officielle.
4.2 Streams
L'API de flux nouvellement ajouté (java.util.stream) présente la programmation fonctionnelle de l'environnement généré dans la bibliothèque Java. Il s'agit de loin de la plus grande amélioration des bibliothèques Java afin que les développeurs puissent écrire du code plus efficace, concis et compact.
L'API Steam simplifie considérablement les opérations de collecte (nous verrons plus que des collections plus tard). Tout d'abord, jetons un coup d'œil à cette classe appelée Task:
public class streams {private Enum status {open, closed}; Tâche de classe finale statique privée {Statut final privé Statut; Points entiers finaux privés; tâche (statut de statut final, points entiers finaux) {this.status = statut; this.points = points;} public Integer getPoint () {return Points;} Statut public getStatus () {return status;} @Override public String toString () {return String.format ("[% s,% d]", statut, points);}}}La classe de tâches a un concept de fraction (ou pseudo-complexité), et il y a deux autres états: ouvert ou fermé. Supposons maintenant qu'il existe une collection de tâches:
Collection finale <Task> tasks = arrays.aslist (nouvelle tâche (status.open, 5), nouvelle tâche (status.open, 13), nouvelle tâche (status.closed, 8));
Tout d'abord, regardons une question: combien de points d'état ouvert y a-t-il dans cette collection de tâches? Avant Java 8, pour résoudre ce problème, vous devez utiliser Foreach pour faire une touche sur la collection de tâches; Cependant, dans Java 8, vous pouvez utiliser des vapeur pour le résoudre: inclure une liste d'une série d'éléments et prendre en charge le traitement séquentiel et parallèle.
// calculer les points totaux de toutes les tâches actives en utilisant sum () final long totalpointsofopentasks = tasks.stream (). Filter (tâche -> task.getStatus () == status.open) .maptInter (tâche :: getpoints) .sum (); System.out.println ("points totaux:" + totalPpointsofoPentasks);La sortie de la console pour l'exécution de cette méthode est:
Points totaux: 18
Il y a de nombreux points de connaissance qui valent la peine d'être en train de parler ici. Premièrement, l'ensemble des tâches est converti en représentation de vapeur; Deuxièmement, l'opération de filtre sur la vapeur filtre toutes les tâches fermées; Troisièmement, l'opération MaptOrt convertit le flux de tâches en une collection entière basée sur la méthode tâche :: getPoint de chaque instance de tâche; Enfin, la somme est calculée par la méthode SUM pour obtenir le résultat final.
Avant d'apprendre l'exemple suivant, vous devez vous souvenir de certains points de connaissances sur les vapeur (cliquez ici pour plus de détails). Les opérations au-dessus de la vapeur peuvent être divisées en opérations intermédiaires et en retard.
Les opérations intermédiaires renvoient une nouvelle vapeur - effectuer une opération intermédiaire (telle que le filtre) n'effectuera pas l'opération de filtrage réelle, mais créera une nouvelle vapeur et mettra les éléments qui remplissent les conditions de la vapeur d'origine dans la vapeur nouvellement créée.
Les opérations tardives (comme Forach ou Sum) traverseront la vapeur et obtiendront des résultats ou des résultats d'accompagnement; Après avoir effectué les opérations tardives, la ligne de traitement de vapeur a été traitée et ne peut pas être utilisée. Dans presque tous les cas, les opérations tardives traversent immédiatement la vapeur.
Une autre valeur de Steam est le support créatif pour le traitement parallèle. Pour la collection de tâches ci-dessus, nous pouvons utiliser le code suivant pour calculer la somme des points de toutes les tâches:
// Calculer les points totaux de toutes les tâches Double Final Double Pointts = Tasks.Stream (). Parallel (). Map (Task -> Task.getPoints ()) // ou Map (Task :: Getpoints) .reduce (0, entier :: SUM); System.out.println ("Total Points (tous les tasks):" + Totalpoints);Ici, nous utilisons la méthode parallèle pour traiter toutes les tâches en parallèle et calculer le résultat final en utilisant la méthode de réduction. La sortie de la console est la suivante:
Points totaux (toutes les tâches): 26.0
Pour une collection, il est souvent nécessaire de y regrouper des éléments en fonction de certaines conditions. Ce type de tâche peut être effectué rapidement en utilisant l'API fourni par Steam. Le code est le suivant:
// Tâches de groupe par leur statut Final Map <Status, List <Task>> Map = tasks.Stream (). Collect (Collectors.GroupingBy (tâche :: getStatus)); System.out.println (Map);
La sortie de la console est la suivante:
{Fermé = [[fermé, 8]], ouvert = [[ouvert, 5], [ouvert, 13]]}
Le dernier exemple de question sur la collection de tâches est: comment calculer la proportion de points de chaque tâche de la collection de la collection. Le code de traitement spécifique est le suivant:
// Calculez le poids de chaque tâches (en pourcentage du total des points) Collection finale <string> result = tasks.stream () // stream <string> .maptInter (task :: getpoints) // intStream .AsLongStream () // LongStream .Maptodouble (Points -> Points / Totalpoints) // DoubleStream. Boxed () // Stream <bouble> .MaptOl )) // longstream .maptoobj (pourcentage -> pourcentage + "%") // stream <string> .collect (Collectors.tolist ()); // list <string> System.out.println (résultat);
Les résultats de sortie de la console sont les suivants:
[19%, 50%, 30%]
Enfin, comme mentionné précédemment, l'API Steam peut non seulement agir sur les collections Java, mais les opérations d'IO traditionnelles (lire les données d'un fichier ou d'un réseau ligne par ligne) peuvent bénéficier du traitement Steam. Voici un petit exemple:
PATH FINAL PATH = nouveau fichier (nom de fichier) .topath (); essayez (stream <string> lines = files.lines (path, standardcharsets.utf_8)) {lines.Onclose (() -> System.out.println ("Done!")) .ForEach (System.out :: println);}Méthode Stream OnClose Renvoie un flux équivalent avec une poignée supplémentaire. Cette poignée sera exécutée lorsque la méthode Close () du flux est appelé. L'API Stream, les expressions lambda et les références de méthodes prises en charge par les méthodes par défaut d'interface et les méthodes statiques sont la réponse de Java 8 au paradigme moderne du développement de logiciels.
4.3 API Date / Heure (JSR 310)
Java 8 introduit une nouvelle API à date d'heure (JSR 310) pour améliorer le traitement du temps et des dates. La gestion du temps et de la date a toujours été le problème le plus douloureux pour les développeurs Java. Java.util.date et plus tard java.util.calendar n'ont pas résolu ce problème (encore plus confus par les développeurs).
Pour les raisons ci-dessus, la bibliothèque tierce Joda-Time est née, qui peut remplacer l'API de gestion du temps de Java. La nouvelle API de gestion de l'heure et de la date dans Java 8 est profondément influencée par Joda-Time et a absorbé une grande partie de l'essence de Joda-Time. Le nouveau package Java.Time contient toutes les classes sur la date, l'heure, le fuseau horaire, l'instant (similaire à la date mais précis aux nanosecondes), la durée (durée) et les opérations d'horloge. L'API nouvellement conçue examine sérieusement l'invariance de ces classes (conférences apprises de java.util.calendar), et renvoie un nouvel objet si une instance doit être modifiée.
Jetons un coup d'œil aux classes clés et à leurs exemples d'utilisation respectifs dans le package Java.Time. Tout d'abord, la classe d'horloge utilise un fuseau horaire pour retourner l'heure et la date de nanoseconde actuelles. L'horloge peut remplacer System.Currenttimemillis () et Timezone.getDefault ().
// obtient l'horloge du système en tant qu'horloge finale de décalage UTC = horloge.SystemUtc (); System.out.println (Clock.instant ()); System.out.println (Clock.Millis ());
La sortie de cet exemple est:
2014-04-12T15: 19: 29.282Z 1397315969360
Deuxièmement, concentrez-vous sur les classes locales et locales. LocalDate ne contient que la partie de date du système de calendrier ISO-8601; LocalTime ne contient que la partie du temps dans le système de calendrier. Les objets des deux classes peuvent être construits à l'aide d'objets d'horloge.
// obtient la date locale et l'heure locale finale LocalDate Date = localDate.Now (); final localDate dateFromClock = localDate.Now (horloge); System.out.println (DateFromClock); // obtient la date locale et l'heure locale finale finale Time = localTime.Now (); final localtime timefromclock = localtime.now (horloge); System.out.println (time); System.out.println (TimeFromClock);
Les résultats de sortie de l'exemple ci-dessus sont les suivants:
2014-04-12 2014-04-12 11: 25: 54.568 15: 25: 54.568
La classe LocalDateTime contient des informations sur LocalDate et LocalTime, mais ne contient pas d'informations de fuseau horaire dans le système de calendrier ISO-8601. Voici quelques exemples sur LocalDate et Localtime:
// Obtenez la date / heure locale finale LocalDateTime DateTime = localDateTime.Now (); Final localDateTime DateTimeFromClock = localDateTime.now (horloge); System.out.println (DateTime); System.out.println (DateTimeFromClock);
Les résultats de sortie de l'exemple ci-dessus sont les suivants:
2014-04-12T11: 37: 52.309 2014-04-12T15: 37: 52.309
Si vous avez besoin d'informations de données / temps pour un fuseau horaire spécifique, vous pouvez utiliser ZoneDateTime, qui contient la date et l'heure du système de date ISO-8601, et a des informations de fuseau horaire. Voici quelques exemples d'utilisation de différents fuseaux horaires:
// Obtenez la date / heure zonée finale zonedDateTime ZonedDateTime = zonedDateTime.now (); Final ZonedDateTime ZonedDateTimeFromClock = zonedDatetime.now (horloge); Final 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);La sortie de cet exemple est:
2014-04-12T11: 47: 01.017-04: 00 [America / New_york] 2014-04-12T15: 47: 01.017Z 2014-04-12T08: 47: 01.017-07: 00 [America / Los_Angeles]
Enfin, regardons le cours de durée, qui maintient le temps des secondes et des nanosecondes. Cela permet de calculer facilement la différence entre deux dates, l'exemple de code est le suivant:
// Obtenir une durée entre deux dates LocalDateTime final de = LocalDateTime.of (2014, Monthy.april, 16, 0, 0, 0); final localDatetime to = localDatetime.of (2015, mois.april, 16, 23, 59, 59); Durée finale de durée = durée. durée.todays ()); System.out.println ("Durée en heures:" + durée.toHours ());Cet exemple est utilisé pour calculer le nombre de jours et d'heures entre le 16 avril 2014 et le 16 avril 2015, et la production est la suivante:
Durée en jours: 365 durée en heures: 8783
L'impression globale de la nouvelle date et temps de Java 8 est relativement positive, en partie à cause de l'impact positif du temps de Joda, et en partie parce que le fonctionnaire a finalement écouté les besoins des développeurs. Si vous souhaitez en savoir plus, vous pouvez vous référer à la documentation officielle.
4.4 Nashorn JavaScript Engine
Java 8 fournit le nouveau moteur JavaScript Nashorn, nous permettant de développer et d'exécuter des applications JS sur le JVM. Nashorn JavaScript Engine est une autre version d'implémentation de javax.script.scriptengine. Ce type de moteur de script suit les mêmes règles et permet à Java et JavaScript d'être utilisés de manière interactive. L'exemple de code est le suivant:
ScriptenGineManager Manager = new ScriptenNeManager (); Scriptengine Engine = Manager.getEngineByName ("JavaScript"); System.out.println ("Result:" + Engine.eval ("fonction f () {return 1;}; f () + 1;");La sortie de ce code est la suivante:
jdk.nashorn.api.scripting.nashornscriptengine Résultats: 2
4.5 Base64
La prise en charge du codage Base64 a été ajoutée à la bibliothèque officielle Java 8, afin que l'encodage Base64 puisse être effectué sans utiliser de bibliothèque tierce. L'exemple de code est le suivant:
package com.javacodegeeks.java8.base64; Importer java.nio.charse.standardCharsets; import java.util.base64; classe publique Base64s {public static void main (String [] args) {final String text = "base64 enfin dans java 8!"; String final encodé = Base64.GetEncoder (). EncodetoString (text.getBytes (StandardCharsets.Utf_8)); System.out.println (codé); Final String Decoded = new String (base64.getDeccoder (). Decode (codé), StandardCharsets.Utf_8); System.out.println (Decoded);}}La sortie de cet exemple est la suivante:
Qmfzzty0IGZPBMFSBHKGAW4GSMF2ySA4IQ ==
Base64 Enfin dans Java 8!
La nouvelle base64API prend également en charge le codage et le décodage des URL et des mines.
(Base64.GetUrlencoder () / base64.GetUrlDEcoder (), Base64.getMimeencoder () / base64.getMimeDecoder ()).
4.6 tableaux parallèles
La version Java8 a ajouté de nombreuses nouvelles méthodes pour prendre en charge le traitement parallèle du tableau. La méthode la plus importante est Parallelsort (), qui peut considérablement accélérer le tri des tableaux sur des machines multi-core. L'exemple suivant démontre la méthode de la série Parallelexxxx:
package com.javacodegeeks.java8.parallel.arrays; import java.util.arrays; import java.util.concurrent.threadlocalrandom; classe publique 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. 结论
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。