Liste de  cours java

Java resume du language avec exemples d’application


Télécharger



Java résume du language avec exemples d’application

...

1 NOTIONS ÉLÉMENTAIRES

1.1 QU’EST-CE QU’UN PROGRAMME ?

L’objectif de la programmation est de créer des logiciels ou programmes. Ceux-ci sont constitués d’un ensemble de traitements qui permettent de trans



former des données numériques (les entrées) en d’autres données numériques (les sorties). Les données de sortie peuvent être affichées sous une forme graphique (avec des fenêtres comme le fond les programmes tels que Word et Excel) ou plus simplement affichées dans une console?1 sous forme de texte.

Que se passe-t-il pour l’ordinateur lorsqu’on exécute un programme ? Il va lire le fichier exécutable du programme comme une suite de 0 et de 1 (codage binaire) et exécuter l’une après l’autre les instructions ainsi codées. Cette suite de 0 et de 1 est appelée langage machine et est directement exécutable par le microprocesseur de l’ordinateur. Or il est très difficile pour un humain de programmer directement en binaire, c’est pourquoi on utilise un langage de programmation écrit en langage textuel dans un fichier source (fichier .java). Le fichier source est ensuite compilé? en langage binaire (fichier .class) puis exécuté pour réaliser les traitements :

Compilation Exécution

1.2 LES VARIABLES

Les variables constituent l’aspect le plus important de la programmation, puisqu’elles permettent de stocker dans un emplacement mémoire les données et de les transformer en utilisant des opérateurs?. On peut se représenter une variable comme une étiquette associée à une unique boîte dans laquelle est rangée une valeur d’un certain type (entier ou réel, par exemple) :

Avant de pouvoir utiliser une variable, il est nécessaire de la déclarer?, c’est-à-dire d’associer la variable à un emplacement de la mémoire et de spécifier son type. C’est en fonction du type de la variable que la taille de l’emplacement mémoire (en octet, soit 8 bits) et le codage binaire de la valeur seront déterminés. La déclaration se fait par une instruction de la forme :

  typeVariable nomVariable;

La variable nomVariable est de type typeVariable. Il lui est associé un emplacement mémoire qui contient une valeur qu’il faut initialiser?. Cela se fait en utilisant l’opérateur d’affectation “_” :

nomVariable = uneValeur;

L’affectation? écrit dans l’emplacement mémoire associé à nomVariable la valeur uneValeur. On dit alors que nomVariable “prend la valeur” uneValeur.

uneValeur doit être compatible avec le type de nomVariable. Par exemple si typeVariable est un entier, uneValeur doit être une valeur entière. On peut faire ici le parallèle entre le type d’une variable et l’unité de mesure d’une grandeur physique qui spécifie la nature de la grandeur manipulée.

On peut également faire la déclaration et l’initialisation d’une variable en une seule instruction:

1Le symbole ? indique que le mot est défini dans la section Glossaire.

 typeVariable nomVariable = uneValeur;

Dans la suite, nous allons détailler les quatre types dits “primitifs”?, qui sont directement accessibles en mémoire (contrairement aux types non primitifs que nous verrons dans la section 3), c’est-à-dire que la valeur affectée à la variable est stockée dans l’emplacement mémoire associée à celle-ci. Pour chacun de ces types, nous verrons également les opérateurs que l’on peut appliquer sur des variables de ce type pour transformer leur valeur.

1.2.1 LES TYPES PRIMITIFS

On distingue 4 catégories de types primitifs (entier, réel, booléens, caractères). L’intervalle de valeurs représentables pour chacun des types peut varier en fonction de l’espace mémoire qu’ils occupent.

LES ENTIERS En Java, tous les types permettant de représenter des entiers sont signés. Ainsi, sur n bits, on peut coder les entiers de −(2n−1) à 2n−1 − 1. Les valeurs négatives sont encodées en complément à 2. Pour un rappel sur cet encodage, nous vous renvoyons au cours de numération disponible sur la page du CIPC. Les différents types d’entiers sont les suivants :

...

