Prise en main IntelliJ

Découverte de l'environnement

L'environnement se compose comme ceci :

1. Au centre se situe l'interface de développement : l'endroit où nous allons coder.

2. A gauche se trouve l'architecture du projet et l'ensemble des fichiers relatifs à ce projet.

3. En bas se situe la console. C'est l'endroit où nous voyons comment s'exécute notre programme :

  • les informations qu'on a demandé à écrire en console;
  • les erreurs retournées par le programme.

Votre premier programme JAVA

Le point d'entrée d'un programme JAVA est toujours la méthode main(). Cette méthode est définie par les lignes suivantes :

public static void main( String[] args){
...
}

Cette méthode se situe toujours dans une classe déclarée comme ceci :

public class NomDeMaClasse {
...
}

Cet objet encapsule toutes les lignes de code du programme. La méthode main() doit absolument se situer entre les accolades qui déterminent la classe.

Une ligne d'instruction se termine toujours par un ;.
Le programme JAVA exécute toujours les lignes d'instructions de haut en bas. Il n'exécute que ce qui est entre les accolades de la méthode main().

Dans le cas de notre exemple, la ligne d'instruction :
System.out.println("Hello World!");
permet d'afficher Hello World! dans la console (Outil vu précédemment).

Exécutons notre programme

Avant de démarrer le programme, il faut tout d'abord le compiler(*) à l'aide du bouton Build.

Pour démarrer notre programme, nous devons utiliser le bouton Run.

Les commentaires

Un commentaire permet d'ajouter une information sur le programme.
C'est une ligne que nous écrivons dans le programme mais qui n'est pas interprété lors de l'exécution de ce dernier.

Il existe plusieurs façons d'écrire un commentaire :

  • //pour commenter une simple ligne
  • /* pour commenter
    * plusieurs
    * lignes
    */

Les variables

Un algorithme est une suite de lignes d'instructions. Un programme informatique est un gros algorithme.

Comme en mathématiques, pour fonctionner, un algortithme (informatique) ou un programme a besoin de variables.

Mais qu'est-ce qu'une variable ?

Nous pouvons visualiser une variable comme un tiroir :


Il existe plusieurs types de variables que je vous invite à découvrir ci-contre :

Un int est stocké sur 32 bits. Un long est stocké sur 64 bits.
Un float est stocké sur 32 bits, soit 6 ou 7 décimales. Un double est stocké sur 64 bits, soit 15 ou 16 décimales.

/***************************************
* Déclaration de variables
****************************************/


//On précise d'abord le type puis le nom de la variable et enfin on affecte la valeur
//Il existe aussi le type short mais on l'utilise peu

int variableDeTypeEntier = 6;
long variableDeTypeEntierPlusLong = 6L; //Il est fortement conseillé de rajouter L derrière afin de le différencier d'un int

double variableDecimale = 6.3;
float variableDecimaleDeTypeFloat = 6.3f; //Nous sommes obligés de rajouter un f derrière pour signifier que ce n'est pas un double

//char signifie caractère. La valeur est à définir entre simple quote.
char variableLettre = 'a';

//String signifie chaîne de caractères. La valeur est à définir entre double quotes
String variableDeTypeTexte = "mon texte";

//boolean signifie une variable qui ne peut prendre que deux valeurs
//true signifie vrai
//false signifie faux

boolean variableBoolean = true;

/*************************************************
* Modification de la valeur de variables existantes
*************************************************/

//nom de la variable = nouvelle valeur
//Il n'est pas nécessaire de définir à nouveau le type puisque cela a déjà été fait

variableDeTypeEntier = 8;

//Portion de code impossible. Un entier ne peut être affecté à une variable de type chaîne de caractères
//variableDeTypeTexte = 9;

variableDeTypeTexte = "mon nouveau texte";

Les types de variables commençant par des minuscules sont des variables de type primitif. On ne peut leur appliquer de méthode. Elles ont juste une valeur.

Les variables dont les types commencent par des majuscules sont des variables de type Objet. Des méthodes peuvent leur être appliquées. Par exemple :
String var = "variable chaîne de caractères";
char[] letters = var.toCharArray();

Caster une variable

Pour transformer une variable d'un certain type en un autre type, il faut la caster. En d'autres termes, il faut forcer la variable à changer de type le temps de l'exécution de la ligne. Pour rappel, nous avons dit dans le cours que lorsque le type d'une variable était définie, nous ne pouvions pas la changer en cours de route. Voici l'exception qui confirme cette règle.
Pour cela, il faut préciser lors de l'affectation à une autre variable le nouveau type de la variable entre parenthèses.
Voici un exemple concret :
int a = 6;
double b = (double) a; //b est alors égal à 6.0

