Pensez en Java

Comment penser comme un informaticien ?

Allen B. Downey

Traducteurs

| Laethy | Autran |

Certains livres Java introduisent les notions de classes et d’objets immédiatement. D'autres commencent par la programmation procédurale et font une transition vers une approche orientée objet plus progressivement.

Un grand nombre des fonctionnalités orientées objet de Java ainsi que leur implémentation sont influencées par des problèmes rencontrés avec d’autres langages de programmation. Certaines de ces fonctionnalités sont difficiles à expliquer lorsque les utilisateurs ne connaissent pas les problèmes qu’elles résolvent.

Nous abordons la programmation orientée objet le plus rapidement possible, en introduisant le plus clairement possible chacun des concepts, de manière à permettre aux lecteurs de pratiquer chaque idée séparément avant de passer à autre chose. Il faut donc du temps pour y arriver.

Mais vous ne pouvez pas écrire de programmes Java (même bonjour) sans aborder des fonctionnalités orientées objet. Dans certains cas, nous expliquons brièvement une fonctionnalité lorsqu'elle apparaît pour la première fois, puis nous l'expliquons plus en profondeur ultérieurement.

Commentez

- Titre : Pensez en Java

- Auteur : Allen B. Downey

- Traducteurs : Laethy, Autran

- Parution : 28 février 2019

- Mise à jour : 8 mai 2021

- Licence : Le contenu de cet article est rédigé par Allen B. Downey et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- Préface

- La philosophie derrière le livre

- Annexes

- Utilisation des exemples de code

- I. Le cheminement d’un programme

- I-A. Qu’est ce que la programmation

- I-B. Qu’est ce que l’informatique ?

- I-C. Les langages de programmation

- I-D. Le programme Hello World

- I-E. Afficher les chaînes de caractères

- I-F. Séquence d’échappement

- I-G. Formatage de code

- I-H. Débogage

- I-I. Glossaire

- I-J. Exercice 1

- I-K. Exercice 2

- I-L. Exercice 3

- II. Variables et opérateurs

- II-A. Déclaration des variables

- II-B. Assignation

- II-C. Diagramme d’état

- II-D. Affichage des variables

- II-E. Opérateurs arithmétiques

- II-F. Nombres à virgule flottante

- II-G. Erreurs d’arrondis

- II-H. Opérateurs pour les chaînes de caractères

- II-I. Composition

- II-J. Types d’erreurs rencontrés

- II-K. Vocabulaire

- II-L. Exercices

- III. Entrées et sorties conversationnelles

- III-A. La classe System

- III-B. La classe Scanner

- III-C. Structure d’un programme

- III-D. Des pouces aux centimètres

- III-E. Littéraux et constantes

- III-F. Sortie formatée

- III-G. Des centimètres aux pouces

- III-H. Opérateur modulo

- III-I. Assembler tout cela

- III-J. Les bogues de Scanner

- III-K. Vocabulaire

- III-L. Exercices

- IV. Structures conditionnelles et récursion

- V. Entrée et sortie

- VI. Méthodes Void

- VII. Conditions et logique

- VIII. Méthodes de valeur

- IX. Boucles

- X. Tableaux

- XI. Chaîne de caractères et autres

- XII. Objets

- XIII. Les classes

- XIV. Tableaux d'objets

- XV. Objets de tableaux

- XVI. Objets d'objets

- XVII. Outils de développement

- XVIII. Graphiques 2D en Java

- XIX. Le débogage

- XX. Indice

Préface

Pensez en Java est une introduction à l'informatique et à la programmation destinée aux lecteurs ayant peu ou pas d'expérience. Nous commençons par les concepts les plus élémentaires et prenons soin de définir tous les termes lorsqu’ils sont utilisés pour la première fois. Le livre présente chaque nouvelle idée dans une progression logique. Les sujets plus larges, tels que la récursivité et la programmation orientée objet, sont divisés en plus petits exemples et introduits sur plusieurs chapitres.

Ce livre est intentionnellement concis. Chaque chapitre couvre le matériel d’une semaine de cours en université. Il ne s'agit pas d'une présentation complète de Java, mais d'une première introduction à des concepts et techniques de programmation. Nous commençons par de petits problèmes et des algorithmes de base et travaillons en direction de la conception orientée objet. Dans le vocabulaire de la pédagogie informatique, ce livre utilise l'approche « étude tardive des objets ».

La philosophie derrière le livre

Voici les principes directeurs de ce livre :

- un concept à la fois. Nous décomposons les sujets qui perturbent les débutants en une série de petites étapes, leur permettant d’appliquer chaque nouveau concept séparément avant de continuer ;

- équilibre entre Java et concepts. Le livre ne concerne pas principalement Java ; il utilise des exemples de code pour illustrer la science informatique. La plupart des chapitres commencent par les fonctionnalités du langage et se terminent par des concepts ;

- concision. Un objectif important du livre est d'être suffisamment petit pour que les étudiants puissent lire et comprendre le texte en entier dans un cours universitaire d'un semestre ;

- accent mis sur le vocabulaire. Nous essayons d'introduire un minimum de termes et de les définir avec soin lors de leur première utilisation. Nous les récapitulons également dans des glossaires à la fin de chaque chapitre ;

- développement de programme. Il existe de nombreuses stratégies pour écrire des programmes, notamment ascendantes, descendantes et autres. Nous montrons plusieurs techniques de développement de programmes, permettant aux lecteurs de choisir les méthodes qui leur conviennent le mieux ;

- plusieurs courbes d'apprentissage. Pour écrire un programme, vous devez comprendre l'algorithme, connaître le langage de programmation et pouvoir déboguer les erreurs. Nous abordons ces aspects, ainsi que d’autres, tout au long du livre, et incluons une annexe résumant nos conseils.

Annexes

Les chapitres de ce livre sont destinés à être lus dans l'ordre, car chacun s'appuie sur le précédent. Nous incluons également trois annexes avec du matériel qui peut être lu à tout moment.

Annexe 1 : outils de développement

Les étapes de compilation, d’exécution et de débogage du code Java dépendent des détails de l’environnement de développement et du système d’exploitation. Nous avons évité de mettre ces détails dans le texte principal, car ils peuvent être gênants. Au lieu de cela, nous fournissons à cette annexe une brève introduction à DrJava - un environnement de développement interactif (EDI) utile pour les débutants – et à d’autres outils de développement, notamment Checkstyle pour la qualité du code et JUnit pour les tests.

Annexe 2 : graphiques Java 2D

Java fournit des bibliothèques permettant de travailler avec des graphiques et des animations. Ces rubriques peuvent intéresser les étudiants. Les bibliothèques requièrent des fonctionnalités orientées objet que les lecteurs ne comprendront pas complètement avant le Chapitre 11, mais qui peuvent être utilisées beaucoup plus tôt.

Annexe 3 : débogage

Nous fournissons des suggestions de débogage tout au long du livre, mais nous rassemblons également nos conseils de débogage dans une annexe. Nous recommandons aux lecteurs de relire cette annexe plusieurs fois au cours de la lecture du livre.

Utilisation des exemples de code

La plupart des exemples de code de ce cours sont disponibles dans un dépôt Git à l'adresse https://github.com/AllenDowney/ThinkJavaCode. Git est un « système de contrôle de version » qui vous permet de garder la trace des fichiers qui composent un projet. Une collection de fichiers sous le contrôle de Git est appelée un « dépôt ».

GitHub est un service d'hébergement qui fournit un stockage pour les dépôts Git et une interface web pratique. Il offre plusieurs façons de travailler avec le code :

- vous pouvez créer une copie du dépôt sur GitHub en appuyant sur le bouton Fork. Si vous n'avez pas encore de compte GitHub, vous devez en créer un. Après la bifurcation, vous disposerez de votre propre dépôt sur GitHub, que vous pourrez utiliser pour suivre le code que vous écrivez. Vous pouvez ensuite « cloner » le dépôt, ce qui télécharge une copie des fichiers sur votre ordinateur ;

- vous pouvez également cloner le référentiel sans forker. Si vous choisissez cette option, vous n'avez pas besoin d'un compte GitHub, mais vous ne pourrez pas enregistrer vos modifications sur GitHub ;

- si vous ne voulez pas du tout utiliser Git, vous pouvez télécharger le code dans une archive ZIP en utilisant le bouton « Télécharger ZIP » sur la page GitHub, ou ce lien : http://tinyurl.com/ThinkJavaCodeZip.

Après avoir cloné le dépôt ou décompressé le fichier ZIP, vous devriez avoir un répertoire appelé ThinkJavaCode avec un sous-répertoire pour chaque chapitre du livre.

Tous les exemples de ce livre ont été développés et testés avec le kit de développement Java SE 8. Si vous utilisez une version plus récente, les exemples de ce livre devraient continuer à fonctionner. Si vous utilisez une version plus ancienne, il se peut que certains d'entre eux ne fonctionnent pas.

I - Le cheminement d’un programme

Le but de ce livre est de vous apprendre à penser comme un informaticien. Cette façon de penser combine certaines des meilleures caractéristiques des mathématiques, de l'art de l'ingénieur et des sciences. Comme les mathématiciens, les informaticiens utilisent des langages formels pour désigner des idées (spécifiquement, des calculs). Comme les ingénieurs, ils conçoivent des choses, en assemblant des composants en systèmes et en évaluant des compromis entre les différentes solutions. Comme les scientifiques, ils observent le comportement des systèmes complexes, formulent des hypothèses et testent des prévisions.

La compétence la plus importante pour un informaticien est la capacité de résoudre des problèmes. Et cela implique la capacité de formuler des problèmes, de penser de manière créative aux solutions, et d'exprimer une solution claire et précise. Il se trouve que le processus d'apprentissage de la programmation est une excellente occasion de pratiquer les compétences de résolution de problèmes. Voilà pourquoi ce chapitre est appelé « Le cheminement d’un programme ».

À un certain niveau, vous allez apprendre à programmer, une compétence utile en elle-même. Sur un autre plan, vous pourrez utiliser la programmation comme un moyen pour une fin. Au fur et à mesure que nous avançons, cette fin deviendra plus claire.

I-A - Qu’est ce que la programmation

Un programme est une suite d'instructions qui spécifie comment effectuer une opération. L’opération peut être un calcul mathématique, comme résoudre un système d'équations ou trouver les racines d'un polynôme, mais il peut aussi être un calcul symbolique tel que la recherche et le remplacement de texte dans un document ou quelque chose de graphique, comme le traitement d'une image ou la lecture d'une vidéo.

Les détails diffèrent d'un langage à un autre, mais quelques instructions de base apparaissent dans presque tous les langages :

- entrée : obtenir des données à partir du clavier, d'un fichier, du réseau ou un autre dispositif ;

- sortie : afficher des données à l'écran, les enregistrer dans un fichier, les envoyer sur le réseau, etc. ;

- maths : effectuer des opérations mathématiques de base, comme l'addition et la multiplication ;

- exécution conditionnelle : vérifier certaines conditions et exécuter le code approprié ;