On peut appliquer des opérateurs arithmétiques (+,-,*,/) à deux variables ou deux expressions de type entier (la composition étant possible comme en mathématiques). Le résultat de cette opération est également du type entier (ce qui permet la composition).

Lorsque les deux opérandes sont de type entier, l’opérateur / calcule la division entière et l’opérateur % calcule le reste de cette division.

Par exemple2 :

int valA = 7; int valB = 2;

int valC = valA / valB; // valC contient la valeur 3

int valD = valA % valB; // valD contient la valeur 1

LES RÉELS La description du codage des réels est décrite dans le cours de numération sur la page du CIPC. En Java il existe deux types de représentation pour les nombres réels: simple et double précision (respectivement les types float et double).

...

Comme toute donnée numérique, un caractère est encodé sous forme d’une suite de 0 et de 1 que l’on peut interpréter comme un entier non signé. Dans certains contextes, il est utile de manipuler directement ce code. On convertit alors la variable de type char en une variable de type int comme expliqué ci-dessous.

1.2.2 LES CONVERSIONS

La conversion (également appelée transtypage) d’un type primitif en un autre se fait de la manière suiv-ante :

  typeVariableA variableA = (typeVariableA) valeurB

Si variableA et valeurB ne sont pas du même type, cette instruction affecte à variableA la conversion de la valeur de valeurB dans le type typeVariableA :

Entier vers Réel la même valeur codée en réel

Réel vers Entier la partie entière du réel

Entier vers caractère le caractère dont le code est l’entier

Caractère vers Entier le code numérique correspondant au caractère

int i;

double x = 2;

i = (int) (x * 42.3); // i vaut 84

1.2.3 LES OPÉRATEURS DE COMPARAISON

Les opérateurs de comparaison permettent de comparer deux variables d’un même type primitif (entier, flottant, booléen et caractère) et renvoient une valeur booléenne :

== comparaison d’égalité

!= différence

< inférieur strict

<= inférieur ou égal (s’écrit de la même manière dont on le prononce)

> supérieur strict

>= supérieur ou égal (s’écrit de la même manière dont on le prononce)

Attention, l’opérateur = correspond à l’affectation alors que l’opérateur == correspond à la comparaison d’égalité, c’est-à-dire au signe = utilisé en mathématiques !!

Les expressions composées doivent être complètement parenthésées :

double a = 8;

boolean estDansIntervalle = ((a >= 0) && (a <= 10));

// vaut true ssi a appartient à [0,10]

1.3 LE PROGRAMME

De manière générale, la structure d’un programme simple est toujours la même. Cette structure de base doit être apprise par cœur, car elle constitue le squelette du programme. Il est conseillé, lors de la création d’un programme, de commencer par écrire cette structure. En effet, une fois cette structure créée, le programme est fonctionnel : il peut être compilé* et exécuté*. Bien entendu à ce stade, le programme ne fait strictement rien puisqu’il n’y a aucune instruction, seulement des commentaires.

public class Exemple{ //Exemple est le nom du programme

// écrit dans le fichier Exemple.java

public static void main (String[] args){

//bloc d’instructions du programme

//exécutées lors du lancement du programme

}}

Déclare et initialise deux variables celsius et fahrenheit, fahrenheit étant calculée à partir de celsius, pour ensuite les afficher à l’écran.

public class ConversionCelsiusVersFahrenheit{

public static void main (String[] args){

double celsius = 12.0;

double fahrenheit = ((9.0/5.0) * celsius) + 32.0;

System.out.print(celsius);

System.out.print(" degrés Celsius convertit en Fahrenheit vaut ");

System.out.println(fahrenheit);

}}

L’instruction System.out.print permet d’afficher la valeur d’une variable de type primitif ou un texte délimité par des guillemets.

 1.4 LES STRUCTURES DE CONTRÔLE

Le principe d’un programme est de modifier le contenu des variables à l’aide des instructions élémentaires que nous venons de voir (affectation et opérateurs). Or, nous pouvons vouloir que ces instructions ne soient réalisées que dans certains cas, ou bien nous pouvons vouloir répéter l’exécution de ces instructions. Ce sont les structures de contrôle qui permettent de spécifier si l’exécution d’un traitement est conditionnée ou bien si elle se fait de manière répétée.

