Skip to main content

New Page

Introduction

Logiciels

  • Gradle (obligatoire)
  • Un éditeur de code (au choix), par exemple Eclipse, IntelliJ ou VS Code
  • Java JDK 17

Contenu du cours

  • Exceptions
  • Tests
  • Nouveautés Java
  • Évènements
  • Types génériques
  • Réflexion?

Exceptions

2023-09-18_17-29-09_screenshot.png

On va surtout utiliser Exception et Runtime Exception, mais pas directement (on va plus tot utiliser des exceptions dérivées de ceux-là, tel que NullPointerException ou IllegalArgumentException).

Par défault, le comportement d’une exception est de print l’erreur dans la console, cependant on ne va pas tellement l’utiliser en cours car on va utiliser un système de logging plus élaboré.

Try - catch

try {
    // On essaye d'exécuter un certain code ici...
} catch (NullPointerException e) {
    // Le code ici s'exécutera si il y a une NullPointerException et l'erreur sera mise dans la variable e.
} catch (NullPointerException | IndexOutOfBoundException e) {
    // Le code ici s'exécutera dans le cas d'une NullPointerException ou d'une IndexOutOfBoundException et sera mise dans la variable e.
}
// Si aucun problème n'est arrivé ou qu'elle a été bien catchée, le code ici s'exécutera...

Les try-catch permettent d’exécuer son propre code dans le cas d’une erreur.

Réexécution de fonction

Si on veut réexécuter une fonction il vaut mieux éviter de rappeller simplement la fonction récursivement dans le catch car si une erreur persiste, cela augmente grandement le stack des fonctions appellée ce qui peut mener à faire crash le programme.

Il vaut mieux utiliser un do-while

// On initialise les variables en dehors de la boucle
boolean locked = false;
int entier = 0;
do {
    System.out.print("Entrer un entier : ");
    try {
        entier = lireEntier();
        // Si le code a fonctionné, on remet le locked à false pour sortir de la boucle
        // On doit le remettre à false car si elle a raté la première fois, le locked aura été mis à true par le catch
        locked = false;
    } catch {
        // Si un soucis survient, on met la variable locked à true, pour que la boucle réexécute le code
        // Il n'y a ainsi aucune récursion, donc pas de risque de stackoverflow
        locked = true;
    }
} while (locked);

Throw - throws

public class PersonalException extends Exception {
    // Ici on met le code de l'exception, par exemple on peut y mettre des messages d'erreurs, des fonctions spéciales, etc.
}

// La méthode suivante retourne une exception
public static void method() {
    // Si quelque chose ne fonctionne pas on peut retourner notre exception custom
    throw new PersonalException();
}

On peut créer nos propre exceptions pour des cas particulier de nos programmes en étendant la classe Exception puis en utilisant throw new pour l’appeller.

Finally

try {
    // On essaye d'exécuter un certain code
} catch (NullPointerException e) {
    // On exécute le code ici si le code dans le try ne fonctionne pas
} finally {
    // Quoi qu'il arrive, le code ici sera exécuté, même si le catch retourne une exception.
}
// Si une erreur arrive dans catch ou finally, le code ici ne sera pas exécuté

Lorsque l’on veut qu’un code s’exécute quoi qu’il arrive, on peut utiliser le bloc finally, ainsi même si l’un des catch retourne une exception, on exécutera quand même le bloc finally.

Tester les exceptions

On peut également tester des exceptions avec assertThrows

assertThrows(RunTimeException.class, ()=>maMethode());

On passe directement la méthode à assertThrows. La syntaxe étrange s’appelle une lambda, c’est une fonction anonyme temporaire qui exécute la méthode à tester. Cette lambda est nécessaire, sinon on passe le résultat de maMethode à la place de passer la méthode elle même. La fonction assertThrows va ensuite tester pour voir si une exeception est émise par la méthode, et si oui, elle va tester que l’exception est bien de la classe RunTimeException.

Moteur de production (gradle)

L’objectif est d’automatiser les actions pour produire un logiciel, gérer les dépendances, les détecter et adapter la production du logiciel à la platforme. Gradle est un système qui permet d’automatiser tout ça.

Cela permet donc de rendre un projet indépendant de l’environement de développement de la personne qui écrit le code, car Gradle va automatiquement gérer toutes les dépendences nécessaires.

Historique des moteurs de production

Année Programme Langage Avantage
1977 make C et autres  
2000 Apache Ant Java  
2004 Maven Java Plus complet que Apache Ant
2008 Gradle Java (et autres comme Kotlin) Plus complet que Maven

Que font Gradle et Maven

  • La compilation
  • Le packaging (jar, war, etc)
  • Gestion des dépendences
  • Génération de la documentation
  • Gestionnaire de sources
  • Accès au depot des gestinnaires des dépendences
  • Le déploiement en différents environements (test, développement, production, etc)

Avantage de Gradle

  • Plus rapide
  • Plus flexible (pas limité à Java, contrairement à Maven)
  • Fichier de config et plus simple
  • Gestion des dépendances plus complète
  • Indépendant
  • Multilangage

Configuration de Gradle

  • settings.gradle qui est à la racine du projet et contient le nom du projet ainsi que l’ensemble des sous-projets
  • build.gradle est dans chaque (sous-)projet et contient l’ensemble des éléments utile pour compiler le projet (version de Java, dépendance, , tests, PMD, etc)

Liste des librairies

On peut rechercher les libraries sur le site MVN Repository.

Définition librarie

On ne s’amuse pas à réinventer la roue dès que l’on veut faire un programme. Du coup on va réutiliser des composants qui ont déjà été fait par d’autres personnes. Une librarie c’est exactement ça, c’est une collection d’outils qui permettent de programmer plus facilement et plus rapidement sans devoir réinveter les même choses en boucle.

Identification d’une dépendence/librarie

Il y a 3 éléments qui identifie une dépendence :

  • Le group id qui défini le groupe des librairies
  • Le artifact id qui défini la dépendence dans le groupe
  • La version de la dépendence

Il est important de vérifier la version des dépendences qui ne sont pas toujours compatibles entre elles et il faut éviter de prendre des versions trop vieilles.

Dernière fonctionalités utiles du JDK 17

Depuis JDK 8

  • Les lambdas qui sont des fonctions anonymes dans des variables
  • Les interfaces fonctionnelles, c’est comme une interface mais pour une seule méthode pour les lambdas
  • Default methods in interfaces, qui permettent de mettre du code par défault dans des interfaces
  • Stream API, permet de mettre une liste et de la traiter de manière fonctionnelle. Par exemple à la place d’utiliser des boucles on peut simplement faire un .filter(n -> n % 2 == 0) sur un stream
  • Date and time API qui permet de simplifier l’accès au date et au temps

Stream API

Pour expliquer plus en détails les avantages de la stream API, voici quelques exemples de code avec et sans stream.

Les streams API sont assez important et sont attendu à l’AI.

  1. Exemple 1

    Sans stream api :

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    for (int number: numbers) {
        if (number % 2 == 0) {
            System.out.println(number);
        }
    }
    

    Avec stream api :

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    numbers.stream().filter(n -> n % 2 == 0).forEach(System.out::println);
    
  2. Exemple 2

    Sans stream api :

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    for (String name: names) {
        System.out.println(name.toUpperCase());
    }
    

    Avec stream api :

    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    names.stream().map(String::toUpperCase).forEach(System.out::println);