- répétition : effectuer une action à plusieurs reprises, le plus souvent avec une certaine variation.

Croyez-le ou non, c'est à peu près tout ce qu'il y a à faire. Chaque programme que vous avez déjà utilisé, quelle que soit sa complexité, est constitué d'instructions qui ressemblent à peu près à celles-ci. Alors, vous pouvez percevoir la programmation comme un processus de division d'une grande tâche complexe en sous-tâches de plus en plus petites, jusqu'à ce que les sous-tâches soient assez simples pour être effectuées en utilisant l'une de ces instructions de base.

I-B - Qu’est ce que l’informatique ?

L'un des aspects les plus intéressants de la programmation est que nous pouvons décider de comment résoudre un problème particulier lorsqu'il existe plusieurs solutions. Par exemple, il existe plusieurs façons de trier une liste de numéros, et chaque façon a ses avantages. Afin de déterminer quel est le meilleur moyen pour une situation donnée, nous avons besoin de techniques pour décrire et analyser les solutions de manière formelle.

L'informatique est la science des algorithmes, y compris leur découverte et leur analyse. Un algorithme est une séquence d'étapes qui spécifie comment résoudre un problème. Certains algorithmes sont plus rapides que d'autres et certains utilisent moins d'espace mémoire. Au fur et à mesure que vous apprenez à développer des algorithmes pour des problèmes que vous n'avez pas résolus auparavant, vous apprenez aussi à penser comme un informaticien.

Concevoir des algorithmes et écrire du code est difficile et sujet aux erreurs. Pour des raisons historiques, les erreurs de programmation sont appelées bogues (ou bugs, en anglais), et le processus de suivi et de correction est appelé débogage. Au fur et à mesure que vous apprendrez à déboguer vos programmes, vous développerez de nouvelles compétences en résolution de problèmes. Vous devrez penser de façon créative lorsque des erreurs inattendues se produiront.

Bien que cela puisse être frustrant, le débogage est une partie intellectuellement riche, stimulante et intéressante de la programmation informatique. À certains égards, le débogage est comme un travail de détective. Vous êtes confronté à des indices, et vous devez déduire les processus et les événements qui ont conduit aux résultats que vous voyez. Penser à corriger des programmes et améliorer leurs performances conduit parfois même à la découverte de nouveaux algorithmes.

I-C - Les langages de programmation

Dans ce cours, Java est le langage de programmation que nous allons aborder. Java est un langage de haut niveau. Python, C et C ++, Ruby et JavaScript sont d'autres langages de haut niveau dont vous avez peut-être entendu parler.

Avant de pouvoir fonctionner, les programmes écrits dans les langages de haut niveau doivent être traduits dans un langage de bas niveau, également appelé "langage machine". Cette traduction prend du temps, ce qui est un petit inconvénient des langages de haut niveau. Mais les langages de haut niveau ont deux avantages :

- il est beaucoup plus facile de programmer dans un langage de haut niveau. L’écriture des programmes prend moins de temps, ils sont plus courts et plus faciles à lire, et ils sont plus susceptibles d'être corrects ;

- les langages de haut niveau sont portables, ce qui signifie qu'ils peuvent fonctionner sur différents types d'ordinateurs avec peu ou pas de modifications. Les programmes de bas niveau ne peuvent fonctionner que sur un type d'ordinateur et doivent être réécrits pour fonctionner sur un autre.

Deux types de programmes traduisent des langages de haut niveau en langages de bas niveau : les interpréteurs et les compilateurs. Un interpréteur lit un programme de haut niveau et l'exécute, ce qui signifie qu'il fait ce que dit le programme. Il traite le programme progressivement, en alternant la lecture des lignes de code et l'exécution des calculs. La figure 1.1 montre la structure d'un interpréteur.

Figure 1.1: Comment les langages interprétés sont exécutés.

En revanche, un compilateur lit le programme entier et le traduit complètement avant l’exécution du programme. Dans ce contexte, le programme de haut niveau est appelé le code source, et le programme traduit est quant à lui appelé le code objet ou l'exécutable. Une fois qu'un programme est compilé, vous pouvez l'exécuter à plusieurs reprises sans autre traduction. Par conséquent, les programmes compilés s'exécutent souvent plus vite que les programmes interprétés.

Java est à la fois compilé et interprété. Au lieu de traduire les programmes directement en langage machine, le compilateur Java génère du bytecode. Similaire au langage machine, le bytecode est facile et rapide à interpréter. Mais il est également portable, il est donc possible de compiler un programme Java sur une machine, de transférer le bytecode vers une autre machine et de l'y exécuter. L'interpréteur qui exécute le bytecode est appelé "Java Virtual Machine" (JVM).

Figure 1.2: Processus de compilation et d’exécution d’un programme Java

La figure 1.2 montre les étapes de ce processus. Bien que cela puisse sembler compliqué, ces étapes sont automatisées pour vous dans la plupart des environnements de développement. Habituellement, vous n'avez qu'à appuyer sur un bouton ou taper une seule commande pour compiler et exécuter votre programme. D'un autre côté, il est important de savoir quelles sont les étapes en arrière-plan, afin de comprendre les différentes erreurs.

I-D - Le programme Hello World

Traditionnellement, le premier programme que vous écrivez lors de l'apprentissage d'un nouveau langage de programmation, s'appelle le programme Hello World. Tout ce qu'il fait est d’afficher « hello world » à l'écran. En Java, cela ressemble à ceci :

1. public class Hello {

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

3. // Affiche un résultat simple

4. System.out.println("Hello, World!");

5. }

6. }

Lorsque ce programme s'exécute, il affiche :

Hello, World!

Notez que la sortie n'inclut pas les guillemets.

Les programmes Java sont constitués de définitions de classes et de méthodes, et les méthodes sont constituées d'instructions. Une instruction est une ligne de code qui effectue une opération de base. Dans le programme Hello, World! ; la ligne suivante est une instruction d'impression qui affiche un message à l'écran :

System.out.println("Hello, World!");

System.out.println affiche les résultats sur l'écran ;

println signifie « impression de la ligne ». Ce terme peut être source de confusion : impression peut signifier à la fois « afficher à l'écran » et « envoyer à l'imprimante ». Dans ce cours, nous allons essayer de dire « afficher » quand nous entendons sortie à l'écran. Comme la plupart des instructions, l'instruction d'impression se termine par un point-virgule (;).

Java est "sensible à la casse", ce qui signifie que les lettres capitales (majuscules) et les minuscules ne sont pas interchangeables. Dans cet exemple, le mot System doit commencer par une lettre majuscule ; system et SYSTEM ne fonctionneront pas.

Une méthode est une suite d'instructions nommées. Ce programme définit une méthode nommée main :

public static void main(String[] args)

Le nom et le format de la méthode main sont spéciaux : lorsque le programme s'exécute, il commence à la première instruction de main et se termine après la dernière instruction. Plus tard, nous verrons des programmes qui définissent non pas une seule, mais plusieurs méthodes.

Une classe est une collection de méthodes. Ce programme définit une classe nommée Hello. Vous pouvez donner un nom quelconque à une classe, mais par convention, le nom des classes commence par une lettre majuscule. Le nom de la classe doit correspondre au nom du fichier dans lequel elle se trouve ; donc cette classe doit être dans un fichier nommé Hello.java.

Java utilise des accolades ({et }) pour regrouper les éléments. Dans Hello.java, les accolades les plus externes contiennent la définition de classe(s) et les accolades internes contiennent la définition de la méthode.

Un commentaire est une ligne qui commence par deux barres obliques (//) ; ce n’est rien d’autre que du texte explicatif du code. Lorsque le compilateur voit //, il ignore tout jusqu'à la fin de la ligne. Les commentaires n'ont aucun effet sur l'exécution du programme, mais ils permettent à d'autres programmeurs (et à vous-même) de comprendre plus facilement ce que vous avez l'intention de faire.

I-E - Afficher les chaînes de caractères

Vous pouvez mettre autant d'instructions que vous le souhaitez dans main. Par exemple, pour afficher plus d'une ligne à l’écran :

1. public class Hello {

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

3. // Affiche un résultat simple

4. System.out.println("Hello,World!");// Première ligne

5. System.out.println("How are you?"); // Une autre ligne

6. }

7. }

Comme le montre cet exemple, vous pouvez placer des commentaires à la fin d'une ligne ou encore sur une ligne entière

Les phrases qui apparaissent entre guillemets sont appelées chaînes de caractères, car elles contiennent une séquence de "caractères" reliés ensemble. Les caractères peuvent être des lettres, des chiffres, des signes de ponctuation, des symboles, des espaces, des tabulations, etc.

System.out.println ajoute une nouvelle ligne qui se situe au début de la ligne suivante. Si vous ne voulez pas de retour à la ligne, vous pouvez utiliser print au lieu de println :

1. public class Goodbye {

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

3. System.out.print("Goodbye, ");

4. System.out.println("cruel world");

5. }

6. }

Dans cet exemple, la première instruction n'ajoute pas de saut de ligne, donc la sortie apparaît sur une seule ligne, c’est à dire Goodbye, cruel world. Notez qu'il y a une espace à la fin de la première chaîne, qui apparaît à la sortie.

I-F - Séquence d’échappement

Il est possible d'afficher plusieurs lignes à la sortie avec une seule ligne de code. Il suffit de dire à Java où placer les sauts de ligne.

1. public class Hello {

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

3. System.out.print("Hello!\nHow are you doing?\n");

4. }

5. }

La sortie est sur deux lignes, chacune se terminant par un caractère de saut de ligne :

Hello!
How are you doing?

Le \n est une séquence d'échappement, c'est-à-dire une suite de caractères représentant un caractère spécial. L’antislash (ou barre oblique inverse) vous permet d'échapper à l'interprétation littérale de la chaîne. Notez qu'il n'y a pas d'espace entre \n et How. Si vous y ajoutez une espace, il y aura en aura une au début de la deuxième ligne.

\n

Nouvelle ligne

\t

Tabulation

\"

Guillemet (double)

\\

Antislash (barre oblique inverse)

Une autre utilisation courante des séquences d'échappement est d'avoir des guillemets à l'intérieur des chaînes. Puisque les guillemets doubles indiquent le début et la fin des chaînes, vous devez les échapper avec un antislash.

System.out.println("She said \"Hello!\" to me.");

Le résultat est :

She said "Hello!" to me.

I-G - Formatage de code

Dans les programmes Java, certaines espaces sont requises. Par exemple, vous avez besoin d'au moins une espace entre les mots, donc ce programme n'est pas correct :

1. publicclassGoodbye{

2. publicstaticvoidmain(String[] args) {

3. System.out.print("Goodbye, ");

4. System.out.println("cruel world");

5. }

Mais la plupart des autres espaces sont facultatives. Par exemple, ce programme est correct :

1. public class Goodbye {

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

3. System.out.print("Goodbye, ");

4. System.out.println("cruel world");

5. }

6. }

Aller à la ligne est également facultatif. Donc nous pouvons écrire :

public class Goodbye { public static void main(String[] args) { System.out.print("Goodbye, "); System.out.println ("cruel world");}}