1.4.1 BLOC D’INSTRUCTIONS

Les accolades {} permettent de délimiter un bloc d’instructions, c’est-à-dire un ensemble d’instructions qui vont être exécutées les unes à la suite des autres. Un bloc d’instructions peut, par exemple, être exécuté que lorsqu’une condition est vérifiée ou bien il peut être exécuté plusieurs fois de suite. Ce sont les structures de contrôle conditionnelles et itératives qui permettent d’exprimer cela. Les variables déclarées* dans un bloc sont accessibles à l’intérieur de ce bloc uniquement.

Deux variables de même nom peuvent être déclarées dans deux blocs distincts. Ce sont deux variables différentes associées à deux emplacements mémoires différents. Ainsi, leurs valeurs sont généralement différentes. Confondre l’une avec l’autre peut être une source d’erreur de programmation.

1.4.2 STRUCTURES CONDITIONNELLES

Les structures de contrôle conditionnelles permettent de spécifier à quelles conditions un bloc d’instructions va être exécuté. Cette condition est exprimée par une expression logique.

LA STRUCTURE ALTERNATIVE Le premier type de conditionnelle s’écrit comme suit:

if (condition) {// équivalent à (condition == true)

// bloc d’instructions exécutées si condition est vraie } else {

// bloc d’instructions exécutées si condition est fausse

}

Cette structure de contrôle exprime une alternative. Or, il est possible de vouloir qu’un bloc soit exécuté sous une certaine condition et que sinon, aucune instruction ne soit exécutée. Dans ce cas, la clause else et son bloc sont supprimés. Les parenthèses autour de condition, qui est variable ou une expression à valeur booléenne, sont obligatoires.

Affiche un message si la température est supérieure à 50.

public class QuelleUnite{

public static void main (String[] args){

int temperature = 36;

if(temperature > 50) {

System.out.println("La température est probablement en Fahrenheit");

}}}

LA STRUCTURE CHOIX MULTIPLES Le second type de conditionnelle permet de faire plusieurs tests de valeurs sur le contenu d’une même variable. Sa syntaxe est la suivante :

switch (variable) {

case valeur1 :

Liste d’instructions // exécutées si (variable == valeur1)

break;

case valeur2 :

Liste d’instructions // exécutées si (variable == valeur2)

break;

...

case valeurN :

Liste d’instructions // exécutées si (variable == valeurN)

break;

default:

Liste d’instructions // exécutées sinon

}

Le mot clé default précède la liste d’instructions qui sont exécutées lorsque variable a une valeur différentes de valeur1,..,valeurN. Le mot clé break indique que la liste d’instructions est terminée.

1.4.3 STRUCTURES ITÉRATIVES

Il existe 3 formes de structure itérative, chacune a un cadre d’utilisation bien spécifique que nous allons voir.

L’ITÉRATION RÉPÉTÉE n FOIS La première forme itérative est la boucle for. Elle permet de répéter un bloc d’instructions un nombre de fois fixé. Dans sa syntaxe, il faut déclarer et initialiser la variable qui sert de compteur de tours de boucle, indiquer la condition sur le compteur pour laquelle la boucle s’arrête et enfin donner l’instruction qui incrémente* ou décrémente* le compteur :

for (int compteur = 0 ; compteur < n ; compteur = compteur + 1) {

// bloc instructions répétées n fois

}

ou

for (int compteur = n ; compteur > 0 ; compteur = compteur - 1) {

// bloc instructions répétées n fois

}

Affiche la conversion en Fahrenheit des degrés Celsius de 0 à 39.

public class ConversionCelsiusVersFahrenheit{

public static void main (String[] args){

for(int celsius = 0; celsius < 40; celsius = celsius + 1) {

double fahrenheit = ((9.0/5.0) * celsius) + 32.0;

System.out.print(celsius);

System.out.print(" degres Celsius convertit en Fahrenheit vaut ");

System.out.println(fahrenheit);

}}}

La boucle for s’utilise lorsque l’on connaît a priori le nombre de répétitions à effectuer.