Les tableaux

Parfois, il est nécessaire de stocker au "même endroit" plusieurs éléments.



Exemple :

Prenons l'exemple de notes. Pour chaque matière, nous avons une série de notes. Nous pouvons donc aisément imaginer stocker toutes les notes d'une même matière dans un tableau.



Le tableau pourrait ainsi prendre le nom de la matière et donner ceci :


french = [15, 16, 20, 12];

Si nous décomposons ce tableau, il s'agit de notes entières ou décimales.

Le problème c'est qu'en JAVA, il est impossible de mettre des éléments de types différents dans un tableau. On choisira donc un tableau de doubles puisque nous pouvons convertir facilement des entiers en décimaux sans perdre d'informations alors que l'inverse n'est pas vrai.

(int) 15.75 = 15
(double) 15 = 15.0

Pour déclarer un tableau, plusieurs méthodes sont possibles :


int[] french = {15, 16, 20, 12}; //Ceci est valable lorsque nous connaissons les notes à l'avance.

Si nous ne les connaissons pas, nous devons quand même connaître à l'avance le nombre de notes :


int[] french = new int[4]; //Ceci est valable lorsque nous connaissons le nombre de notes à l'avance.
french[0] = 15; //Les indices des tableaux commencent à 0.
french[1] = 16;
french[2] = 20;
french[3] = 12;

En résumé, la taille du tableau french est bien de 4 mais le dernier indice est égal à tailleDuTableau -1.


Les listes

Les tableaux ont une réelle limite. Celle de la taille. Si le professeur de français décide de faire une interrogation surprise et rajoute une note, notre programme ne fonctionne plus. C'est pour cela qu'il existe les listes.

Comme les tableaux, on ne peut mettre qu'un type d'éléments dans une liste mais il est possible d'en ajouter, supprimer,... comme bon nous semble.

Pour cela :

ArrayList<Integer> french = new ArrayList<>();
french.add(15);
french.add(16);
french.add(20);
french.add(12):

Comme pour les tableaux, les indices des listes commencent à 0.


INFORMATION IMPORTANTE, on ne peut stocker que des objets dans des ArrayList. Si vous souhaitez stocker des types primitifs, vous devez utiliser leur équivalent en Objet :


  • int : Integer
  • long : Long
  • double : Double
  • float : Float
/*************************************
* TABLEAUX
************************************/

// Initialisation du tableau avec des données
int[] monPremierTableau = {1, 2, 3, 4};

//Initialisation du tableau sans données
//Obligation de préciser la taille

int[] monDeuxiemeTableau = new int[4];
//Ajout des données
//Les indices des tableaux et listes commencent à 0
//monDeuxiemeTableau[index] = valeur;

monDeuxiemeTableau[0] = 1;
monDeuxiemeTableau[1] = 2;
monDeuxiemeTableau[2] = 3;
monDeuxiemeTableau[3] = 4;

//Récupération d'une donnée
//Premier élément du tableau

System.out.println(monPremierTableau[0]);
//Dernier élément du tableau récupéré à l'aide de la taille
//monTableau.length

System.out.println(monDeuxiemeTableau[monDeuxiemeTableau.length-1]);


/*************************************
* LISTES
************************************/

//Seuls des objets peuvent remplir des listes
//Integer est l'objet correspondant à int

ArrayList<Integer> maPremiereListe = new ArrayList<>(); //On oublie pas de l'initialiser sinon NPE
maPremiereListe.add(1);
maPremiereListe.add(2);
maPremiereListe.add(3);
maPremiereListe.add(4);
maPremiereListe.add(5);

//Déclaration de liste non instanciée, l'objet n'existe pas
//ArrayList<String> listeChaine = null;
//On ne peut donc pas ajouter de valeur dedans
//& on obtient l'erreur suivante : listeChaine est null
//java.lang.NullPointerException
//listeChaine.add("45");
//listeChaine.add("56");


System.out.println(maPremiereListe);
System.out.println(maPremiereListe.size()); //Affiche la taille de la liste
maPremiereListe.remove(1); //Enlève l'élément "2" de la liste
System.out.println(maPremiereListe);
System.out.println(maPremiereListe.size());

Les méthodes

Une méthode permet d'encapsuler une partie de code. Cela a plusieurs avantages :

Pourquoi plus lisible ?

Chaque méthode comporte un nom. Ce nom doit être explicite !. Il est donc interdit de les appeler a, methode ou autre.

Pourquoi serait-il réutilisable ?