Cela fonctionne toujours, mais le programme devient de plus en plus difficile à lire. Les nouvelles lignes et les espaces sont importantes pour l'organisation visuelle de votre programme, ce qui facilite la compréhension du programme et permet de détecter les erreurs lorsqu'elles se produisent.

De nombreux éditeurs mettent automatiquement en forme le code source avec des indentations cohérentes et des sauts de ligne. Par exemple, dans DrJava, vous pouvez mettre le texte en retrait en sélectionnant tout le texte (Ctrl + A) et en appuyant sur la touche Tabulation.

Les organisations qui développent beaucoup de logiciels ont généralement des règles strictes sur la façon de formater le code source. Par exemple, Google publie ses normes de codage Java pour les projets Open Source : http://google.github.io/styleguide/javaguide.html.

Vous ne comprenez peut-être pas ces instructions pour l'instant, car elles font référence à des fonctionnalités du langage que nous n'avons pas encore vues. Mais vous voudrez peut-être y revenir périodiquement en lisant ce cours.

I-H - Débogage

Il est conseillé de lire ce cours devant un ordinateur pour pouvoir essayer les exemples au fur et à mesure. Vous pouvez exécuter de nombreux exemples directement dans le volet Interactions de DrJava (voir l'annexe A). Mais si vous mettez le code dans un fichier source, il sera plus facile d'essayer les variations.

Chaque fois que vous testez une nouvelle fonctionnalité, vous devriez également essayer de faire des erreurs. Par exemple, dans le programme « Hello World », que se passe-t-il si vous omettez l'un des guillemets ? Et si vous omettez les deux ? Que se passe-t-il si vous orthographiez mal println ? Des tests de ce genre vous aident à vous souvenir de ce que vous avez lu. Ils facilitent également le débogage, car vous apprenez ce que signifient les messages d'erreur. Il est préférable de faire des erreurs maintenant et intentionnellement plutôt que plus tard et accidentellement.

Le débogage est comparable à une science expérimentale : une fois que vous avez une idée de ce qui ne va pas, vous modifiez votre programme et réessayez. Si votre hypothèse était correcte, vous pouvez prédire le résultat de la modification et vous rapprocher d'un programme qui fonctionne. Si votre hypothèse était fausse, vous devez en trouver une nouvelle.

La programmation et le débogage doivent aller de pair. N'écrivez pas simplement un tas de code pour ensuite déboguer et tester à l'aveuglette afin d'éliminer les erreurs jusqu'à ce que tout fonctionne. Au lieu de cela, commencez par un programme qui fait quelque chose et faites de petites modifications, en les déboguant au fur et à mesure, jusqu'à ce que le programme fasse ce que vous voulez. De cette façon, vous aurez toujours un programme qui fonctionne, et il sera plus facile d'isoler les erreurs.

Un bon exemple de ce principe est le système d'exploitation Linux, qui contient des millions de lignes de code. Linux a commencé comme un programme simple que Linus Torvalds utilisait pour explorer la puce Intel 80386. Selon Larry Greenfield dans le Linux Users 'Guide, « l'un des projets antérieurs de Linus était un programme qui permutait entre l'impression AAAA et BBBB. Cela a évolué plus tard vers Linux. »

Enfin, la programmation apporte parfois des émotions fortes. Si vous êtes aux prises avec une situation difficile, vous pourriez vous sentir en colère, découragé ou vexé. Rappelez-vous que vous n'êtes pas seul et que la plupart des programmeurs, sinon tous, ont eu des expériences similaires. N'hésitez pas à contacter un ami et posez des questions !

I-I - Glossaire

Tout au long du cours, nous essayons de définir chaque terme la première fois que nous l'utilisons. À la fin de chaque chapitre, nous incluons les nouveaux termes et leurs définitions dans l'ordre d'apparition. Si vous passez du temps à apprendre ce vocabulaire, vous aurez plus de facilité à lire les chapitres suivants.

Résolution de problème :

le processus de formulation d'un problème, de trouver une solution et d'exprimer la solution.

Programme :
une séquence d'instructions qui spécifie comment effectuer des tâches sur un ordinateur.

La programmation :
l'application de la résolution de problèmes à la création de programmes informatiques exécutables.

L'informatique :
l'approche scientifique et pratique du calcul et de ses applications.

Algorithme :
une procédure ou une formule pour résoudre un problème, avec ou sans ordinateur.

Bogue :
une erreur dans un programme.

Débogage :
le processus de recherche et de suppression des erreurs

Langage de haut niveau :
Un langage de programmation conçu pour être facile à lire et à écrire pour les humains.

Langage de bas niveau :
un langage de programmation conçu pour être facile à utiliser pour un ordinateur. Aussi appelé « langage machine » ou « assembleur ».

Portabilité :
la capacité d'un programme à fonctionner sur plus d'un type d'ordinateur.

Interpréter:
exécuter un programme écrit dans un langage de haut niveau en le traduisant ligne par ligne et en exécutant immédiatement les instructions correspondantes.

Compiler :
traduire un programme écrit dans un langage de haut niveau en un langage de bas niveau, en vue d'une exécution ultérieure.

Code source :
un programme écrit dans un langage de haut niveau avant compilation.

Code objet :
programme à la sortie du compilateur, après la traduction.

Exécutable :

un autre nom pour le code objet qui est prêt à fonctionner sur un matériel spécifique.

Byte code :
un type spécial de code objet utilisé pour les programmes Java. Le byte code est similaire à un langage de bas niveau, mais il est portable comme un langage de haut niveau.

Instruction :
partie d'un programme qui spécifie une étape d'un algorithme.

Instruction d'impression :
une instruction qui entraîne un affichage à l'écran.

Méthode :
une séquence d'instructions nommée.

Classe :
pour l'instant, une collection de méthodes connexes. (Nous verrons plus tard que cette notion est plus riche que cela.)

Commentaire :
une partie d'un programme contenant des informations sur le programme mais n'ayant aucun effet sur l’exécution du programme.

Chaîne de caractères :
une séquence de caractères, le type primitif de données pour le texte.

Fin de ligne :
un caractère spécial signifiant la fin d'une ligne de texte. Également appelé saut de ligne ou retour à la ligne.

Séquence d'échappement :
une séquence de code représentant un caractère spécial lorsqu'il est utilisé dans une chaîne de caractères.

I-J - Exercice 1

Les informaticiens ont la fâcheuse habitude d'utiliser des mots communs pour signifier autre chose que leur sens commun.

- Qu'est-ce qu’un programme portable ?

- Que signifie le mot compilation ?

- Qu'est-ce qu'un exécutable ? Pourquoi l’appelle-t-on ainsi ?

Le glossaire à la fin de chaque chapitre est destiné à mettre en évidence les mots et les expressions ayant une signification particulière en informatique. Quand vous y voyez des mots familiers, ne supposez pas que vous savez ce qu'ils veulent dire !

I-K - Exercice 2

- Avant toute chose, découvrez comment compiler et exécuter un programme Java. Certains environnements fournissent des exemples de programmes similaires à l'exemple de la section 4.

- Saisissez le programme hello world, compilez-le et exécutez-le.
Ajoutez une instruction d'impression qui affiche un deuxième message après le "Hello World!". Mettez une phrase du style : "Comment allez-vous ?", compilez et exécutez le programme à nouveau.

- Ajoutez un commentaire au programme (n'importe où), recompilez et ré-exécutez-le. Le nouveau commentaire ne devrait pas affecter le résultat.

Cet exercice peut sembler trivial, mais c'est le point de départ de nombreux programmes avec lesquels nous travaillerons. Pour déboguer en toute confiance, vous devez avoir confiance en votre environnement de programmation.

I-L - Exercice 3

C'est une bonne idée de commettre des erreurs volontairement afin de voir les messages d'erreur du compilateur. Parfois, le compilateur vous dit exactement ce qui ne va pas, et tout ce que vous avez à faire est de le réparer. Mais parfois, les messages d'erreur sont trompeurs. Au fil du temps, vous saurez quoi faire exactement.

En commençant par le programme Hello World, essayez chacune des erreurs suivantes. Après avoir effectué chaque modification, compilez le programme, lisez le message d'erreur (s'il y en a un), puis corrigez l'erreur.

- Retirez l'une des accolades ouvrantes.

- Retirez l'une des accolades fermantes.

- Au lieu demain, écrivez mian.

- Supprimez le mot static.

- Supprimez le mot public.

- Supprimez le mot System.

- Remplacez println par Println.

- Remplacez println par print.

- Supprimez l'une des parenthèses. Ajoutez-en une autre.

II - Variables et opérateurs

Ce chapitre décrit comment écrire des traitements en utilisant des variables, permettant de stocker des valeurs telles des nombres ou des mots et des opérateurs, représentant des symboles pour effectuer des calculs. Nous expliquerons également trois types d’erreurs de programmation puis nous vous donnerons des conseils additionnels pour les opérations de débogage des programmes.

II-A - Déclaration des variables

L’une des fonctionnalités les plus puissantes d'un langage de programmation est la possibilité de manipuler des variables. Une variable est un nom qui fait référence à une valeur. Les valeurs peuvent être des nombres, du texte, des images, du son, voire d’autres types de données. Afin de stocker une valeur, vous devez au préalable déclarer une variable.

String message;

Ce traitement est une déclaration, parce qu’il déclare que la variable nommée message est de type String. Chaque variable a un type déterminant quel genre de valeurs elle peut stocker. Par exemple, le type int permet de stocker des entiers, et le type char quant à lui permet de stocker des caractères.

Certains types débutent avec une majuscule et certains avec des minuscules. Nous apprendrons la signification de cette distinction plus loin dans cet ouvrage, mais pour l’instant, vous devez juste faire attention à cela. Sachez toutefois qu’il n’y a pas de type Int ni de type string.

Pour déclarer une variable de type entier nommée x, vous saisissez tout simplement

int x;

Notez que x est un nom arbitrairement donné à cette variable. Les programmeurs choisissent généralement des noms significatifs pour leurs variables, qui expliquent à quoi sert la variable. Par exemple, en voyant la déclaration de ces variables, vous pouvez probablement en déduire les valeurs qu’elles devraient stocker :

1. String firstName;

2. String lastName;

3. int hour, minute;

Cet exemple déclare deux variables avec le type String et deux avec le type int. Quand le nom d’une variable contient plus d’un mot, comme firstName, il est d’usage dans la convention des langages de programmation de mettre en majuscule la première lettre de chaque mot hormis la première.

Le nom des variables est sensible à la casse (minuscule ou majuscule) des lettres, donc firstName est différent de firstname ou de FirstName.

Cet exemple démontre également la syntaxe pour effectuer une déclaration multiple de variables du même type sur la même ligne : hour et minute sont toutes deux de type entier. Notez que chaque traitement de déclaration se termine par un point-virgule.

Vous pouvez utiliser n’importe quel nom pour une variable comme bon vous semble. Mais vous devez toutefois savoir que cinquante mots sont réservés par le langage Java, ils sont appelés mots-clefs ou mots réservés, vous n’êtes pas autorisé à les utiliser en tant que noms de variables.

Ces mots incluent public, static, void, et int, qui sont utilisés par le compilateur pour analyser la structure du programme.

Vous pouvez trouver la liste complète des mots-clefs à l’adresse suivante http://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html, mais rassurez-vous, vous n’aurez pas à les retenir. La plupart des éditeurs de code possèdent « le surlignage syntaxique » faisant apparaître certaines portions de votre code, et notamment les mots réservés, d’une couleur différente.

II-B - Assignation

Maintenant que nous avons déclaré nos variables, nous voulons les utiliser pour stocker des valeurs. Nous effectuons cette opération avec un traitement d’assignation.

1. message = "Hello!";        // donner à  message la valeur "Hello!"

2. hour = 11;                          // assigner la valeur 11 à hour

3. minute = 59;                   // initialiser minute à 59

Cet exemple nous montre trois assignations, et les commentaires associés illustrent les différentes façons dont les gens parlent parfois des déclarations d'affectation.

Le vocabulaire peut paraître confus à cet instant, mais le concept est simple :

- lorsque vous déclarez une variable, vous créez un espace de stockage nommé pour cette variable ;

- tandis que lorsque vous faites une assignation, vous mettez à jour la valeur de la variable.

En règle générale, une variable doit avoir le même type que la valeur à laquelle vous l’assignez. Par exemple, message peut contenir la chaîne de caractères "123", qui est composée des caractères '1', '2', et '3'. Ce qui est différent de l’entier 123.

message = "123";             // correct
message = 123;                // incorrect

Les variables doivent impérativement être initialisées (assignation d’une valeur) avant leur première utilisation. Vous pouvez déclarer une variable puis lui assigner une valeur par la suite, comme dans l’exemple précédent. Vous pouvez aussi effectuer l’opération de déclaration et d’affectation sur la même ligne.

1. String message = "Hello!";

2. int hour = 11;

3. int minute = 59;

II-C - Diagramme d’état

Puisque le langage Java utilise le symbole = pour l’affectation, nous pourrions penser que le traitement a = b est interprété comme une égalité. Or ce n’est pas le cas !

L’égalité est commutative, alors que l’assignation ne l’est pas. Par exemple, dans le monde des mathématiques si a = 7 alors 7 = a.

Tandis que dans le monde Java a = 7 ; est une instruction d’assignation tout à fait correcte ; mais 7 = a ne l’est pas. L’opérande de gauche du traitement d’assignation doit absolument être le nom d’une variable (espace de stockage en mémoire).

Par ailleurs en mathématique, un traitement d’égalité est toujours vrai quoi qu’il en soit. Si a = b maintenant, a sera toujours égal à b. Tandis qu’en Java, un traitement d’assignation peut rendre deux variables égales à un point de l’exécution, mais elles n’ont pas à demeurer égales tout au long du programme.

int a = 5;
int b = a;            // a et b sont maintenant égales
a = 3;                   // a et b ne sont désormais plus égales

La troisième ligne modifie la valeur de a, mais ne modifie en aucun cas la valeur de b, donc les deux variables a et b ne sont désormais plus égales.

Prises ensemble, les variables d’un programme et leur valeur actuelle nous donnent l’état du programme. La figure 2.1 montre l’état du programme à l’issue des traitements d’assignation en cours.

Diagramme d'état des variables a et b.

Des diagrammes comme celui-ci montrant l’état du programme sont appelés des diagrammes d’état. Chaque variable est représentée dans une case affichant le nom de la variable dans le programme ainsi que sa valeur stockée en mémoire. Lors de l’exécution du programme, l’état change, donc vous devez garder à l’esprit que le diagramme d’état n’est qu’une image à l’instant t du programme et non son état définitif.

II-D - Affichage des variables

Vous pouvez afficher la valeur d’une variable en utilisant l’instruction print ou println. Les traitements suivants déclarent une variable nommée firstLine, lui assignent la valeur "Hello, again!", et affichent cette valeur.

String firstLine = "Hello, again !";
System.out.println(firstLine);

Lorsque nous discutons d’affichage d’une variable, nous voulons dire par là la valeur de la variable et non son nom. Pour afficher le nom de la variable , nous devons placer son nom entre guillemets.

System.out.print("La valeur de firstLine est ");
System.out.println(firstLine);

Pour cet exemple nous avons l’affichage suivant :

La valeur de firstLine est Hello, again!

Comme par hasard, la syntaxe pour afficher une variable est totalement indépendante de son type. Par exemple :

1. int hour = 11;

2. int minute = 59;

3. System.out.print("L’heure courante est ");

4. System.out.print(hour);

5. System.out.print(" :");

6. System.out.print(minute);

7. System.out.println(".");

La sortie de ce programme est la suivante :

L’heure courante est 11 : 59.

Pour afficher plusieurs valeurs sur une seule et même ligne, il est d’usage d’utiliser plusieurs instructions print suivies d’une instruction println à la fin. Mais n’oubliez pas le println !

Sur certains systèmes, la sortie de l’instruction print est stockée sans pour autant être affichée tant que l’instruction println n’est pas saisie explicitement ; une fois l’instruction println saisie l’intégralité de la ligne est affichée.

Si vous omettez l’instruction println, il se peut que l’affichage du programme survienne à un moment inattendu, voire qu’il n’y ait pas d’affichage alors que le programme s’est terminé correctement.

II-E - Opérateurs arithmétiques

Les opérateurs sont des symboles représentant de simples opérations. Par exemple, l’opérateur pour l’addition est +, la soustraction -, la multiplication * et la division est /.

Le programme suivant convertit un temps donné en heures et minutes en minutes :

int hour = 11;
int minute = 59;
System.out.print("Nombre de minutes depuis minuit : ");
System.out.println(hour * 60 + minute) ;

Dans ce programme, hour * 60 + minute est une expression représentant une simple valeur à calculer. Quand le programme s’exécute, chaque variable est remplacée par sa valeur courante, puis les opérations sont appliquées. Les valeurs des opérateurs travaillent sur les opérandes appelés.

Le résultat de l’exemple précédent est le suivant :

Le nombre de minutes depuis minuit : 719

Les expressions sont généralement la combinaison de nombres, de variables et d’opérateurs. Lors de la phase de compilation puis de l’exécution, elles deviennent une simple valeur.

Par exemple, l’expression 1 + 1 a pour valeur 2. Dans l’expression hour – 1, Java remplace la variable par sa valeur associée rendant ainsi 11 – 1, qui a pour valeur 10. Dans l’expression hour * 60 + minute, les deux variables sont remplacées par leurs valeurs respectives rendant derechef 11 * 60 + 59. La multiplication s’effectue en premier, rendant 660 + 59. Puis l’addition renvoie 719.

L’addition, la soustraction et la multiplication font chacune ce que l’on attend d’elles, mais vous risquez d’être surpris par la division. Par exemple, l’extrait de code suivant essaye de calculer la fraction d’une heure qui s’est écoulée :

System.out.print("Fraction d’une heure écoulée : ");
System.out.println(minute / 60);

La sortie restituée est la suivante :

Fraction d’une heure écoulée : 0

Ce résultat peut paraître confus pour certaines personnes. La valeur de minute est 59, et 59 divisés par 60 devrait valoir 0,98333 et non pas 0. Le « problème » vient du fait que Java effectue une division euclidienne (sur des types entiers) lorsque les opérandes sont des entiers.

Intentionnellement, la division des nombres entiers est toujours arrondie à l’entier inférieur le plus proche, même dans des cas comme celui-ci où le résultat réel est plus proche d’un entier supérieur (ce n’est pas un arrondi automatique, mais une suppression de la partie décimale).

Comme autre possibilité, nous pouvons calculer un pourcentage plutôt qu’une fraction :

System.out.print("Pourcentage de l’heure écoulée : ");
System.out.println(minute * 100 / 60);

La nouvelle sortie est la suivante :

Pourcentage de l’heure écoulée :  98

De nouveau le résultat est arrondi à la baisse, mais au moins maintenant il est approximativement correct.

II-F - Nombres à virgule flottante

Une solution plus générale consiste en l’utilisation des nombres à virgule flottante, qui peuvent représenter aussi bien des fractions (nombres réels) que des nombres entiers. En Java, le type par défaut pour les nombres flottants est appelé double, qui est le type court pour la double précision des nombres. Vous pouvez créer des variables de type double et leur assigner des valeurs en utilisant la même syntaxe utilisée que pour les autres types vus plus haut dans ce chapitre :

double pi;
pi = 3.14159;

Java effectue la « division des nombres à virgule flottante » quand un ou plusieurs opérandes sont des valeurs de type double. Ainsi nous pouvons résoudre le problème évoqué dans la section précédente :

double minute = 59.0;
System.out.print("Fraction d’une heure écoulée : ");
System.out.println(minute / 60.0);

La sortie est :

Fraction d’une heure écoulée : 0.9833333333333333

Bien que les nombres à virgule flottante soient particulièrement utiles, ils peuvent devenir une source de confusion.

Par exemple, Java fait la distinction entre le nombre entier 1 et le nombre à virgule flottante 1,0. Bien qu’ils semblent représenter le même nombre, en réalité ils ne sont pas identiques.

Ce qu’il faut bien retenir c’est qu’ils appartiennent tous deux à des types de données différents, en d’autres termes, vous n’êtes pas autorisé à effectuer des comparaisons entre ces deux types distincts. L’expression suivante est illégale puisque la variable de gauche est un type entier alors que la valeur assignée à cette variable est un double :

int x = 1.1;       // erreur de compilation

Il est facile d’oublier cette règle, car dans certains cas Java convertit automatiquement un type vers un autre :

double y = 1;       // correct, mais mauvais style de programmation

L’exemple précédent devrait être illégal, mais Java permet cette écriture en convertissant la valeur 1 de type int en la valeur 1.0 de type double automatiquement.

Cette indulgence syntaxique est pratique, mais elle est souvent cause de problèmes pour les débutants en programmation. Par exemple :

double y = 1 / 3;       // erreur courante

Il se peut que vous vous attendiez à ce que la variable y contienne la valeur 0,333333, qui correspond à la valeur d’un flottant. Mais au lieu de cela, la valeur est 0,0.

L’expression de droite divise deux entiers, donc Java effectue une division entière, qui renvoie donc la valeur de type int 0. Convertie en double, la valeur assignée à y est 0,0. Une méthode pour résoudre ce problème (une fois détecté comme n’étant pas un bug) est de transformer l’opérande de droite en flottant. Cette modification aura pour effet d’initialiser y à 0,333333, comme attendu :

double y = 1.0 / 3.0;       // correct

En matière de style de programmation, vous devriez toujours assigner une valeur flottante à une variable à virgule flottante. Le compilateur ne vous demandera pas de le faire, mais vous devez être attentif aux différents types que vous manipulez. En adoptant un mauvais style, vous favorisez beaucoup l’apparition de bugs pas forcément évidents.

II-G - Erreurs d’arrondis

La plupart des nombres à virgule flottante ne sont qu’approximativement corrects. Certains nombres semblent être des décimaux bien définis dotés d’une précision infinie, cependant, en utilisant des variables contenant des fractions, comme 1.0/3.0 et des nombres irrationnels, comme π, nous nous rendons compte qu’ils ne le sont pas vraiment.

La différence entre le nombre que nous voulons réellement obtenir et le nombre que nous obtenons est appelée une erreur d’arrondi. Par exemple, les deux traitements suivants devraient être équivalents :

System.out.println(0.1 * 10);
System.out.println(0.1 + 0.1 + 0.1 + 0.1 + 0.1
+ 0.1 + 0.1 + 0.1 + 0.1 + 0.1);

Mais sur certaines stations de travail, la sortie est :

1.0
0.9999999999999999

Le problème est que la valeur 0,1 est une fraction décimale terminale, en quelque sorte une fraction répétitive en nombre binaire. Ce qui entraîne que cette représentation de ce nombre flottant n’est qu’approximative.

Lorsque nous ajoutons des approximations dans nos calculs, les erreurs d’arrondi s’accumulent. Dans plusieurs types d’applications, telles les applications utilisant des calculs graphiques, de la cryptologie, des analyses statistiques ou du rendu multimédia, l’arithmétique avec les flottants a le bénéfice d’alléger les coûts. Cependant, si vous avez besoin d’une absolue précision, nous vous recommandons vivement d’utiliser des entiers. Par exemple, considérons un compte bancaire ayant le solde de 123,45€ :

double balance = 123.45        // erreur potentielle d’arrondi

Dans cet exemple, au fur et à mesure des opérations arithmétiques de retraits et de versements ,la valeur de la variable balance deviendra inexacte. Le résultat risque d’énerver les clients et d’entraîner des poursuites judiciaires à l’encontre de la banque. Vous pouvez toutefois éviter cet inconvénient en typant la variable balance en un nombre entier.

int balance = 12345        // totaliser le nombre en centimes

Cette solution fonctionne tant que le nombre totalisé n’excède pas la taille limite d’un nombre de type entier, c’est-à-dire proche des 2 milliards.

II-H - Opérateurs pour les chaînes de caractères

En règle générale, vous ne pouvez pas effectuer d’opérations mathématiques sur les chaînes de caractères, et ce même si la chaîne ressemble à un nombre. Les expressions suivantes sont illégales :

"Hello" -1              "World" / 123               "Hello" * "World"

L’opérateur + fonctionne avec les chaînes de caractères, toutefois le résultat renvoyé n’est pas toujours celui espéré. Pour les chaînes de caractères, l’opérateur + permet la concaténation, opération qui permet la jointure bout à bout des chaînes de caractères concernées. Ainsi :

"Hello, " + "World !" renvoie la chaîne de caractère  "Hello, World !".

Ou, si vous avez une variable nommée name de type String, l’expression "Hello, " + name attache la valeur de la variable name à la chaîne de caractères hello, qui crée par la suite une chaîne d’invite personnalisée. Dès lors qu’une opération d’addition est définie entre des nombres et une chaîne de caractères, Java effectue automatiquement une conversion à laquelle vous ne vous attendiez probablement pas :

1. System.out.println(1 + 2 + "Hello" );

2. // la sortie est 3Hello

3. System.out.println("Hello"+ 1 + 2) ;

4. // la sortie est Hello12

Java exécute les opérations de la gauche vers la droite. La première ligne, 1 + 2 donne 3, et 3 + "Hello" donne "3Hello".

Quand plus d’un opérateur apparaît dans une expression, chacun est évalué selon l’ordre de précédence des opérateurs. En général, Java évalue les opérateurs de la gauche vers la droite (comme nous l’avons vu dans la section précédente). Mais dès lors qu’il s’agit des opérateurs arithmétiques, Java suit les règles des conventions mathématiques :

- la multiplication et la division sont prioritaires sur l’addition et la soustraction, ce qui signifie que la multiplication et la division sont effectuées en premier puis viennent l’addition et la soustraction. Donc 1 + 2 * 3 renvoie 7 et non 9, et 2 + 4 / 2 renvoie 4 et non 3 ;

- si les opérateurs ont la même précédence, ils sont évalués de la gauche vers la droite. Ainsi dans l’expression minute * 100 / 60, la multiplication s’effectue en premier ; si la valeur de minute vaut 59, nous obtenons 5900 / 60, qui nous renvoie 98. Si ces mêmes opérations étaient effectuées de la droite vers la gauche, le résultat aurait été 59 * 1, qui est incorrect ;

- si toutefois vous voulez modifier l’ordre de précédence des opérateurs (ou si vous n’êtes pas sûr de ce qu’il en ressort) vous pouvez utiliser des parenthèses. Dès lors, les expressions mises entre parenthèses sont évaluées en premier, ainsi (1 + 2) * 3 donne 9. Vous pouvez également utiliser des parenthèses pour rendre une expression plus facile à lire, comme dans l’expression (minute * 100) / 60, même si cela ne changera pas le résultat final.

Ne vous cassez pas la tête à mémoriser l’ordre de précédence des opérateurs, notamment pour les autres opérateurs non listés ici. Si vous avez un doute sur le résultat en regardant une expression, utilisez les parenthèses pour rendre les choses plus claires.

II-I - Composition

Nous avons vu les différents éléments du langage de programmation – les variables, les expressions et les traitements – de façon isolée, sans se soucier de leur imbrication. L’une des fonctionnalités les plus utilisées des langages de programmation et qui correspondent à leur aptitude primordiale est de construire des petits blocs et de les associer. Par exemple, nous savons comment multiplier des nombres et nous savons comment afficher ces valeurs. Nous pouvons dès lors combiner ces opérations dans un unique traitement :

System.out.println(17 * 3);

N’importe quelle opération arithmétique peut être utilisée à l’intérieur d’une instruction print. Nous avons déjà vu un exemple comme celui-ci :

System.out.println(hour * 60 + minute);

Vous pouvez aussi placer arbitrairement des expressions dans l’opérande de droite de l’assignation :

int percentage;
percentage = (minute * 100) / 60 ;

L’opérande de gauche de l’assignation doit être un nom de variable et non une expression. Tout simplement par le fait que l’opérande de gauche décrit où sera stocké le résultat de l’expression, et bien entendu une expression ne peut pas représenter un espace de stockage en mémoire.

hour = minute + 1;       // correct
minute + 1 = hour;       // incorrect → erreur de compilation

L’aptitude à composer des expressions ne vous semble peut-être pas impressionnante pour l’instant, mais nous verrons par la suite des exemples plus complexes de calcul nous autorisant à écrire des expressions proprement et de façon concise.

Toutefois, ne vous laissez pas emporter. Les expressions complexes et volumineuses peuvent être difficiles à lire et à déboguer.

II-J - Types d’erreurs rencontrés

Trois types d’erreurs peuvent apparaître dans un programme : des erreurs survenant lors de la compilation du programme, lors de l’exécution du programme ou tout simplement des erreurs de logique dans l’écriture du programme. Il est primordial de les distinguer afin de les traquer rapidement. Les erreurs lors de la compilation apparaissent lorsque vous ne respectez pas les règles syntaxiques du langage Java. Par exemple, les parenthèses et les accolades vont de pair. Ainsi (1 + 2) est légal, mais 8) ne l’est pas. Dans le dernier cas, le programme ne compileras pas, et le compilateur affichera une erreur. Les messages d’erreur du compilateur indiquent d’habitude où dans le programme l’erreur est apparue, et parfois, peuvent même vous indiquer exactement quelle est la cause de l’erreur. Comme exemple, retournons dans le programme hello world de la Section 1.4