L’ITÉRATION RÉPÉTÉE TANT QU’UNE CONDITION EST VRAIE La seconde forme d’itérative est la boucle while. Elle exécute le bloc d’instructions tant que la condition est vraie. Le bloc peut ne jamais être exécuté. La syntaxe est la suivante :

while (condition) { // équivalent à (condition == true)

// bloc d’instructions répétées tant que condition est vraie. // condition doit être modifiée dans ce bloc

}

Cette structure exécute le bloc d’instructions tant que (while en anglais) la condition est réalisée.

 Il est important de toujours s’assurer que la condition deviendra fausse lors d’une itération de la structure itérative. Dans le cas contraire, l’exécution du programme ne s’arrêtera jamais.

Affiche la conversion en Fahrenheit des degrés Celsius tant que la conversion est inférieur à 100.

public class ConversionCelsiusVersFahrenheit{

public static void main (String[] args){

int celsius = 0;

double fahrenheit = ((9.0/5.0) * celsius) + 32.0;

while(fahrenheit < 100) {

System.out.print(celsius);

System.out.print(" degres Celsius convertit en Fahrenheit vaut ");

System.out.println(fahrenheit);

celsius = celsius + 1;

fahrenheit = ((9.0/5.0) * celsius) + 32.0;

}}}

La boucle while s’utilise lorsque le nombre d’itérations n’est pas connu a priori mais peut s’exprimer au moyen d’une expression à valeur booléenne qui devient fausse lorsque la répétition doit s’arrêter.

L’ITÉRATION EXÉCUTÉE AU MOINS UNE FOIS La troisième forme d’itérative est la boucle “do while”. C’est une variante de la boucle while, où la condition d’arrêt est testée après que les instructions ont été exé-cutées :

do {

// bloc d’instructions exécutées

// condition doit être modifiée dans ce bloc

} while (condition); // si condition est vraie,

// le bloc est exécuté à nouveau

Ne pas oublier le ; après la condition d’arrêt. Le bloc d’instructions est exécuté au moins une fois.

Affiche la conversion en Fahrenheit des degrés Celsius jusqu’à ce que le degré Fahrenheit soit supérieur ou égale à 100.

public class ConversionCelsiusVersFahrenheit{

public static void main (String[] args){

double fahrenheit;

int celsius = 0;

do {

fahrenheit = ((9.0/5.0) * celsius) + 32.0;

System.out.print(celsius);

System.out.print(" degres Celsius convertit en Fahrenheit vaut ");

System.out.println(fahrenheit);

celsius = celsius + 1;

} while (fahrenheit < 100);

}

}

 2 LES MÉTHODES

Une méthode est un bloc d’instructions pouvant être exécutées par un simple appel* de la méthode dans le bloc du programme principal (méthode main) ou dans une autre méthode. Les méthodes permettent d’exécuter dans plusieurs parties du programme un même bloc d’instructions. On est amené à créer une méthode dans deux cas de figure :

  • Pour regrouper un ensemble d’instructions qui participent à la réalisation d’une même tâche. Cela permet de rendre le programme plus lisible et compréhensible par une autre personne (ou vous-même lors du TP suivant) et de faciliter la mise au point du programme (correction et tests3). A ce bloc d’instructions est associé un nom, choisi en rapport avec le traitement réalisé par la méthode.
  • Pour regrouper un ensemble d’instructions qui sont répétées à différents endroits du programme (contrairement aux formes itératives qui répètent le bloc d’instructions de manière consécutive).

Le rôle d’une méthode est de traiter des données. Cela signifie qu’en général, la méthode effectue un traitement à partir des données qui entrent, et renvoie un résultat.

2.1 LES MÉTHODES PRÉDÉFINIES

En Java, il existe de nombreuses méthodes prédéfinies. La plus connue est sans doute la méthode suivante qui permet d’afficher une chaîne de caractères à l’écran :

System.out.println("la chaîne de caractères à afficher");

D’autres exemples de méthodes que vous pouvez utiliser sont celles de la librairie Math: sqrt, cos, sin, abs, etc.. Lorsqu’on appelle une méthode, on utilise son nom suivi de la liste de ses paramètres effectifs (séparés par une virgule) entre parenthèses :

  nomMethode(parametre_1,... , parametre_n);

