Il y a des dizaines de fonctionnalités ajoutées à Java 8, les plus importantes sont mentionnées ci-dessous -
Lambda expression - Ajoute une capacité de traitement fonctionnelle à Java.
Method references- Référencer les fonctions par leurs noms au lieu de les appeler directement. Utilisation des fonctions comme paramètre.
Default method - Interface pour avoir l'implémentation de la méthode par défaut.
New tools - De nouveaux outils et utilitaires du compilateur sont ajoutés comme «jdeps» pour comprendre les dépendances.
Stream API - Nouvelle API de flux pour faciliter le traitement du pipeline.
Date Time API - API de date et heure améliorée.
Optional - Accent sur les meilleures pratiques pour gérer correctement les valeurs nulles.
Nashorn, JavaScript Engine - Un moteur basé sur Java pour exécuter du code JavaScript.
Parallèlement à ces nouvelles fonctionnalités, de nombreuses améliorations de fonctionnalités sont apportées sous le capot, à la fois au niveau du compilateur et de la JVM.
Le code suivant trie une liste de chaînes à l'aide de l'expression lambda Java 8:
//sort using java 8
private void sortUsingJava8(List<String> names) {
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
}
Une expression lambda est caractérisée par la syntaxe suivante -
parameter −> expression body
Voici les caractéristiques importantes d'une expression lambda -
Optional type declaration- Pas besoin de déclarer le type d'un paramètre. Le compilateur peut déduire la même chose à partir de la valeur du paramètre.
Optional parenthesis around parameter- Pas besoin de déclarer un seul paramètre entre parenthèses. Pour plusieurs paramètres, les parenthèses sont obligatoires.
Optional curly braces - Pas besoin d'utiliser des accolades dans le corps de l'expression si le corps contient une seule instruction.
Optional return keyword- Le compilateur renvoie automatiquement la valeur si le corps a une seule expression pour renvoyer la valeur. Des accolades sont nécessaires pour indiquer que l'expression renvoie une valeur.
Les expressions Lambda sont principalement utilisées pour définir l'implémentation en ligne d'une interface fonctionnelle, c'est-à-dire une interface avec une seule méthode. Dans l'exemple ci-dessus, nous avons utilisé différents types d'expressions lambda pour définir la méthode d'opération de l'interface MathOperation. Ensuite, nous avons défini l'implémentation de sayMessage de GreetingService.
L'expression Lambda élimine le besoin d'une classe anonyme et donne une capacité de programmation fonctionnelle très simple mais puissante à Java.
En utilisant l'expression lambda, vous pouvez faire référence à la variable finale ou effectivement à la variable finale (qui n'est affectée qu'une seule fois). L'expression Lambda génère une erreur de compilation si une valeur est affectée à une variable la deuxième fois.
Les références de méthode aident à désigner les méthodes par leurs noms. Une référence de méthode est décrite en utilisant le symbole :: (double deux-points). Une référence de méthode peut être utilisée pour pointer les types de méthodes suivants -
Méthodes statiques
Méthodes d'instance
Constructeurs utilisant un nouvel opérateur (TreeSet :: new)
La méthode System.out :: println est une référence de méthode statique à la méthode println de l'objet out de la classe System.
Les interfaces fonctionnelles ont une seule fonctionnalité à présenter. Par exemple, une interface Comparable avec une seule méthode «compareTo» est utilisée à des fins de comparaison. Java 8 a défini de nombreuses interfaces fonctionnelles à utiliser de manière intensive dans les expressions lambda.
Il représente une opération qui accepte deux arguments d'entrée et ne renvoie aucun résultat.
Il représente une fonction qui accepte deux arguments et produit un résultat.
Il représente une opération sur deux opérandes du même type, produisant un résultat du même type que les opérandes.
Il représente un prédicat (fonction à valeur booléenne) de deux arguments.
Il représente un fournisseur de résultats à valeur booléenne.
Il représente une opération qui accepte un seul argument d'entrée et ne renvoie aucun résultat.
Il représente une opération sur deux opérandes à double valeur et produisant un résultat à double valeur.
Il représente une opération qui accepte un seul argument à double valeur et ne renvoie aucun résultat.
Il représente une fonction qui accepte un argument à double valeur et produit un résultat.
Il représente un prédicat (fonction à valeur booléenne) d'un argument à double valeur.
Il représente un fournisseur de résultats à double valeur.
Il représente une fonction qui accepte un argument à double valeur et produit un résultat à valeur int.
Il représente une fonction qui accepte un argument à double valeur et produit un résultat à valeur longue.
Il représente une opération sur un seul opérande à double valeur qui produit un résultat à double valeur.
Il représente une fonction qui accepte un argument et produit un résultat.
Il représente une opération sur deux opérandes à valeur int et produit un résultat à valeur int.
Il représente une opération qui accepte un seul argument à valeur int et ne renvoie aucun résultat.
Il représente une fonction qui accepte un argument de valeur int et produit un résultat.
Il représente un prédicat (fonction à valeur booléenne) d'un argument à valeur int.
Il représente un fournisseur de résultats valorisés en int.
Il représente une fonction qui accepte un argument à valeur int et produit un résultat à double valeur.
Il représente une fonction qui accepte un argument à valeur int et produit un résultat à valeur longue.
Il représente une opération sur un seul opérande à valeur int qui produit un résultat à valeur int.
Il représente une opération sur deux opérandes à valeur longue et produit un résultat à valeur longue.
Il représente une opération qui accepte un seul argument à valeur longue et ne renvoie aucun résultat.
Il représente une fonction qui accepte un argument à valeur longue et produit un résultat.
Il représente un prédicat (fonction à valeur booléenne) d'un argument à valeur longue.
Il représente un fournisseur de résultats à long terme.
Il représente une fonction qui accepte un argument à valeur longue et produit un résultat à valeur double.
Il représente une fonction qui accepte un argument à valeur longue et produit un résultat à valeur int.
Il représente une opération sur un seul opérande à valeur longue qui produit un résultat à valeur longue.
Il représente une opération qui accepte un argument à valeur objet et un argument à valeur double, et ne renvoie aucun résultat.
Il représente une opération qui accepte un argument à valeur objet et un argument à valeur int, et ne renvoie aucun résultat.
Il représente une opération qui accepte une valeur d'objet et un argument de valeur longue, et ne renvoie aucun résultat.
Il représente un prédicat (fonction à valeur booléenne) d'un argument.
Il représente un fournisseur de résultats.
Il représente une fonction qui accepte deux arguments et produit un résultat à deux valeurs.
Il représente une fonction qui produit un résultat à double valeur.
Il représente une fonction qui accepte deux arguments et produit un résultat de valeur int.
Il représente une fonction qui produit un résultat de valeur int.
Il représente une fonction qui accepte deux arguments et produit un résultat à valeur longue.
Il représente une fonction qui produit un résultat à valeur longue.
Il représente une opération sur un seul opérande qui produit un résultat du même type que son opérande.
Avec java 8, une interface peut avoir une implémentation par défaut d'une fonction dans les interfaces.
Une interface peut également avoir des méthodes d'assistance statiques à partir de Java 8.
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
Utilisation du super mot-clé avec le nom de l'interface.
interface Vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
class Car implements Vehicle {
public void print() {
Vehicle.super.print();
}
}
Utilisation du nom de l'interface.
interface Vehicle {
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
class Car implements Vehicle {
public void print() {
Vehicle.blowHorn();
}
}
Stream représente une séquence d'objets provenant d'une source, qui prend en charge les opérations d'agrégation.
La plupart des opérations de flux retournent le flux lui-même afin que leur résultat puisse être mis en pipeline. Ces opérations sont appelées opérations intermédiaires et leur fonction est de prendre des entrées, de les traiter et de renvoyer la sortie à la cible. La méthode collect () est une opération terminale qui est normalement présente à la fin de l'opération de pipelining pour marquer la fin du flux.
Les opérations de flux effectuent les itérations en interne sur les éléments source fournis, contrairement aux collections où une itération explicite est requise.
Stream a fourni une nouvelle méthode 'forEach' pour itérer chaque élément du flux.
Le segment de code suivant montre comment imprimer 10 nombres aléatoires à l'aide de forEach.
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
La méthode 'map' est utilisée pour mapper chaque élément à son résultat correspondant.
Le segment de code suivant imprime des carrés uniques de nombres à l'aide de la carte.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
//get list of unique squares
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
La méthode «filtre» permet d'éliminer des éléments en fonction d'un critère.
Le segment de code suivant imprime un nombre de chaînes vides à l'aide de filter.
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//get count of empty string
int count = strings.stream().filter(string −> string.isEmpty()).count();
La méthode «limite» est utilisée pour réduire la taille du flux.
Le segment de code suivant montre comment imprimer 10 nombres aléatoires.
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
La méthode «triée» est utilisée pour trier le flux.
Le segment de code suivant montre comment imprimer 10 nombres aléatoires dans un ordre trié.
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
parallelStream est l'alternative de stream pour le traitement parallèle. Jetez un œil au segment de code suivant qui imprime un nombre de chaînes vides à l'aide de parallelStream.
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//get count of empty string
int count = strings.parallelStream().filter(string −> string.isEmpty()).count();
//It is very easy to switch between sequential and parallel streams.
Les collecteurs sont utilisés pour combiner le résultat du traitement sur les éléments d'un flux. Les collecteurs peuvent être utilisés pour renvoyer une liste ou une chaîne.
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
Avec Java 8, des collecteurs de statistiques sont introduits pour calculer toutes les statistiques lorsque le traitement du flux est en cours.
Le code suivant imprimera le numéro le plus élevé présent dans une liste.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = integers.stream().mapToInt((x) −> x).summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
Le code suivant imprimera le numéro le plus élevé présent dans une liste.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = integers.stream().mapToInt((x) −> x).summaryStatistics();
System.out.println("Lowest number in List : " + stats.getMin());
Le code suivant imprimera la somme de tous les nombres présents dans une liste.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = integers.stream().mapToInt((x) −> x).summaryStatistics();
System.out.println("Sum of all numbers : " + stats.getSum());
Le code suivant imprimera la moyenne de tous les nombres présents dans une liste.
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = integers.stream().mapToInt((x) −> x).summaryStatistics();
System.out.println("Average of all numbers : " + stats.getAverage());
Facultatif est un objet conteneur utilisé pour contenir des objets non nuls. L'objet facultatif est utilisé pour représenter null avec une valeur absente. Cette classe a diverses méthodes utilitaires pour faciliter le code pour gérer les valeurs comme «disponibles» ou «non disponibles» au lieu de vérifier les valeurs nulles. Il est introduit dans Java 8 et est similaire à ce qu'est Optional dans Guava.
Avec Java 8, Nashorn, un moteur javascript bien amélioré est introduit, pour remplacer le Rhino existant. Nashorn offre des performances 2 à 10 fois supérieures, car il compile directement le code en mémoire et transmet le bytecode à JVM. Nashorn utilise la fonction invokedynamics, introduite dans Java 7 pour améliorer les performances.
Pour le moteur Nashorn, JAVA 8 introduit un nouvel outil de ligne de commande, jjs, pour exécuter des codes javascript sur la console.
Oui! À l'aide de ScriptEngineManager, le code JavaScript peut être appelé et interprété en Java.
Local - API date-heure simplifiée sans complexité de gestion du fuseau horaire.
Zoné - API de date-heure spécialisée pour gérer différents fuseaux horaires.
java.time.temporal.ChronoUnit enum est ajouté dans Java 8 pour remplacer les valeurs entières utilisées dans l'ancienne API pour représenter le jour, le mois, etc.
Le code suivant obtient la date actuelle en utilisant l'API datetime locale -
//Get the current date
LocalDate today = LocalDate.now();
System.out.println("Current date: " + today);
Le code suivant ajoute 1 semaine à la date actuelle en utilisant l'API datetime locale -
//add 1 week to the current date
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
System.out.println("Next week: " + nextWeek);
Le code suivant ajoute 1 mois à la date actuelle en utilisant l'api datetime local:
//add 1 month to the current date
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plus(1, ChronoUnit.MONTHS);
System.out.println("Next month: " + nextMonth);
Le code suivant ajoute 1 an à la date actuelle en utilisant l'API datetime locale -
//add 1 year to the current date
LocalDate today = LocalDate.now();
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
System.out.println("Next year: " + nextYear);
Le code suivant ajoute 10 ans à la date actuelle en utilisant l'API datetime locale -
//add 10 years to the current date
LocalDate today = LocalDate.now();
LocalDate nextDecade = today.plus(1, ChronoUnit.DECADES);
System.out.println("Date after ten year: " + nextDecade);
Le code suivant est obtenu mardi prochain en utilisant java8 -
//get the next tuesday
LocalDate today = LocalDate.now();
LocalDate nextTuesday = today.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
System.out.println("Next Tuesday on : " + nextTuesday);
Le code suivant obtient le deuxième samedi du mois prochain en utilisant java8 -
//get the second saturday of next month
LocalDate firstInYear = LocalDate.of(date1.getYear(),date1.getMonth(), 1);
LocalDate secondSaturday = firstInYear.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY)).with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
System.out.println("Second Saturday on : " + secondSaturday);
Le code suivant obtient l'instant de la date actuelle en millisecondes -
//Get the instant of current date in terms of milliseconds
Instant now = currentDate.toInstant();
Le code suivant obtient l'instant de la date locale en utilisant l'heure en millisecondes -
Instant now = currentDate.toInstant();
ZoneId currentZone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(now, currentZone);
System.out.println("Local date: " + localDateTime);
Le code suivant obtient l'instant de la date zonée en utilisant le temps en millisecondes -
Instant now = currentDate.toInstant();
ZoneId currentZone = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, currentZone);
System.out.println("Zoned date: " + zonedDateTime);
classe statique Base64.Decoder - Cette classe implémente un décodeur pour décoder les données d'octets en utilisant le schéma de codage Base64 comme spécifié dans RFC 4648 et RFC 2045.
Classe statique Base64.Encoder - Cette classe implémente un encodeur pour coder les données d'octet à l'aide du schéma de codage Base64 comme spécifié dans RFC 4648 et RFC 2045.
La méthode getDecoder () de la classe Base64 renvoie un Base64.Decoder qui décode à l'aide du schéma de codage base64 de type Basic.
La méthode getEncoder () de la classe Base64 renvoie un Base64.Encoder qui encode en utilisant le schéma de codage base64 de type Basic.
La méthode getMimeDecoder () de la classe Base64 renvoie un Base64.Decoder qui décode à l'aide du schéma de décodage de type MIME base64.
La méthode getMimeEncoder () de la classe Base64 renvoie un Base64.Encoder qui encode à l'aide du schéma de codage de type MIME base64.
La méthode getUrlDecoder () de la classe Base64 renvoie un Base64.Decoder qui décode à l'aide du schéma de codage base64 de type sécurisé URL et Nom de fichier.
La méthode getUrlEncoder () de la classe Base64 renvoie un Base64.Encoder qui code à l'aide du schéma de codage base64 de type sécurisé URL et Nom de fichier.