1. public class  Hello {

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

4. // génère une simple sortie

5. System.out.println("Hello World !") ;

6. }

7. }

Si vous oubliez le point-virgule à la fin de l’instruction print, il se peut que vous ayez un message d’erreur comme celui-ci :

File: Hello.java [line: 5]
Error:';'
expected

Ce qui est une bonne chose en soi : la localisation de l’erreur est correcte, et le message d’erreur vous dit ce qui ne va pas. Mais sachez toutefois que tous les messages d’erreur ne sont pas aussi faciles à comprendre. Parfois le compilateur rapporte l’endroit dans le programme où l’erreur a été détectée, mais pas l’erreur en elle-même où elle est présente dans le programme. Et parfois même la description du problème est plus confuse que toute autre chose.

Par exemple, si vous laissez une accolade ouverte à la fin du main (ligne 6), vous obtiendrez probablement le message d’erreur suivant :

File: Hello.java [line: 7]
Error: reached end of file while parsing

Il y a deux problèmes ici. Premièrement, le message d’erreur est écrit du point de vue du compilateur et non du vôtre. Parsing représente le processus de lecture d’un programme avant la traduction par le compilateur ; si le compilateur obtient l’indicateur de fin du fichier alors qu’il est encore en train de parser le fichier c’est qu’il y a un problème quelque part et que quelque chose a été omis. Mais le compilateur ne sait pas de quoi il s’agit. Et surtout ne sait pas où cela se situe dans le programme. Le compilateur découvre l’erreur à la fin du programme (ligne 7), mais l’accolade manquante devrait être sur la ligne précédente.