Il faut voir une méthode comme un bloc de lignes de code. Ce bloc est donc encapsulé par des {...} précédées par le nom. En appelant dans la méthode main() le nom de cette méthode, le programme va lire toutes les lignes de code présentes dans la méthode puis revenir à la méthode main().


Ok pour la théorie mais dans la pratique, comment cela se présente-t-il ?

En réalité, depuis le début, vous avez vu grand nombre de méthodes et notamment la fameuse :


public static void main(String[] args) {...}

Cette méthode est la méthode principale, le point de départ du programme.



Pour faire les exercices, vous avez travaillé avec des méthodes. Celles-ci commencent toujours par les mots clefs suivants :


  • public static : ceci indique que tout le programme peut utiliser la méthode (Nous rentrerons dans les détails plus tard.)
  • void , String ou autre : ce que retourne la méthode. void signifie que la méthode ne retourne rien. Elle se contente d'exécuter des lignes de code sans rien retourner à la méthode principale. Ce que fait cette méthode ne sera pas "exploitable" par le programme par la suite.
  • nomDeLaMéthode() : le nom utilisé pour l'appeler dans le programme. En CamelCase (*)
  • éventuellement des paramètres : ce sont des variables que nous passons à la méthode pour que cette dernière puisse l'utiliser.
  • CamelCase : tous les mots composants le nom de la méthode ou de la variable sont attachés. Le premier mot commence par une minuscule, les autres mots commencent par une majuscule.
    Par exemple : calculMoyenne()

    public static void main(){
    // Méthode principale d'un programme JAVA
    System.out.println("Cette méthode est déroulée automatiquement dès qu'on lance le programme.");

    //Appel d'une autre méthode par le biais de son nom
    methodeSansParametre();

    //Je stocke ce qui est retourné par la méthode dans une variable afin de le réutiliser
    String chaineRenvoyee = methodeSansParametreQuiRenvoieUneString();
    //J'utilise le résultat retournée par methodeSansParametreQuiRenvoieUneString() pour l'afficher dans la console
    System.out.println(chaineRenvoyee);

    //Je créé une variable de type int correspondant à mon âge
    int monAge = 27;
    //J'appelle la méthode methodeAvecUnParametre et je mets entre les parenthèses la variable qui se nomme monAge
    methodeAvecUnParametre(monAge);
    //Je peux aussi l'appeler en passant une valeur directement
    methodeAvecUnParametre(30);

    //J'appelle methodeAvecPlusieursParametres et je mets directement les valeurs
    //Attention : ici, il est attendu en premier une chaine de caractères et en second un entier
    //Veillez à mettre les éléments dans cet ordre sinon à la compilation, vous obtiendrez une erreur
    methodeAvecPlusieursParametres("Marie", 27);
    //CODE GENERANT UNE ERREUR
    //methodeAvecPlusieursParametres(27, "Marie");

    //J'appelle methodeAvecPlusieursParametres
    //Non mais comment ça, elle existe déjà mais on met une chaine de caractères & un entier normalement
    //Oui mais nous en avons créé une seconde qui attend deux entiers
    //C'est comme cela que JAVA saura laquelle appeler. Grâce au type !

    int monAgeCalcule = methodeAvecPlusieursParametres(20,7);
    System.out.println("Mon âge calculé : "+monAgeCalcule + " ans");
    }

    public static void methodeSansParametre() {
    System.out.println("Je suis une méthode sans paramètre et qui ne revoie rien à la méthode qui l'a appelée.");
    }

    public static String methodeSansParametreQuiRenvoieUneString() {
    System.out.println("Je suis une méthode sans paramètre et qui renvoie une chaîne de caractères à la méthode qui l'a appelée.");
    String resultat = "La chaîne que methodeSansParametre() renvoie";
    //J'utilise le mot clef return pour retourner la chaîne de caractères à la méthode qui l'a appelée
    return resultat;
    }

    public static void methodeAvecUnParametre( int age) {
    System.out.println("J'affiche l'âge passé en paramètre de la fonction "+ age);
    }

    public static void methodeAvecPlusieursParametres( String prenom, int age) {
    System.out.println("Je m'appelle "+ prenom + " et j'ai "+age +" ans.");
    }

    public static int methodeAvecPlusieursParametres( int nb1, int nb2) {
    //On additionne nb1 et nb2
    int resultat = nb1 + nb2;
    //J'utilise le mot clef return afin de retourner le résultat à la méthode qui l'a appelée
    return resultat;
    }

    /***********************************************************************************
    * CODE IMPOSSIBLE : Nous pouvons créer deux méthodes portant le même nom
    * mais il ne faut pas qu'elles aient le même nombre et le même type de paramètres
    * sinon "JAVA ne saura pas dans quelle méthode aller"
    ************************************************************************************/
    /* public static int methodeAvecPlusieursParametres(int nb1, int nb2) {
    int resultat = nb1 * (int) nb2;
    return resultat;
    }*/

    Structures et boucles

    Structures de conditions (choix)

    Ces structures permettent au programme de prendre des décisions. Si certaines conditions sont remplies alors le programme exécutera telle ligne de code plutôt qu'une autre.

    Comment savoir si une condition est remplie ?

    Vous vous souvenez des booléens ? C'est ici qu'ils interviennent. Si ce qu'on demande au programme est vrai alors la condition est remplie sinon elle n'est pas remplie. Voici un exemple concret.

    Est-ce que Pauline est majeure ?
    Vous ne savez pas son âge à cet instant mais vous pouvez traduire cette condition par
    ageDePauline >= 18.

    Plus tard, dans votre programme, vous apprenez que Pauline a deux ans. La condition devient alors
    ageDePauline >= 18 devient 2 >= 18 ?



    Nous sommes bien d'accord le résultat est faux et traduit en booléen false. La condition n'est donc pas remplie.

    & si nous avons plusieurs conditions à tester ?

    Pas de problème ! Nous avons le && (et) et le || (ou) pour combiner plusieurs conditions.



    Condition 1 Condition 2 RESULTAT
    false && false false
    false && true false
    true && false false
    true && true true
    Condition 1 Condition 2 RESULTAT
    false || false false
    false || true true
    true || false true
    true || true true

    Si...et si...sinon...

    En anglais if... else if... else.



    /*************************************************
    * IF
    *************************************************/
    //Une condition
    int ageDePauline = 2;
    if (ageDePauline >= 18){
    System.out.println("La condition est remplie");
    }
    else {
    System.out.println("La condition n'est pas remplie");
    }

    if (ageDePauline >= 0 && ageDePauline < 2){
    System.out.println("C'est un bébé");
    }
    else if (ageDePauline <= 2 && ageDePauline < 19){
    System.out.println("C'est une enfant.");
    }
    else {
    System.out.println("C'est un adulte"); }

    //Combinaison de plusieurs conditions
    if (ageDePauline >= 2 && ageDePauline <= 18){
    System.out.println("Ce n'est plus un bébé et ce n'est pas un adulte non plus. Les deux conditions sont remplies.");
    }

    if (ageDePauline > 2 && ageDePauline <= 18){
    System.out.println("Loupé... si on utilise && (et). Si l'une des conditions n'est pas remplie alors l'ensemble des conditions sera faux.");
    }
    else {
    System.out.println("Pauline n'a pas un âge strictement supérieur à deux ans (conditions non remplies) et elle est bien mineur (condition remplie)");
    }

    if (ageDePauline > 2 || ageDePauline <= 18){
    System.out.println("Bien joué... si on utilise || (ou). Si l'une des conditions est remplie alors l'ensemble des condition sera vrai.");
    }
    else {
    System.out.println("Pauline n'a pas un âge strictement supérieur à deux ans (conditions non remplies) mais elle est bien mineur (condition remplie)");
    }

    Si...et si...sinon... Version ternaire

    La version ternaire est la version "raccourcie" du if...else. Elle va plus vite à écrire mais est plus difficile à lire.

    /*******************************************************
    * TERNAIRE
    * résultat = condition ? condition vérifiée : condition non vérifiée;
    *******************************************************/

    int age = 20;
    //Le résultat de la variable isMajeur dépend de la condition
    //age >= 18 ?
    //Si oui, isMajeur = true
    //Sinon, isMajeur = false
    String isMajeur = age >= 18 ? "Il est majeur" : "Il est mineur";

    /* EQUIVALENT
    String isMajeur1;
    if(age >= 18){
    isMajeur1 = "Il est majeur";
    }
    else{
    isMajeur1 = "Il est mineur";
    }*/



    System.out.println("majeur si âge = 20 ? " + isMajeur);

    age = 15;
    isMajeur = age >= 18 ? "true" : "false";
    System.out.println("majeur si âge = 15 ? " + isMajeur);

    En fonction du cas

    En anglais switch(condition) case....

    /*******************************************************
    * SWITCH CASE
    *******************************************************/

    //Date du jour
    LocalDate date = LocalDate.now();
    //Jour en français
    String dayInFrench = date.format(DateTimeFormatter.ofPattern("EEEE", Locale.FRANCE));
    System.out.println(dayInFrench);
    switch (dayInFrench){
    case "lundi" :
    System.out.println("pfffff le début de semaine, duuuur.");
    break ;
    case "mardi" :
    System.out.println("en cours...");
    break ;
    case "jeudi" :
    System.out.println("dire qu'avant, on sortait en soirée étudiante le jeudi.");
    break ;
    case "samedi":
    case "dimanche":
    System.out.println("C'est le week-end !");
    break ;
    default :
    System.out.println("En cours... mais par défaut !");
    }


    /*******************************************************
    * SWITCH CASE
    * DEPUIS JAVA 12 mais je suis en JAVA 11
    *******************************************************/
    /*switch(dayInFrench){
    case "lundi" -> System.out.println("pfffff le début de semaine, duuuur.");
    case "mardi" -> System.out.println("en cours...");
    case "jeudi" -> System.out.println("dire qu'avant, on sortait en soirée étudiante le jeudi.");
    case "samedi", "dimanche" -> System.out.println("C'est le week-end !");
    default -> System.out.println("En cours... mais par défaut !");
    }*/

    Boucles (répétition ou parcours d'un tableau/liste)

    Ces structures permettent au programme de parcourir un tableau, une liste ou encore de répéter une opération un certain nombre de fois. Le programme arrête alors l'exécution de cette boucle de différentes manières :

    En programmation, nous avons le choix entre plusieurs boucles. Pour ma part, je préfère les boucles for (pour). Nous avons moins de risques de tomber dans une boucle infinie et ainsi faire planter le programme. Nous terminerons donc par celles-ci. Avant cela, les boucles tant que et faire tant que.


    Tant que...

    En anglais while.


    Ici, tant que la condition est vérifiée (tant que la valeur passée entre parenthèses renvoie true), le programme exécute les instructions placées entre les accolades.

    Si la condition n'est pas vérifiée, le programme ne passe pas du tout dans les accolades.

    int condition = 15;
    /************************************************
    * TANT QUE
    ************************************************/

    int j = 13;
    while (j > condition){
    System.out.println("On ne passe jamais dans la boucle puisque la condition n'est pas vérifiée.");
    }

    j = 18;
    while (j > condition){
    System.out.println("On passe dans la boucle puisque la condition est vérifiée.");
    //On décrémente j pour que la condition ne soit plus vérifiée.
    j--; //j = j-1;
    System.out.println(j);
    //Lorsque j atteindra 15, la condition ne sera plus vérifiée. On sortira de la boucle.
    }

    Faire... tant que...

    En anglais do... while.


    Ici, le programme exécute au moins une fois ce qu'il y a entre les accolades. Arrivé à la fin, il continue d'exécuter ce qu'il y a entre les accolades tant que la condition est vérifiée (tant que la valeur passée entre parenthèses renvoie true).

    /************************************************
    * FAIRE TANT QUE
    ************************************************/
    j = 13;
    do {
    System.out.println("Avec le do...while, on passe une fois dans la boucle même si la condition n'est pas vérifiée.");
    }
    while (j > condition);

    j = 18;
    do {
    System.out.println("On passe dans la boucle une première fois et après aussi puisque la condition est encore vérifiée.");
    //On décrémente j pour que la condition ne soit plus vérifiée.
    j--; //j = j-1;
    System.out.println(j);
    //Lorsque j atteindra 15, la condition ne sera plus vérifiée. On sortira de la boucle.
    }
    while (j > condition);

    Pour

    En anglais for....
    La boucle for permet d'exécuter une portion de code x foisx est préalablement défini.
    /************************************************
    * POUR
    ************************************************/
    //Pour un index nommé i ALLANT de 0 A 5 non compris (puisque <) PAR PAS DE 1 (i++)
    for (int i = 0; i < 5; i++){
    System.out.println("Boucle for : "+i);
    }

    //Avec un tableau ou une liste
    int[] tab = {1,2,3,4,5,6,7};
    ArrayList<Double> list = new ArrayList<>();
    list.add(1.1);
    list.add(2.2);
    list.add(3.3);

    for (int k=0; k < tab.length; k++){
    int el = tab[k];
    System.out.println("Boucle for avec tableau : "+ el);
    }

    for ( int l=0; l < list.size(); l++){
    Double el = list.get(l);
    System.out.println("Boucle for avec liste : "+ el);
    }

    Pour chaque élément

    En anglais for each... écrit for dans le code.
    La boucle for each permet de parcourir un tableau ou une liste sans avoir connaissance de la taille de l'élément parcouru. Cela permet aussi de manipuler directement l'élément du tableau/liste sans passer par l'étape tab[index] ou list.get(index).

    /************************************************
    * POUR CHAQUE ELEMENT
    ************************************************/
    //for TYPE des éléments dans le tableau/liste NOMVARIABLE : ELEMENT_PARCOURU
    for ( int elementTab : tab){ //foreach($tab as $elementTab)
    System.out.println("Boucle foreach avec tableau : "+ elementTab);
    }

    for ( Double elementList : list){
    System.out.println("Boucle foreach avec liste : "+ elementList);
    }

    Ruptures de séquence

    Les ruptures de séquence permettent d'arrêter de manière prématurée l'exécution d'une boucle ou passer une itération. En effet, parfois, dans un cas précis, la boucle ne doit pas avoir le même comportement et c'est alors que les conditions d'arrêt entrent en jeu.

    break

    Si la condition est vérifiée, le mot clef break permet de quitter la boucle.

    /************************************************
    * BREAK
    ************************************************/
    for ( int k=0; k < tab.length; k++){
    if (k == 2){
    break ;
    }
    int el = tab[k];
    System.out.println("On affiche l'élément parce que la condition k == 2 n'est pas vérifiée : "+ el);
    }
    System.out.println("Dès que k==2, on sort de la boucle. Les autres éléments du tableau ne seront pas traités.");

    continue

    Si la condition est vérifiée, le mot clef continue permet de passer à l'itération d'après sans exécuter le code qui suit.

    /************************************************
    * CONTINUE
    ************************************************/
    for ( int k=0; k < tab.length; k++){
    if (k == 2){
    System.out.println("Puisque k==2, on ne va pas afficher la phrase habituelle avec la valeur. On passe notre tour : on \"continue\".");
    System.out.println("ATTENTION");
    System.out.println("Pour rappel, les indices des tableaux et listes commencent à 0 et nos valeurs à 1. C'est pour cela que nous avons un \"décalage\".");
    continue ;
    }
    int el = tab[k];
    System.out.println("On affiche l'élément parce que la condition k == 2 n'est pas vérifiée : "+ el);
    }

    /!\ Les ruptures de séquence ne peuvent pas s'appliquer à une boucle for each.

    Exemple d'utilisation des ruptures de séquence :

    Qu'affiche cet algorithme ? Il affiche 1.

    int n = 50;
    for ( int i = 1; i < n; i++){
    if (i%2 == 0) {
    continue;
    }
    if(i%3 == 0){
    break;
    }
    System.out.print(i);
    }
    % signifie modulo, c'est-à-dire le reste de la division
    System.out.print() permet d'afficher quelque chose sans retourner à la ligne. tous les éléments affichés vont donc se suivre sans espace, retour à la ligne ou autre.



    Langage orienté Objets

    JAVA ?

    Revenons sur le langage JAVA. Créé par Sun en 1995 puis racheté par Oracle en 2009. JAVA 15 depuis septembre 2020 mais non LTS (*). C'est pour cela qu'en cours, nous utilisons JAVA 11, qui lui est LTS et plus stable.

    (*) LTS : Support à long terme

    Lors de la création du langage Java, il avait été décidé que ce langage devait répondre à cinq objectifs :


    Orienté objets ?

    La programmation orientée objet (P.O.O) est une programmation basée sur des objets qui possèdent des attributs et des méthodes. Cela permet d’avoir une programmation proche de la réalité.

    En P.O.O, nous pourrions utiliser un objet de type ordinateur pour créer :

    De même, l'objet Ordinateur est lui-même composé de plusieurs objets :

    En programmation orientée objet, c'est pareil !

    Les classes

    La P.O.O tourne autour des objets. Chaque objet est unique et défini par :

    • Une classe (type -nature de l'objet),
    • Ses attributs,
    • Un comportement (actions qu'il est capable de réaliser, ses méthodes).

    Concrètement, une classe est un moule pour représenter des objets qui ont des caractéristiques communes.

    Exemple : Ordinateur est le "moule" pour créer un Mac Pro, un Mac, un Asus Chromebook,...


    Ce sont ces caractéristiques que nous représentons en UML :

    UML généré via IntelliJ qui correspond à la classe Student.java dont le lien est ci-dessous

    Code source de la classe Student.java

    Un diagramme de classes permet aussi et surtout de décrire l'ensemble des classes d'un programme comme le diagramme ci-dessous qui représente le cours :







    Code source de la classe Address.java


    Ok, mais revenons aux méthodes ? Ici, static ou non ?

    Une méthode dite statique est une méthode qui est indépendante d’un objet. La méthode getCurrentYear() ne se rapporte à aucun objet précisément puisqu'elle relate l'année en cours. Nous pouvons donc le mettre statique dans une classe que l'on nommera Time.


    public class Time {
    public static int getCurrentYear(){
    LocalDate today = LocalDate.now();
    return today.getYear();
    }
    }

    Pour l'utiliser, rien de plus simple : NomDeLaClasse.nomMethode()

    Pour utiliser une méthode se rapportant à un objet (non statique), nomObjet.nomMethode();

    //Création d'un objet
    Student adrien = new Student("QUILLERE", "Adrien");
    //Appel d'une méthode non statique
    //se référant à l'objet adrien de type Student
    System.out.println(adrien.getFirstname());

    //Pour appeler une méthode statique qui se situe dans une autre classe
    //NomDeLaClasse.methode()
    System.out.println(Time.getCurrentYear());
    //Pour appeler une méthode static qui se situe dans la même classe
    //le nom de la méthode suffit

    System.out.println(getMethodStaticInSameClass());


    Utilisation des objets

    Pour créer un objet (instance), il faut utiliser le constructeur. & pour l'appeler, nous devons utiliser le mot clef new. Ici, vous pouvez voir :


    Address ad = new Address();
    soit : TYPE_OBJET nomVariable = new CONSTRUCTEUR;

    Ici, c'est le contsructeur vide qui est appelé.

    Dans le second cas, celui de Student, c'est le second constructeur qui est appelé. Celui avec les paramètres. Attention, un constructeur est une méthode particulière, nous devons donc (comme pour les méthodes) passer les arguments dans le bon ordre.


    Student student = new Student(1L, "GUYOTON", "Marie", date, ad);
    • 1L est la valeur donnée pour l'attribut id
    • "GUYOTON" est la valeur donnée pour l'attribut lastname
    • "Marie" est la valeur donnée pour l'attribut firstname
    • date est la valeur donnée pour l'attribut birthdate (date est bien un objet)
    • ad (objet créé précédement) est la valeur donnée pour l'attribut address
    //Création d'un Student adrien
    Student adrien = new Student("QUILLERE", "Adrien");
    //Création d'un Student leo avec un autre constructeur
    Student leo = new Student(2L, "CHARRIER");
    System.out.println(adrien.getFirstname());
    //NPE : NullPointerException
    //On essaie d'accéder à un attribut d'un objet qui n'existe pas

    System.out.println(adrien.getAddress().getPostalCode());
    //Ajout d'une adresse dans l'attribut address d'adrien
    adrien.setAddress(address);
    //Cela fonctionne puisque l'attribut address d'adrien n'est plus null
    System.out.println(adrien.getAddress().getPostalCode());

    Les énumérations

    Une énumération est un type d'objets avec un nombre d'instances déterminées. Il est impossible de créer un nouvel objet de ce type là. Les attributs (s'il y a) sont aussi définis dès le départ. Il est impossible d'utiliser les setters pour les modifier.

    Deux façons de les créer :

    • * Soit une simple liste ("de String") :

      public enum Role {
      ADMIN, USER;
      }


    • * Soit une liste d'objets avec des attributs non modifiables :

      //enum & non class
      public enum Days {
      //INSTANCES toujours déclarées en MAJUSCULES
      MONDAY(1, "lundi"),
      TUESDAY(2, "mardi"),
      WEDNESDAY(3, "mercredi"),
      THURSDAY(4, "jeudi"),
      FRIDAY(5, "vendredi"),
      SATURDAY(6, "samedi"),
      SUNDAY(7, "dimanche");

      //final pcq une fois la valeur affectée à l'attribut
      //elle ne change jamais
      private final int nbDay;
      private final String dayInFrench;

      /*Pas de public devant le constructeur
      pcq on ne peut pas créer d'objet Days
      */
      Days( int nbDay, String dayInFrench) {
      this .nbDay = nbDay;
      this .dayInFrench = dayInFrench;
      }

      /*Pas de setters*/
      public int getNbDay() {
      return this .nbDay;
      }
      public String getDayInFrench() {
      return this .dayInFrench;
      }
      }

    L'encapsulation

    L'encapsulation est un des grands principes de la programmation orientée objets. Le principe est simple : tous les attributs sont privés et nous créons des getters et setters SI BESOIN !

    Cela permet aux développeurs d'éviter de fausses manipulations comme changer un id en base de données. Cela peut aussi éviter des injections de code dans des attributs non utilisés...

    L'encapsulation passe par la portée des attributs et méthodes. Il en existe 4 :

    • * public : tout le projet a accès à l'attribut/méthode,
    • * private : seule la classe en elle-même a accès à la méthode ou à l'attribut,
    • * package (par défaut - aucun mot clef nécessaire lors de la déclaration) : seules les classes dans le même package ("répertoire") ont accès aux méthodes et aux attributs définis comme ceci,
    • * protected : seules les classes dans le même package ainsi que les classes filles ont accès aux méthodes et aux attributs.

    Voici un exemple concret : (Cliquez sur le nom des classes pour accéder au code)



    L'héritage

    L'héritage "génétique", c'est avoir sa propre personnalité, ses propres membres et pourtant, on hérite d'une partie de la personnalité de nos parents, de certaines caractéristiques (les yeux...). Qu'on le veuille ou non, cela nous "suit".

    En programmation, c'est pareil. Une classe fille hérite de tous les attributs de la mère (sous réserve que les getters et setters soient publics) ainsi que des méthodes.


    • L’héritage permet à un objet de reprendre tous les attributs de l’objet dont il hérite et d’avoir en plus ses attributs qui lui sont propres.

    • Cela permet aussi au nouvel objet d’utiliser à la fois ses méthodes issues de sa classe mais aussi les méthodes de l’objet dont il est issu.

    • Pour initialiser cet héritage, on utilise le mot clef extends. Ici, la classe Fille (Renegade) hérite de la classe mère Voiture qui hérite elle-même de la classe ObjetDeLocomotion.
    • public class Renegade extends Voiture...

    • Il utilise aussi le constructeur de la classe dont il hérite dans son propre constructeur à l’aide de la méthode super(). Cf. Renegade.java.

    • Il ne peut donc hériter que d’une seule classe. En effet, si l’objet pouvait hériter de plusieurs objets, nous ne saurions pas quel constructeur appeler.

    Voici un exemple concret : (Cliquez sur le nom des classes pour accéder au code)



    Le polymorphisme

    Le polymorphisme commence par le biais d’une interface abstraite. Une interface (abstraite) est une "classe" où des méthodes sont définies mais non implémentées. Cela permet des les implémenter différemment en fonction des objets auxquelles elles s’appliquent.

    Par exemple, prenons l'interface objetDeLocomotion. Dans celle ci est déclarée la méthode avancer(). Si nous créons trois objets voiture, train et avion, nous pouvons tous les implémenter de notre classe objetDeLocomotion car ils sont tous des objets de locomotion. Ils avancent donc tous.

    Nous devons donc à présent implémenter la méthode avancer(). Or, celle-ci ne sera pas la même si l’objet de locomotion est une voiture, un train ou un avion.

    // Les interfaces sont souvent nommés avec un "I" devant pour les différencier des classes
    //Aujourd'hui, les IDE utilisent des logos différents afin de mieux le faire
    public interface IObjetDeLocomotion {

    //Méthode déclarée mais non implémentée
    public void avancer();

    public void stop();
    }

    Pour cela, nous allons utiliser le mot clef implements. L'avantage de cette solution par rapport à l'héritage, c'est que nous pouvons implémenter plusieurs classes.

    public class Avion implements IObjetDeLocomotion, IDeuxiemeInterface {

    //On définit le comportement des méthodes présentes dans IObjetDeLocomotion
    @Override
    public void avancer() {
    System.out.println("Un avion vole pour avancer.");
    }

    @Override
    public void stop() {
    System.out.println("stop");
    }

    //Méthode qui provient de IDeuxiemeInterface
    @Override
    public void ok() {
    System.out.println("ok avion");
    }
    }

    Nous pouvons aussi utiliser ce concept avec l'héritage mais ce n'est pas son but premier. Pour cela, il suffit d’ajouter des méthodes abstraites dans la classe mère ou éventuellement des méthodes classiques dont on redéfinit le comportement dans les classes filles. Il faut pour cela rajouter un @Override devant la méthode en question.

    public abstract class ObjetDeLocomotion {

    private String name;
    //...

    public ObjetDeLocomotion( String pName){
    this .name = pName;
    }

    //Méthode pouvant être redéfinie dans les classes filles
    //Avec un comportement par défaut
    public String rouler(){
    return "rouler";
    }

    //Méthode abstraite à définir absolument dans les classes filles
    //Pas d'implémentation définie
    public abstract void test();

    }
    public class Avion extends ObjetDeLocomotion implements IObjetDeLocomotion, IDeuxiemeInterface {

    public Avion( String pName) {
    super (pName);
    }
    //Vu précédemment, implémentation des méthodes provenant des interfaces
    [...]

    //On redéfinit le comportement de la méthode rouler qui se trouve
    // dans ObjetDeLocomotion
    @Override
    public String rouler(){
    return "un avion roule juste pour décoller";
    }

    //On redéfinit la méthode abstraite héritée de ObjetDeLocomotion
    @Override
    public void test() {
    System.out.println("test avion");
    }
    }


    Voici un exemple concret : (Cliquez sur le nom des classes pour accéder au code)