Si cette méthode renvoie un résultat, il faut alors affecter ce résultat à une variable de type compatible pour pouvoir ensuite utiliser ce résultat :

double racine = Math.sqrt(5.2);

2.2 LES MÉTHODES PROPRES

DÉCLARATION D’UNE MÉTHODE La définition d’une méthode s’appelle déclaration*. La déclaration d’une méthode se fait selon la syntaxe suivante :

static TypeRetour nomMethode(Type1 param1,..., TypeN paramN) { //bloc d’instructions

return valeurRetournee;

}

Le fait que l’on doive précéder la déclaration d’une méthode par le mot clé static est expliqué page 15. TypeRetour est le type de valeurRetournee, la valeur renvoyée par la méthode. Si la méthode ne renvoie aucune valeur, le mot-clé void est utilisé à la place du type de la valeur retournée.

Pour une raison inconnue des enseignants, il y a souvent confusion entre le fait de retourner une valeur et le fait de l’afficher.

Les paramètres correspondent aux données d’entrée de la méthode. Au niveau de la déclaration, les paramètres sont dits “formels” : ce sont des variables qui représentent chaque donnée d’entrée. On peut faire ici l’analogie avec la variable x utilisée en mathématique lorsque l’on définit une fonction f (x). Les paramètres sont facultatifs, mais s’il n’y a pas de paramètres, les parenthèses doivent rester présentes.