Les messages d’erreur contiennent des informations utiles, donc vous devriez faire un effort pour les lire et essayer de les comprendre. Mais ne les prenez pas toujours au pied de la lettre. Durant les quelques premières semaines de votre carrière de programmeurs, vous passerez probablement beaucoup de temps à traquer les erreurs de compilation. Cependant, lorsque vous gagnerez de l’expérience, vous ferez moins d’erreurs et les trouverez plus rapidement qu’à vos débuts.

Le second type d’erreur est constitué par les erreurs d’exécution du programme, appelées ainsi parce qu’elles ne peuvent apparaître que lorsque le programme est en cours d’exécution. En Java, ces erreurs apparaissent lorsque l’interpréteur est en train d’exécuter du byte code et que quelque chose s’est mal déroulé. Ces erreurs sont également appelées des « exceptions » parce qu’elles indiquent d’habitude que quelque chose d’exceptionnel (et de « mal ») est arrivé.

Les erreurs d’exécution sont rares dans des programmes simples vous le verrez dans les tout premiers chapitres de ce livre, donc il est possible qu’il s’écoule un peu de temps avant que vous en trouviez une. Quand une erreur d’exécution apparaît, l’interpréteur affiche un message d’erreur expliquant ce qui arrive et où cela arrive. Par exemple, si vous effectuez accidentellement une division par zéro, vous aurez probablement un message d’erreur tel que celui-ci :

Exception in thread "main" java.lang.ArithmeticException: / by zero
at Hello.main(Hello.java:5)

Le débogage s’avère dès lors facile à effectuer. La première ligne reprend le nom de l’exception, java.lang.ArithmeticException, et un message indiquant précisément ce qui est arrivé, / by zero. La ligne suivante montre la méthode où l’erreur est survenue ; Hello.main indique la méthode main dans la classe Hello. Le fichier où la méthode est définie, Hello.java est également mentionné ainsi que le numéro de la ligne où l’erreur est apparue, ici la ligne 5.

Les messages d’erreur contiennent parfois des informations additionnelles qui n’ont pas encore de sens pour vous. Ainsi, l’un des défis réside dans la capacité à savoir où trouver l’information utile sans être submergé par le superflu. Gardez donc toujours à l’esprit que la ligne où le programme « crash » n’est pas forcément la ligne qui nécessite une correction.

Le troisième type d’erreur est constitué par les erreurs de logique dans l’élaboration du code de votre programme. Si votre code contient une erreur de logique, le programme se compilera et s’exécutera sans générer aucun message d’erreur, mais il ne fera pas ce que vous espériez qu’il fasse lors de sa conception. Au lieu de cela, il fera exactement ce que vous lui avez dit de faire stricto sensu. Par exemple, voici une version du programme hello world avec une erreur de logique :

1. public class  Hello {

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

4. System.out.println("Hello, ") ;

5. System.out.println("World !") ;

6. }

7. }

Ce programme compilera et s’exécutera comme il le faut, mais la sortie affichera ceci :

Hello,
World !

En partant sur le principe que nous aurions souhaité avoir l’affichage sur une seule et même ligne, le résultat est donc incorrect. Le problème réside dans le fait que la première ligne utilise println, alors que nous aurions dû utiliser print (voir l’exemple du programme « goodbye world » de la Section 1.5). L’identification des sources d’erreurs est extrêmement compliquée à faire parce que vous devez revenir en arrière dans la conception de votre code, explorer la sortie de votre programme, essayer de trouver pourquoi quelque chose d’« inattendu » apparaît alors que tout vous semble correct, et surtout trouver comment faire pour que tout fonctionne comme vous le souhaitez. Dans ce type de situation, l’interpréteur et le compilateur ne vous seront d’aucun soutien, tout simplement parce qu’ils ne savent pas de quoi il en ressort et quel est le résultat attendu.

Maintenant que vous connaissez ces trois types d’erreurs, vous voudrez probablement lire l’ Annexe C, où nous avons collecté quelques-uns de nos conseils préférés pour le débogage. Il fait référence aux fonctionnalités du langage que nous n’avons pas évoqué jusque là, donc vous aurez probablement à les relire de temps en temps.

II-K - Vocabulaire

variable : espace de stockage nommé pour contenir des valeurs. Toutes les variables sont typées, le type est défini dès la déclaration de la variable.

valeur : nombre, une chaîne de caractères ou un autre type de données pouvant être stocké dans une variable. Toutes, absolument toutes les valeurs appartiennent à un type (par exemple, int ou String).

déclaration : traitement créant une nouvelle variable et spécifiant son type.

type : du point de vue mathématique, il s’agit d’un ensemble de valeurs. Le type d’une variable détermine quelles sont les valeurs qu’il peut accepter.

syntaxe : disposition des mots et des symboles propres au langage.

mot-clef : mot réservé utilisé par le compilateur pour analyser les programmes. Vous ne pouvez pas utiliser des mots-clefs (comme public, class, et void) comme noms de variables.

assignation : instruction donnant une valeur à une variable.

initialiser : assigner une variable pour la première fois.

état : variables dans le programme et leur valeur courante.

diagramme d’état : représentation graphique de l’état d’un programme à un moment donné.

opérateur : symbole représentant un calcul arithmétique comme une addition, une multiplication ou la concaténation d’une chaîne de caractères.

opérande : une des valeurs sur lesquelles porte un opérateur. La plupart des opérateurs en Java nécessitent deux opérandes.

expression : combinaison de variables, opérateurs et valeurs représentant une valeur unique. Les expressions ont également des types, déterminés par leurs opérateurs et leurs opérandes.

nombre à virgule flottante : type de données représentant des nombres avec une partie entière et une partie fractionnaire. En Java, double est le type par défaut pour les nombres à virgule flottante.

erreur d’arrondi : différence entre le nombre que nous voulons représenter et le nombre flottant le plus proche.

concaténer : opération permettant de joindre bout à bout deux valeurs, le plus souvent des chaînes de caractères.

ordre des opérations : règles déterminant dans quel ordre les opérations sont évaluées.

composition : aptitude de combiner des expressions simples et des traitements à l’intérieur d’un traitement et d’une expression composée.

erreur de compilation : erreur survenant dans le code source rendant impossible la compilation du programme. Aussi connue sous le nom d’erreur de syntaxe.

parser : opération permettant d’analyser la structure d’un programme ; ce que le compilateur doit faire en premier.

erreur d’exécution : erreur survenant dans le programme rendant impossible l’exécution complète de ce dernier. Aussi connue sous le nom d’ « exception».

erreur de logique : Une erreur survenant dans le programme entraînant un comportement différent du programme de ce qui est attendu par le programmeur.

II-L - Exercices

Le code source de ce chapitre est présent dans le répertoire ch02 de ThinkJavaCode.

Référez-vous à la page xv concernant les instructions pour le téléchargement du répertoire. Avant de commencer les exercices, nous vous recommandons de compiler et d’exécuter les différents exemples.

Si vous n’avez pas encore lu l’Annexe A.2, ce serait une bonne chose de le faire maintenant. Cette annexe décrit le panneau des interactions de l’éditeur DrJava, qui représente un bon compromis pour tester de courts fragments de code sans pour autant écrire une définition complète de classe.

Exercice 2.1. Si vous utilisez ce livre dans une classe d’école, vous serez ravi de faire cet exercice. Trouvez un partenaire et jouez à « Stump the Chump » :

Commencez le programme en le compilant et en l’exécutant sans erreur. L’un d’entre vous s’éloigne alors que l’autre ajoute une erreur dans le programme. Celui qui s’est éloigné tente de trouver la cause du problème puis de corriger cette dernière. Vous gagnez deux points si vous trouvez la cause du problème sans passer par la phase de compilation, un point si vous trouvez la cause à l’aide du compilateur, tandis que votre collègue gagne un point si vous ne trouvez pas la cause de l’erreur.

Exercice 2.2. Le but de cet exercice est (1) d’utiliser la concaténation des chaînes de caractères pour afficher des valeurs de différents types (int et String) et à la fois (2) de pratiquer le développement de programmes en ajoutant progressivement quelques traitements.

1. Créez un nouveau programme que vous appellerez Date.java. Copiez ou tapez quelque chose comme le programme hello world et assurez-vous que vous pouvez compiler et exécuter ce programme.

2. Suivant l’exemple de la Section 2.4, écrivez un programme qui crée une variable nommée jour, date, mois et annee. La variable jour contiendra le jour de la semaine (exemple vendredi), et la date contiendra le jour du mois (exemple le 13). Quel est le type de chaque variable ? Assignez des valeurs à ces variables représentant la date d’aujourd’hui.

3. Affichez (sur l’écran) la valeur de chaque variable. Chaque variable doit être affichée toute seule sur une ligne. Il s’agit d’une étape intermédiaire pour vérifier que tout fonctionne correctement jusque là. Compilez et exécutez votre programme avant d’aller de l’avant.

4. Modifiez le programme afin qu’il affiche la date au format standard américain, par exemple : Jeudi, juillet 26, 2018.

5. Modifiez le programme afin qu’il affiche la date au format standard européen. La sortie finale devrait ressembler à ceci :

American format:
jeudi, juillet 26, 2018
European format:
jeudi 26 juillet 2018

Exercice 2.3. Le but de cet exercice est (1) d’utiliser des opérateurs arithmétiques, et (2) de commencer à réfléchir sur l’utilisation des entités composées (comme l’heure du jour) qui consistent à associer de multiples valeurs.

1. Créez un nouveau programme que vous appellerez Time.java. À partir de maintenant vous devrez démarrer d’un programme vide (plus aucun copier-coller d’un autre programme existant).

2. En suivant l’exemple de du programme de la Section 2.4, créez des variables nommées heure, minute et seconde. Assignez des valeurs en « dur » représentant l’heure actuelle. Utilisez une convention horaire 24h telle que 2 heures de l’après-midi représente la valeur 14 pour la variable heure.

3. Faites en sorte que le programme puisse calculer et afficher le nombre de secondes depuis minuit.

4. Calculez le nombre de secondes restantes jusqu’à la fin de la journée.

5. Calculez et affichez le pourcentage de temps écoulé depuis le début de la journée. Il se peut que vous ayez des problèmes lors du calcul des pourcentages avec des nombres entiers, donc, il est préférable que vous utilisiez des nombres flottants.

6. Modifiez les valeurs d’heure, minute et seconde pour refléter l’heure actuelle. Puis écrivez le code permettant de calculer le temps écoulé depuis que vous avez commencé à travailler sur cet exercice.

Conseil : il se peut que vous ayez besoin d’utiliser des variables additionnelles pour effectuer vos calculs. Ces variables utilisées pour les calculs, mais qui ne sont jamais affichées sont appelées des variables « intermédiaires » ou « temporaires ».

III - Entrées et sorties conversationnelles

Les programmes que nous avons vus jusqu’à maintenant n’affichaient que de simples messages sur la sortie standard (l’écran), ne nécessitant pas beaucoup de vrais calculs. Par contre, dans ce chapitre, nous vous montrerons comment lire des informations saisies depuis l’entrée standard (le clavier) pour calculer un résultat, puis formater ce résultat pour l’affichage sur la sortie standard.

III-A - La classe System

Nous avons déjà utilisé par le passé System.out.println, mais il se peut que vous ne vous soyez pas penché sur la signification de cette instruction. System est une classe fournissant des méthodes relatives au « system » ou en d’autres termes à l’environnement dans lequel le programme s’exécute. Elle fournit également System.out, qui est un objet spécial fournissant une méthode pour l’affichage sur la sortie standard et incluant println.

En fait, nous pouvons utiliser System.out.println pour afficher la valeur de System.out :

System.out.println( System.out);

Le résultat est :

java.io.PrintStream@685d72cd

Cette sortie indique que System.out est un PrintStream, qui est défini dans un paquetage appelé java.io. Un paquetage est une collection de classes apparentées ; java.io contient des classes pour les « E/S » qui correspondent aux entrées et sorties standards.

Les nombres et les lettres suivant le symbole @ représentent l’adresse de System.out, représentée comme un nombre en hexadécimal (base 16). L’adresse d’une valeur est sa localisation dans la mémoire de l’ordinateur, qui peut être différente d’un ordinateur à un autre. Dans cet exemple l’adresse est 685d72cd, mais si vous exécutez le même code une autre fois, il se peut que vous ayez un résultat différent.

Comme le montre la Figure 3.1, la classe System est définie dans un fichier s’appelant System.java, et PrintStream est définie dans PrintStream.java. Ces fichiers font partie de la bibliothèque interne de Java, qui représente une immense collection de classes que vous pouvez utiliser dans vos programmes.

Figure 3.1 : System.out.println se réfère à la variable out de la classe System, qui est de type PrintStream et qui fournit la méthode println.

III-B - La classe Scanner

La classe System fournit également l’objet spécial System.in, qui est de type InputStream et qui fournit les méthodes de lecture des informations depuis le clavier.