APPEL D’UNE MÉTHODE C’est au moment de l’appel de la méthode que les paramètres formels sont initialisés, c’est-à-dire qu’une valeur leur est affectée. Les paramètres “effectifs` de l’appel, ceux passés en argument de la méthode au moment de l’appel, sont affectés aux paramètres formels de la méthode (ceux de la définition de la méthode) par position : la valeur du premier paramètre effectif est affectée au premier paramètre formel, et ainsi de suite. Les paramètres effectifs peuvent être des valeurs ou des variables.

static int addition(int x, int y) {//x et y sont les paramètres formels

return x + y;

}

public static void main (String[] args) {

int a = 7;

int somme = addition(a,3);//a et 3 sont les paramètres effectifs de l’appel

//x prend la valeur de a et y prend la valeur 3

}

En Java, le passage des paramètres se fait par valeur, c’est-à-dire que la valeur du paramètre effectif est affectée au paramètre formel. Ainsi, si la valeur du paramètre formel est modifiée dans le bloc de la méthode, cette modification est locale à la méthode mais n’est pas repercutée dans le contexte appelant.

Rien ne vaut un petit exemple ;-)

static int addition(int entierA, int entierB) {

entierA = entierA + 1; //entierA est modifié

return entierA + entierB;

}

public static void main (String[] args) {

int a = 7;

int b = 3;

int somme = addition(a,b); // la valeur de a est affectée à entierA

// a vaut toujours 7 après l’appel de la méthode addition

}

2.3 LES MÉTHODES RÉCURSIVES

Nous avons précédemment précisé qu’il était possible qu’une méthode appelle dans son bloc une autre méthode. Il est également possible qu’elle s’appelle elle-même. A priori, l’intérêt peut paraître limité, mais la récursivité permet d’effectuer certains calculs mathématiques, en particulier avec les suites définies par récurrence. Par exemple, on peut calculer la factorielle d’un nombre entier en utilisant la récursivité. Pour cela, on peut procéder de la manière suivante, par exemple si l’on souhaite calculer la factorielle de 4 :

4! = 4 × 3!

3! = 3 × 2!

2! = 2 × 1!

1! = 0!

0! = 1

Dans ce calcul, on constate une récurrence : pour calculer 4!, il suffit de calculer 3!, puis 2!, et ainsi de suite, jusqu’à un cas directement résolu (sans expression récursive, le cas de base). Nous pouvons alors créer une méthode calculeFactorielle ayant comme paramètre un entier n, et renvoyant l’entier n!. On voit qu’il suffit que la fonction s’appelle elle-même suivant ce même principe pour calculer facilement la factorielle de n’importe quel nombre entier. Cependant, arrivé au calcul de 0!, il est nécessaire que la fonction renvoie directement la valeur 1, sans faire d’appel récursif. En d’autres mots, il ne faut pas que l’appel de calculeFactorielle(0) provoque l’appel de calculeFactorielle(-1). Il renvoie directement 1, afin de ne pas provoquer une infinité d’appels :

...

2.4 UN EXEMPLE

L’exemple suivant présente un programme avec deux méthodes: CelsiusVersFahrenheit convertit valeurAConvertir, une valeur réelle représentant une température en degré Celsius, en une température en degré Fahrenheit. La valeur en Fahrenheit est retournée par la méthode. CelsiusVersKelvin convertit valeurAConvertir, une valeur réelle représentant une température en degré Celsius, en une température en degré Kelvin. La température en Kelvin est affichée et aucune valeur n’est retournée par la méthode.

Observez la différence entre les appels des deux méthodes dans le main: la valeur retournée par CelsiusVersFahrenhei est affectée à la variable temperature, puis elle est affichée. La méthode CelsiusVersKelvin est directe  ment appelée, sans affectation de sa valeur de retour, puisque celle-ci n’en n’a pas (mot clé void dans la déclaration).

Le programme considère les températures entières, en degré Celsius, de 0 à 39 inclus. Une fois sur deux, sa conversion en degré Fahrenheit est affichée, l’autre fois c’est sa conversion en degré Kelvin qui est affichée.

Notez le changement de valeur de la variable calculeFahrenheit à chaque tour de boucle.

...

3 LES TYPES NON PRIMITIFS 3.1 GÉNÉRALITÉS

Nous avons vu (section 1.2) qu’une variable permet de stocker dans un emplacement mémoire une donnée d’un certain type, et que cette donnée peut être transformée en utilisant des opérateurs spécifiques de son type. Or, au lieu de manipuler une seule valeur, il est parfois beaucoup plus commode qu’une variable soit associée à une collection de valeurs. C’est ce que permettent les types non primitifs, appelés objet en Java.

Supposons que l’on souhaite réaliser un programme permettant d’écrire des courriers de manière automatique aux abonnés d’une bibliothèque. Pour chaque abonné, nous connaissons son nom, prénom, le nombre de volumes empruntés ainsi que le nombre de jours écoulés depuis l’emprunt. Nous devons alors manipuler des centaines d’abonnés, chacun décrit par 4 valeurs, certaines de ces valeurs (nom, prénom) étant également une collection de valeurs primitives (une séquence de caractères). Ce type de traitement est impossible en n’utilisant que des types primitifs.

Tout comme des opérateurs spécifiques sont associés à chaque type primitif*, il peut être commode de définir des opérateurs ou méthodes permettant d’interroger ou transformer les valeurs de ces objets.

Nous souhaitons disposer d’une méthode estEnRetard qui renvoie la liste des abonnés dont le dernier emprunt a été effectué il y a plus de 21 jours.

Dans cette section nous allons introduire les concepts relatifs aux objets nécessaires à la création d’un tel programme. Avant toute chose, nous allons expliquer les mécanismes communs à tout objet Java, c’est-à-dire, la façon dont un objet est stocké en mémoire et la manière dont on l’instancie*, c’est-à-dire la façon dont on réserve l’emplacement mémoire nécessaire à l’objet. Nous verrons également comment fonctionnent les opérateurs de comparaison sur les objets.

STOCKAGE DES OBJETS EN MÉMOIRE Tout comme les variables de type primitif, une variable de type objet est associé à un emplacement mémoire de taille fixe qui contient une unique valeur. Dans cet emplacement mémoire est stocké une valeur de type adresse qui indique l’adresse* de l’emplacement mémoire où sont stockées, de manière contiguë, toutes les valeurs de l’objet. Ainsi, la variable est liée aux données de manière indirecte : elle contient l’adresse à laquelle on peut trouver les données. La variable qui est manipulée est en fait une référence* à l’emplacement mémoire où se trouve l’ensemble des données.

1