Ces méthodes ne sont pas conviviales à utiliser ; heureusement, Java nous fournit d’autres classes facilitant l’utilisation de la gestion des tâches communes pour les saisies utilisateurs.

Par exemple, Scanner est une classe contenant des méthodes pour traiter en entrée des mots, des nombres ou d’autres types de données. La classe Scanner est présente dans le paquetage java.util, qui est un paquetage contenant des classes très utilisées appelées « classes utilitaires ». Avant de pouvoir utiliser la classe Scanner, vous devez importer le paquetage contenant cette classe comme ceci :

import java.util.Scanner;

Ce traitement d’import stipule au compilateur que lorsque vous dites Scanner, cela signifie que vous faites référence à la classe définie dans le paquetage java.util.

Cette précision est nécessaire parce qu’il se peut qu’il y ait une autre classe nommée Scanner dans un autre paquetage. Utiliser un traitement d’import vous permet de rendre votre code non ambigu.

Les traitements d’import ne peuvent pas être à l’intérieur de la définition d’une classe.

Par convention, ils se trouvent au tout début du fichier.

Une fois que vous avez créé un Scanner :

Scanner in = new Scanner(System.in);

Cette ligne déclare une variable de type Scanner nommée in et crée un nouveau Scanner qui prend son entrée de System.in.

La classe Scanner possède une méthode appelée nextLine qui lit une ligne en entrée depuis le clavier et renvoie une String. L’exemple suivant lit deux lignes et les répète en les renvoyant à l’utilisateur.

import java.util.Scanner;

public class Echo {
public static void main(String[] args) {
String line ;
Scanner in = new Scanner(System.in) ;

System.out.print("Ecrivez quelque chose : ");
line = in.nextLine();
System.out.println("Vous avez écrit : " + line);

System.out.print("Ecrivez autre chose : ");
line = in.nextLine();
System.out.println("Vous avez écrit aussi : " + line);
}
}

Si vous omettez de déclarer le traitement d’import de la classe Scanner et que vous invoquez cette classe plus bas dans votre code, vous aurez une erreur de compilation comme celle-ci : « cannot find symbol ». Ceci veut dire que le compilateur ne comprend pas ce que signifie le mot Scanner.

Il se peut que vous soyez surpris de pouvoir utiliser la classe System sans effectuer d’import. La classe System appartient au paquetage java.lang « fournissant les classes fondamentales pour la conception et l’élaboration de la programmation en Java ». La classe String fait également partie du paquetage java.lang.

III-C - Structure d’un programme

À ce stade, nous avons parcouru tous les éléments vous permettant de concevoir un programme Java.

La figure 3.2 nous montre l’unité organisationnelle d’un programme Java.

Figure 3.2 : les éléments d’un programme Java.

Pour résumer, un paquetage est une collection de classes, définissant des méthodes.

Les méthodes quant à elles contiennent des traitements, certaines d’entre elles contiennent des expressions. Les expressions sont conçues à partir d’éléments basiques d’un programme, éléments basiques incluant des nombres, des noms de variables, des opérateurs, des mots-clefs et des éléments de ponctuation comme des parenthèses, des accolades et des points-virgules.

L’édition standard du Java est fournie avec plusieurs centaines de classes que vous pouvez importer, qui peuvent être à la fois enthousiasmantes ou intimidantes. Vous pouvez parcourir cette bibliothèque à l’adresse http://docs.oracle.com/javase/8/docs/api/.

La plupart des bibliothèques Java sont écrites en Java.

Notez qu’il y a une différence majeure entre le langage Java, définissant la syntaxe et le sens des éléments de la figure 3.2 et la bibliothèque Java qui quant à elle fournit des classes prédéfinies.

III-D - Des pouces aux centimètres

Maintenant, voyons un exemple d’usage courant. Bien que la plupart des humains aient adopté le système métrique pour quantifier les poids et les mesures, certains pays utilisent toujours les unités anglo-saxonnes. Par exemple, lorsque je discute avec des amis résidant en Europe à propos de la météo, les Nord-américains doivent convertir les degrés Celsius en Fahrenheit et vice-versa pour se faire comprendre des Européens. Ils doivent de même convertir en centimètres une taille donnée en pouces.

Nous allons écrire un programme pour permettre cette conversion. Nous utiliserons un objet Scanner pour récupérer une mesure en pouces, la convertir en centimètres et enfin, afficher le résultat. Les lignes suivantes déclarent les variables et créent l’objet Scanner :

int inch ;
double cm ;
Scanner in = new Scanner(System.in);

L’étape suivante est de demander une saisie clavier à l’utilisateur. Nous utiliserons print au lieu de println pour que l’utilisateur puisse entrer les données sur la même ligne que la demande. Et nous utiliserons la méthode nextInt de la classe Scanner, méthode qui lit la saisie du clavier et la convertit en un entier.

System.out.print("Combien de pouces: ");
line = in.nextLine();

Ensuite, on multiplie le nombre de pouces par 2.54 – c’est le nombre de centimètres qu’il y a par pouce – et on affiche le résultat :

cm = inch * 2.54 ;
System.out.print(inch + " pouce =");
System.out.println(inch + " cm");

Ce code fonctionne correctement, mais il a un petit problème. Si un autre développeur lit ce code, il pourrait se demander d’où vient 2,54. Pour faciliter la tâche aux autres (et à vous même dans le futur), il serait mieux d’affecter cette valeur à une variable ayant un nom significatif. Nous démontrerons cela dans la section suivante.

III-E - Littéraux et constantes

Une valeur qui apparaît dans un programme, comme 2,54 (ou " pouce =" ), est appelée un littéral. En général, il n’y a rien de problématique avec les littéraux. Mais quand des nombres comme 2,54 apparaissent dans une expression sans aucune explication, ils rendent le code difficile à maintenir.

Les valeurs comme celles-ci sont parfois appelées nombres magiques (en insinuant qu’être « magique » n’est pas une bonne chose). Une bonne pratique est d’affecter ces nombres magiques à des variables ayant un nom significatif, comme ça :

double cmPerInch =2.54;
cm = inc * cmPerInch;

Cette version est plus facile à lire et induira moins d’erreurs, mais comporte encore un problème. Les variables peuvent varier, alors que le nombre de centimètres par pouce ne varie pas. Java propose une fonctionnalité qui renforce cette règle, le mot-clef final.

final double CM_PER_INCH =2.54;

Déclarer qu’une variable est final signifie qu’on ne peut pas changer sa valeur une fois qu’elle a été initialisée. Si vous essayez, le compilateur affichera une erreur. Les variables déclarées final sont appelées des constantes. Par convention, les noms pour les constantes sont entièrement en majuscules avec un underscore (_) entre les mots.

III-F - Sortie formatée

Quand vous affichez un double en utilisant print ou println, cela montre jusqu’à 16 décimales à l’écran :

System.out.print(4.0 / 3.0);

Le résultat est :

1.3333333333333333

Vous ne vouliez peut-être pas afficher autant de décimales. System.out fournit une autre méthode, appelée printf, qui permet un plus grand contrôle sur le format. Le « f » dans printf est le début de « formaté ». Voici un exemple :

System.out.printf("Trois quarts = %.3f ", 4.0 / 3.0);

La première valeur dans les parenthèses est une chaîne formatée qui spécifie comment les données doivent être affichées. Cette chaîne formatée contient du texte ordinaire suivi par une définition de format, qui est une séquence particulière commençant par le signe pourcent. La définition de format %.3f indique que la valeur qui suit devra être affichée comme un nombre décimal, arrondi à 3 chiffres après la virgule.

Le résultat est :

Trois quarts = 1.333

La chaîne formatée peut contenir autant de définitions de format que l’on souhaite ; voici un exemple avec deux :

int inch = 100;
double cm = inch * CM_PER_INCH;
System.out.printf("%d pouces = %.3f cm \n", inch, cm);

Le résultat est :

100 pouces = 254.000000 cm

Comme print, printf ne fait pas de renvoi à la ligne. Donc les chaînes formatées finissent souvent avec le caractère de nouvelle ligne \n.

La définition de format %d affiche des valeurs entières. Les valeurs sont associées avec les définitions de format dans l’ordre, donc inch est affiché en utilisant %d et cm est affiché en utilisant %f.

Apprendre à utiliser les chaînes formatées revient à apprendre un sous-langage dans Java. Il y a beaucoup d’options et les détails peuvent être innombrables. Le tableau 3.1 liste quelques utilisations basiques pour vous donner une idée de comment cela fonctionne. Pour plus de détails, allez voir la documentation de java.util.Formatter. La méthode la plus simple pour trouver de la documentation sur des classes Java et de faire une recherche Web avec « Java » et le nom de la classe.

Tableau 3.1 : exemple de définition de format

III-G - Des centimètres aux pouces

Maintenant, supposons que nous avons une mesure en centimètres et que nous voulons la convertir en pouces de manière précise. Il serait tentant d’écrire :

Inch = cm / CM_PER_INCH ;     // erreur de syntaxe

Mais cela provoque une erreur, vous aurez quelque chose comme : « Bad types in assignment : from double to int. ». Le problème vient du fait qu’à droite c’est un réel, alors que la variable de gauche est un entier.

La manière la plus simple de convertir un réel en entier est d’utiliser une conversion de type (cast), appelée ainsi en anglais, car cela moule la valeur d’un type dans un autre type. La syntaxe pour la conversion de type est de mettre le nom du type entre parenthèses et de l’utiliser comme opérateur.

double pi = 3.14159;
int x= (int) pi;

L’opérateur (int) a pour effet de convertir ce qui suit en entier. Dans l’exemple, x prend pour valeur 3. Convertir vers un entier arrondit toujours le nombre d’origine vers le bas. En clair, cela enlève tout simplement la partie décimale.

La conversion de type est prioritaire sur les opérations arithmétiques. Dans l’exemple suivant, la valeur de pi est convertie en un entier avant la multiplication. C’est pourquoi le résultat sera 60.0 et pas 62.0.

double pi = 3.14159;
int x= (int) pi * 20.0;

En tenant compte de ça, voici comment on peut convertir une mesure en centimètres vers des pouces :

inch = (int) (cm / CM_PER_INCH);
System.out.printf("%f cm = %d pouces\n", cm, inch);

Les parenthèses après l’opérateur de conversion permettent que la division se fasse avant la conversion de type. Et le résultat est arrondi vers 0 ; nous verrons dans le prochain chapitre comment arrondir les réels vers l’entier le plus proche.

III-H - Opérateur modulo

Allons plus loin avec cet exemple : supposez que vous avez une mesure en pouces et que vous voulez la convertir en pieds et en pouces. Le but est de diviser par 12 (12 étant le nombre de pouces dans un pied) et de garder le reste de la division.

Nous avons déjà vu l’opérateur de division (/), qui calcule le quotient de deux nombres. Si les nombres sont des entiers, cela calcule une division entière. Java fournit aussi l’opérateur modulo (%), qui divise deux nombres et calcule le reste.

En utilisant la division et le modulo, on peut convertir en pieds et pouces comme ça :

quotient = 76 /12;     // division
reste = 76 % 12;     // modulo

La première ligne donne 6. La seconde ligne, qui se prononce « 76 mod 12 », donne 4. Donc 76 pouces correspondent à 6 pieds et 4 pouces.

L’opérateur modulo ressemble à un caractère pourcent, mais vous pouvez, comme pense-bête, penser que c’est un signe de division (÷) tourné vers la droite.

L’opérateur modulo peut devenir étonnamment très utile. Pa exemple, vous pouvez vérifier qu’un nombre est divisible par un autre : si x % y donne zéro, alors x est divisible par y. Vous pouvez utiliser modulo pour extraire des chiffres d’un nombre : x % 10 extrait le chiffre le plus à droite du nombre x, et x % 100 extrait les deux derniers chiffres de x. Il y a aussi beaucoup d’algorithmes de cryptage qui utilisent l’opérateur modulo.

III-I - Assembler tout cela

À partir de maintenant, vous avez vu assez de Java pour écrire des programmes utiles, résolvant des problèmes de tous les jours. Vous pouvez (1) importer des bibliothèques de classes Java, (2) créer un objet Scanner, (3) récupérer une saisie du clavier, (4) formater un texte de sortie avec printf et (5) diviser et faire un modulo sur des entiers. Maintenant, nous allons rassembler tout ça dans un programme complet :

import java.util.Scanner;

/**
* Convertit les cm en pieds et pouces
*/
public class Convert {
public static void main(String[] args) {
double cm;
int feet, inches, remainder;
final double CM_PER_INCH = 2.54;
final int IN_PER_FOOT = 12;
Scanner in = new Scanner(System.in);

// Demande à l’utilisateur et récupère la valeur
System.out.print("Combien de cm exactement? ");
cm = in.nextDouble();

// convertit et affiche le résultat
inches = (int) (cm / CM_PER_INCH);
feet = inches / IN_PER_FOOT;
remainder = inches % IN_PER_FOOT;
System.out.printf("%.2f cm = %d ft, %d in\n",
cm, feet, remainder);
}
}

Bien que ce ne soit pas obligatoire, toutes les variables et les constantes sont déclarées au début de la procédure main. Cela permet de retrouver plus facilement leur type plus tard et cela aide le lecteur à savoir quelles données sont concernées dans l’algorithme.

Pour aider aussi à la lecture du code, chaque étape majeure est séparée par une ligne blanche et commence par un commentaire. On ajoute aussi un commentaire de documentation (// ou /*...*/), dont nous parlerons plus en détail dans le prochain chapitre.

De nombreux algorithmes, dont Convert, utilisent ensemble la division et le modulo . Dans les deux étapes, on divise par le même nombre (IN_PER_FOOT).

Quand une ligne de code devient trop longue (généralement, plus grande que 80 caractères), une convention de style courante est de couper la ligne en plusieurs lignes. Le lecteur ne doit pas avoir à faire défiler la page horizontalement.

III-J - Les bogues de Scanner

Maintenant que vous avez un peu d’expérience avec Scanner, nous devons vous avertir d’un comportement inattendu. Le code suivant demande aux utilisateurs leur nom et leur âge :

System.out.print("Quel est votre âge ? ");
age = in.nextInt();
System.out.print("Quel est votre nom ? ");
name = in.nextLine();
System.out.printf("Bonjour %s, %d ans\n", name, age);

Si vous essayez de lancer ce programme exemple, cela ne vous laissera pas saisir votre nom et affichera directement le résultat :

Quel est votre nom ? Bonjour , 45 ans

Pour comprendre ce qu’il se passe, vous devez comprendre que Scanner ne voit pas la saisie sur plusieurs lignes, comme nous. Au lieu de ça, il récupère un « flux de caractères » comme présenté sur la figure 3.3.

La flèche indique le prochain caractère qui va être lu par Scanner. Quand vous appelez nextInt, cela lit les caractères jusqu’à ce que ça arrive à un caractère qui n’est pas un chiffre. La Figure 3.4 montre l’état du flux après avoir appelé nextInt.

À ce moment, nextInt renvoie 45. Ensuite, le programme affiche la question « Quel est votre nom ? » et appelle nextLine, qui lit les caractères jusqu’à ce qu’il arrive à une nouvelle ligne. Mais vu que le prochain caractère est déjà une nouvelle ligne (\n), nextLine renvoie une chaîne vide "".

Pour résoudre le problème, vous avez besoin d’un nextLine supplémentaire après le nextInt.

System.out.print("Quel est votre âge ? ");
age = in.nextInt();
in.nextLine() ;    // lit la nouvelle ligne
System.out.print("Quel est votre nom ? ");
name = in.nextLine();
System.out.printf("Bonjour %s, %d ans\n", name, age);

Cette technique est courante quand on lit un int ou un double qui sont affichés sur leur propre ligne. D’abord vous lisez le nombre et ensuite vous lisez le reste de la ligne, qui est juste un retour à la ligne.

III-K - Vocabulaire

package (paquetage) : un groupe de classes qui sont reliées entre elles.

adress (adresse) : l’emplacement d’une valeur dans la mémoire de l’ordinateur, souvent représenté par un entier hexadécimal.

Library (bibliothèque) : un ensemble de paquetages et de classes disponibles pour être utilisés dans d’autres programmes.

import statement (fonction d’import) : une déclaration permettant aux programmes d’utiliser des classes définies dans d’autres paquetages.

Token : un élément de base d’un programme comme un mot, un espace, un symbole ou un nombre.

literal (littéral) : une valeur qui apparaît dans le code source. Par exemple, "Bonjour" est une chaîne littérale et 74 est un entier littéral.

magic number (nombre magique) : un nombre qui apparaît sans aucune explication dans une expression du code. Il doit généralement être remplacé par une constante.

Constant : une variable, déclarée comme final, dont la valeur ne peut être changée.

format string (chaîne formatée) : une chaîne passée en paramètre à printf pour définir le format de sortie.

format specifier (définition de format) : un code spécial qui commence par le signe pourcent et qui définit le type de données et le format de la valeur correspondante.

type cast (conversion de type) : une opération qui convertit explicitement un type de données vers un autre. En Java, cela se présente sous la forme d’un nom de type entre parenthèses, comme (int).

Modulus (modulo) : un opérateur qui calcule le reste d’une division d’un entier par un autre entier. En Java, on le note avec un signe pourcent ; par exemple, 5 % 2 donne 1.

III-L - Exercices

Le code pour ce chapitre est dans le répertoire ch03 de ThinkJavaCode. Voir la page ?? pour avoir des instructions (en anglais) pour récupérer le dépôt. Avant de commencer les exercices, nous vous recommandons de compiler et lancer les exemples.

Si vous n’avez pas encore lu l’appendice A.3, c’est le moment. Il décrit l’interface en ligne de commandes, qui est une manière puissante et efficace d’interagir avec votre ordinateur.

Exercice 1 : quand vous utilisez printf, le compilateur Java ne vérifie pas votre chaîne formatée. Regardez ce qu’il se passe si vous essayez d’afficher une valeur de type int en utilisant %f. Et que se passe-t-il si vous affichez un double en utilisant %d ? Et si vous mettez deux définitions de format dans le texte, mais que vous ne mettez qu’une valeur en paramètre ?

Exercice 2 : écrivez un programme qui convertit une température en degrés Celsius en une température en degrés Fahrenheit. Il devra (1) demander à l’utilisateur une valeur, (2) lire un double depuis le clavier, (3) calculer le résultat, (4) formater l’affichage de ce résultat avec un seul chiffre après la virgule. Par exemple, il devrait s’afficher : « 24,0 C = 75,2 F ».

Voici la formule, faites attention à ne pas utiliser la division d’entier!

Exercice 3 :écrivez un programme qui convertit un nombre total de secondes en heures, minutes et secondes. Il devra (1) demander à l’utilisateur une valeur, (2) lire un entier depuis le clavier, (3) calculer le résultat, (4) formater l’affichage de ce résultat avec printf. Par exemple, il devrait s’afficher : « 5000 secondes = 1 heures, 23 minutes et 20 secondes ».

P.-S : - Utilisez l’opérateur modulo.

Exercice 4 : le but de cet exercice est de programmer un jeu « Devine le nombre ». Quand il sera fini, il devra fonctionner comme ça :

Je pense à un nombre entre 1 et 100 (inclus). Pouvez-vous le deviner ?
Tapez un nombre : 45
Votre réponse est : 45
Le nombre auquel je pensais était : 14
Vous y étiez presque, à 31 près...

Pour choisir un nombre aléatoire, vous pouvez utiliser la classe Random dans java.util. Voici comment elle fonctionne :

import java.util.Random;

public class GuessStarter {
public static void main(String[] args) {
// sélectionner un nombre aléatoire
Random random = new Random();
int number = random.nextInt(100) + 1;
System.out.println(number);
}
}

Comme la classe Scanner que nous avons vue dans ce chapitre, Random doit être importée avant son utilisation. Et comme nous l’avons vu avec Scanner, on doit utiliser l’opérateur new pour créer un Random (générateur de nombre).

Ensuite, on peut utiliser la méthode nextInt pour générer un nombre aléatoire. Dans cet exemple, le résulta de nextInt(100) sera un nombre entre 0 et 99, inclus. En ajoutant 1, vous aurez un nombre ente 1 et 100 inclus.

1. La déclaration de GuessStarter est dans un fichier appelé GuessStarter.java, dans le répertoire appelé ch03, dans le dépôt de ce cours.

2. Compilez et lancez le programme.

3.Modifiez le programme pour interroger l’utilisateur, puis utilisez Scanner pour lire la réponse. Compilez et testez le programme.

4. Lisez la réponse de l’utilisateur comme un entier et affichez le résultat. Encore une fois, compilez et testez le programme.

5. Calculez et affichez la différence entre la réponse de l’utilisateur et le nombre qui avait été généré.

IV - Structures conditionnelles et récursion

V - Entrée et sortie

Ce chapitre est en cours de traduction.

VI - Méthodes Void

Ce chapitre est en cours de traduction.

VII - Conditions et logique

Ce chapitre est en cours de traduction.

VIII - Méthodes de valeur

Ce chapitre est en cours de traduction.

IX - Boucles

Ce chapitre est en cours de traduction.

X - Tableaux

Ce chapitre est en cours de traduction.

XI - Chaîne de caractères et autres

Ce chapitre est en cours de traduction.

XII - Objets

Ce chapitre est en cours de traduction.

XIII - Les classes

Ce chapitre est en cours de traduction.

XIV - Tableaux d'objets

Ce chapitre est en cours de traduction.

XV - Objets de tableaux

Ce chapitre est en cours de traduction.

XVI - Objets d'objets

Ce chapitre est en cours de traduction.

XVII - Outils de développement

Ce chapitre est en cours de traduction.

XVIII - Graphiques 2D en Java

Ce chapitre est en cours de traduction.

XIX - Le débogage

Ce chapitre est en cours de traduction.

XX - Indice

Ce chapitre est en cours de traduction.