Introduction à la programmation informatique
Introduction à la programmation
Cours de tronc commun, 1re année
Ce cours a pour objectif de vous initier à l’informatique, et en tout premier lieu à la programmation. Le langage «support »choisi est Java, et vous aurez donc l’occasion d’écrire des programmes dans ce langage. Mais avant tout, nous souhaitons vous familiariser avec les concepts et principes fondamentaux de la programmation; de ce fait, vous devriez être capable, à l’issue de ce cours, de vous familiariser assez rapidement avec un autre langage de programmation que Java.
Le cours est basé sur plusieurs livres, dont vous trouverez les références en fin de polycopié. Le langage Java fait l’objet d’une abondante littérature, de qualité parfois inégale. De plus, il peut encore évoluer. En complément d’un livre, il peut donc être opportun de garder un signet sur le site de référence, , où vous trouverez entre autres des tutoriaux en ligne, dans des versions parfois plus récentes que leurs versions imprimées. Notez bien toutefois que la plupart des livres sur le marché sont destinés à un public d’informaticiens, qui savaient déjà programmer avant d’apprendre Java
Enfin, je vous invite à garder un signet sur ma propre page web à l’école, où je regroupe au fur et à mesure des informations plus ou moins directement liées à ce cours et des pointeurs sur des ressources Internet intéressantes. Vous y trouverez notamment un pointeur sur les API de Java, c’est-à-dire la documentation en ligne de l’ensemble des classes disponibles dans l’environnement Java.
Une page spécifiqueregroupe les informations pratiques sur le déroulement du cours : horaires, énoncés et corrigés des TDs, groupes, programme des séances, etc.
c Karl Tombre, École des Mines de Nancy. Document édité avec XEmacs et formatté avec LATEX. Achevé d’imprimer le 2 septembre 2003. Un grand merci à tous ceux, collègues ou étudiants, qui, au fil des éditions successives de ce polycopié, ont contribué à l’améliorer par leurs relectures et leurs nombreuses suggestions d’amélioration, et en particulier à Philippe Dosch, Jacques Jaray, Luigi Liquori, Bart Lamiroy et (last, but not least) Guillaume Bonfante.
Table des matières
1 Introduction 1
1.1 Informatique = mécanisation de l’abstraction.. . . . . 1
1.2 Traduction ... . . . . 2
1.3 La programmation : du problème au programme .. . . 2
2 Les constructions de base en Java 5
2.1 Constantes et variables... . . . . . 5
2.2 Typage .... . 6
2.3 Types élémentaires en Java... . . 6
2.4 Expressions... . . . . 7
2.4.1 Opérateurs arithmétiques.. . . . . 7
2.4.2 Opérateurs logiques et relationnels.8
2.4.3 Opérateurs bit à bit... . . 9
2.5 L’affectation... . . . . 9
2.6 Mon premier programme Java... . 10
2.7 De la structuration du discours : les instructions de contrôle. . 12
2.7.1 Instructions conditionnelles.. . . . 12
2.7.2 Instructions itératives... . 16
3 Structuration 23
3.1 La classe, première approche : un regroupement de variables. . 23
3.1.1 Allocation de mémoire... . 24
3.1.2 Exemple : un embryon de programme de gestion de compte. . . . . 25
3.2 Fonctions et procédures... . . . . 27
3.2.1 Les fonctions – approche intuitive .. . . . . . . 27
3.2.2 Les fonctions – définition plus formelle.. . . . . 30
3.2.3 Les procédures... . . . . . 31
3.2.4 Le cas particulier de main.. . . . . 33
3.2.5 Surcharge... . 33
3.3 Les tableaux ... . . . 34
4 Programmation objet 39
4.1 Retour sur la classe... 39
4.1.1 Fonctions d’accès... . . . . 41
4.2 Les objets... . . . . . 41
4.3 Méthodes et variables de classe.. . . . . . 44
4.3.1 Retour sur la procédure main.. . . 46
4.3.2 Comment accéder aux variables et méthodes de classe? . . . . . . . 47
4.4 Exemple d’une classe prédéfinie : String.. 47
4.4.1 Application : recherche plus conviviale du compte . . . 48
4.5 La composition : des objets dans d’autres objets.. . . 52
4.6 L’héritage... . . . . . 56
4.6.1 Héritage et typage... . . . 60
4.6.2 Liaison dynamique... . . . 62
iii
iv TABLE DES MATIÈRES
5 Modèles et structures de données 67
5.1 Les listes.... . 67
5.1.1 Représentation par un tableau .. . 67
5.1.2 Représentation par une liste chaînée.. . . . . . 68
5.1.3 Comparaison entre les deux représentations.. . 69
5.1.4 Application : l’interface ListeDeComptes.. . . 69
5.2 Les piles.... . 78
5.3 Les arbres... . . . . . 79
6 Programmation (un peu) plus avancée 81
6.1 Portée.... . . 81
6.2 Espaces de nommage : les packages.. . . . 82
6.3 Entrées/sorties... . . 84
6.3.1 L’explication d’un mystère.. . . . . 85
6.3.2 Les fichiers ... 86
6.4 Exceptions ... . . . . 100
6.4.1 Les derniers éléments du mystère.. 100
6.4.2 Exemple : une méthode qui provoque une exception . . 101
6.5 La récursivité... . . . 102
6.5.1 Exemple : représentation d’un ensemble par un arbre. . 102
6.6 Interface homme–machine et programmation événementielle. . 106
6.7 Conclusion... . . . . 125
7 Introduction sommaire au monde des bases de données 127
7.1 Le modèle relationnel ... . . . . . 128
7.2 SQL.... . . . 129
7.2.1 Introduction ... . . . . . . 129
7.2.2 Création de schémas et insertion de données.. . 129
7.2.3 Projections et sélections.. . . . . . 130
7.2.4 Jointure... . . 131
7.2.5 Quelques autres clauses et fonctions.. . . . . . 132
7.3 JDBC.... . . 133
A Quelques éléments d’histoire 137
B La logique et l’algèbre de Boole 139
C Glossaire 141
D Les mots clés de Java 143
E Aide-mémoire de programmation 145
E.1 Constructions conditionnelles... . 145
E.2 Constructions itératives... . . . . 146
E.3 Définition et appel d’une fonction.. . . . . 147
E.4 Définition et appel d’une procédure.. . . . 147
E.5 Définition d’une classe... . . . . . 148
E.6 Instanciation d’une classe et accès aux méthodes.. . . 148
E.7 Récupération d’une exception... . 148
F Quelques précisions sur le codage 151
F.1 Le codage des caractères... . . . . 151
F.2 Le codage des entiers... . . . . . . 152
F.2.1 Codage avec bit de signe.. . . . . . 152
F.2.2 Le complément à un... . . 152
F.2.3 Le complément à deux... . 152
F.3 Le codage des réels... 153
Chapitre 1 Introduction
L’informatique est avant tout une science de l’abstraction — il s’agit de créer le bon modèle pour un problème et d’imaginer les bonnes techniques automatisables et appropriées pour le résoudre. Toutes les autres sciences considèrent l’univers tel qu’il est. Par exemple, le travail d’un physicien est de comprendre le monde et non pas d’inventer un monde dans lequel les lois de la physique seraient plus simples et auxquelles il serait plus agréable de se conformer. À l’opposé, les informaticiens doivent créer des abstractions des problèmes du monde réel qui pourraient être représentées et manipulées dans un ordinateur.
1.1 Informatique = mécanisation de l’abstraction
Un programme est une suite d’instructions définissant des opérations à réaliser sur des données. Les instructions du programme sont exécutées les unes après les autres, le plus souvent dans l’ordre séquentiel dans lequel elles sont données dans le programme – on dit que le flot d’exécution, ou flot de contrôle, est séquentiel. Pour écrire des programmes, on se sert d’une notation, appelée langage de programmation. Les langages de programmation les plus rudimentaires sont ceux qui «collent » au jeu d’instructions de l’ordinateur; on les appelle les langages d’assemblage (cf. annexe A). Un langage d’assemblage est par définition propre à un processeur donné (ou au mieux à une famille de processeurs); l’évolution de la programmation vers des projets à la fois complexes et portables a créé le besoin de langages de plus haut niveau. Ces langages permettent de gérer la complexité des problèmes traités grâce à la structuration, qu’on définira dans un premier temps, de manière très générale, comme le regroupement d’entités élémentaires en entités plus complexes (enregistrements regroupant plusieurs données, modules ou procédures regroupant plusieurs instructions élémentaires ).
Un bon programme doit être facile à écrire, à lire, à comprendre et à modifier. Il faut savoir que la mémoire humaine est limitée par le nombre plus que par la taille des structures à manipuler. Il devient donc vite difficile pour un programmeur de maîtriser de nombreuses lignes de code, si celles-ci ne sont pas regroupées en modules ou autres structures.
Les avantages de l’utilisation d’un langage de haut niveau sont multiples :
– Un programme n’est pas seulement destiné à être lu une fois; plusieurs intervenants risquentde se pencher sur lui au cours de son existence, d’où la nécessité de la lisibilité.
– L’existence de langages évolués a permis la création de logiciels par des programmeurs quin’auraient jamais appris un langage d’assemblage. Il faut noter à ce propos la puissance et la popularité croissante des langages dits de scriptage (Visual Basic, TCL/TK ) et des générateurs de programmes de gestion.
– Un programme en langage évolué peut être «porté »sur différentes architectures et réutiliséau fil de l’évolution du matériel.
– Un module clairement spécifié peut être réutilisé comme une brique de construction, dansdivers contextes.
1
2 Introduction
– Un langage peut choisir de laisser transparaître la machine sous-jacente, ou au contraire four-nir un autre modèle de calcul (cf. parallélisme par exemple, même s’il y a un seul processeur physique, ainsi que la programmation fonctionnelle ou la programmation logique).
– Les langages de programmation offrent des outils pour l’abstraction, la modélisation et lastructuration des données et des opérations. Ils permettent aussi la vérification de type, qui évite beaucoup d’erreurs (elle permet par exemple de refuser un programme qui voudrait additionner 1 et "z").
1.2 Traduction
À partir du moment où l’on utilise un langage différent de celui de la machine, il devient nécessaire de lui traduire les programmes écrits dans ce langage. Deux approches sont possibles pour cela. La première consiste à lancer un programme de traduction simultanée, appelé interprète, qui traduit et exécute au fur et à mesure les instructions du programme à exécuter. La deuxième approche consiste à analyser l’ensemble du programme et à le traduire d’avance en un programme en langage machine, qui est ensuite directement exécutable. C’est la compilation.
Java fait partie des langages qui choisissent une approche hybride : un programme en Java est analysé et compilé, mais pas dans le langage de la machine physique. Pour des raisons de portabilité, le compilateur Java traduit dans un langage intermédiaire, universel et portable, le byte-code, qui est le langage d’une machine virtuelle, la JVM (Java Virtual Machine). Cette «machine » est exécutée sur une machine physique et un interprète le byte-code.
Pour traduire un langage, il faut tenir compte de ses trois niveaux :
– le niveau lexical, qui concerne le vocabulaire du langage, les règles d’écriture des mots du langage (identificateurs de variables ou fonctions), les mots clés, c’est-à-dire les éléments de structuration du discours, et les caractères spéciaux, utilisés par exemple pour les opérateurs;
– le niveau syntaxique, qui spécifie la manière de construire des programmes dans ce langage, autrement dit les règles grammaticales propres au langage;
– le niveau sémantique, qui spécifie la signification de la notation.
1.3 La programmation : du problème au programme
Nous attaquons ici toute la problématique de la programmation. On peut distinguer les étapes suivantes dans la vie d’un logiciel [1] :
– Définition du problème et spécification, incluant éventuellement plusieurs itérations de spé-cification avec les futurs utilisateurs du logiciel, un prototypage du produit final, et une modélisation des données.
– Conception, avec création de l’architecture de haut niveau du système, réutilisant si possibledes composants déjà disponibles.
– Réalisation des composants et test de chacun d’eux pour vérifier qu’ils se comportent commespécifié.
– Intégration et test du système : une fois chaque composant testé et validé, encore faut-il quele système intégré le soit.
– Installation et test «sur le terrain », c’est-à-dire en situation réelle.
– Maintenance : elle représente souvent plus de la moitié du coût de développement. Le systèmedoit être purgé d’effets imprévisibles ou imprévus, ses performances doivent être corrigées ou améliorées, le monde réel dans lequel il est implanté n’étant pas immuable, il faut modifier le programme ou lui ajouter de nouvelles fonctionnalités, le système doit souvent être porté sur de nouvelles plateformes physiques, etc. Tous ces aspects de maintenance soulignent l’importance d’écrire des programmes lisibles, corrects, robustes, efficaces, modifiables et portables!
Nous voyons donc que l’activité d’analyse et de programmation, bien qu’elle soit au cœur de notre cours, ne représente qu’une des étapes dans le cycle logiciel. Ceci étant, nous allons essentiellement nous arrêter sur les phases qui permettent d’aller d’un problème bien spécifié à un programme, en passant par la réalisation d’un algorithme précis, c’est-à-dire de la conception d’une
1.3. La programmation : du problème au programme 3
démarche opératoire pour résoudre le problème donné, cette démarche étant ensuite transcrite dans un langage de programmation, en l’occurrence Java.
Dans ce cours, nous adoptons une démarche pragmatique, l’apprentissage des différentes notions se faisant par l’exemple, en l’occurrence le développement tout au long des chapitres d’un exemple de programme de gestion bancaire, que nous allons peu à peu enrichir de nouvelles fonctionnalités, qui illustrent les notions nouvelles introduites à chaque fois. Plutôt que de remplir de longues pages sur la syntaxe et les règles de programmation, nous avons préféré mettre beaucoup d’exemples complets de programmes, comptant sur votre capacité d’apprentissage par analogie.
Le chapitre 2 donne les premières bases nécessaires pour écrire un programme Java élémentaire. Le chapitre 3 introduit les outils fondamentaux de structuration des données et des instructions, à savoir les classes d’un côté et les fonctions et procédures de l’autre. Au chapitre 4, nous revenons sur la classe, mais pour introduire les bases de la programmation objet, méthodologie devenue quasiment incontournable à l’heure actuelle. Le chapitre 5, quant à lui, vous présente quelques exemples de modèles de données et la manière dont ils peuvent être représentés et manipulés. Il resterait tant à étudier, et le temps nous fait défaut; j’ai donc réuni au chapitre 6 un ensemble probablement un peu disparate d’éléments supplémentaires, qui nous permettront cependant de compléter la partie programmation de ce cours par un programme qui dépasse légèrement la banalité habituelle des exercices d’école. Enfin, pour que vous n’ayez pas l’impression que l’informatique se résume à la programmation, nous apportons également au chapitre 7 un court éclairage sur le monde des bases de données.
J’ai choisi de reporter en annexes plusieurs ensembles d’informations utiles en soi, mais dont la présence directe dans le fil du polycopié aurait nui à la progression de la lecture. Je vous invite cependant à vous reporter aussi souvent que nécessaire à ces annexes. Vous y trouverez en particulier un glossaire (annexe C) que je vous conseille de consulter tout au long de votre lecture. Vous trouverez peut-être également intéressant de recourir parfois à l’aide-mémoire de programmation (annexe E) lors de vos séances de TD.
Chapitre 2 Les constructions de base en Java
2.1 Constantes et variables
La manière la plus simple de manipuler des valeurs dans un programme est d’écrire directement ces valeurs. Ainsi, on pourra écrire 24 pour désigner un nombre entier, 176.54 pour désigner le nombre réel correspondant, 2.54E+12 pour désigner le nombre 2.54 × 1012, ’a’ pour indiquer un caractère, "Jean-Paul" pour désigner une chaîne de caractères, et true pour indiquer la valeur booléenne «vrai ».
Toutes ces valeurs sont des constantes. Je peux par exemple demander à un programme d’afficher le résultat de l’opération 1+1. Le seul problème est bien entendu que si je souhaite effectuer une autre opération, il faudra modifier le programme, par exemple en écrivant 1+2. Or l’un des intérêts de la programmation est justement de pouvoir décrire une démarche opératoire qui reste la même alors que les données du problème peuvent changer. Par exemple, il est clair que l’opération «créditer un compte bancaire » correspond au même algorithme et au même programme, quels que soient le solde initial du compte et le montant dont on doit le créditer.
Il est donc indispensable de ne pas rester limité à l’emploi de constantes dans les programmes informatiques. D’un point de vue algorithmique, la variable est un objet qui a un nom (l’identificateur de la variable) et, au cours de sa vie, une valeur à chaque instant t donné. Concrètement, au sein de l’ordinateur, la variable va correspondre à un emplacement mémoire dans lequel on peut stocker une valeur. Quand on écrit un programme, le traducteur (compilateur ou interprète) associe à chaque variable l’adresse de l’emplacement mémoire correspondant et réalise l’adressage indirect nécessaire pour accéder en lecture ou en écriture à cet emplacement.
Il est bon de prendre l’habitude dès le début de donner à ses variables des noms mnémotechniques. Bien entendu, pour l’ordinateur, il est strictement équivalent que vous appeliez vos variables a, b, c, d, i1, i2, i3 ou soldeDuCompte, montantACrediter, ageDuCapitaine, nomDuClient; mais vous comprenez aisément que lorsqu’un programme dépasse quelques dizaines de lignes, il est beaucoup plus facile pour un être humain de comprendre, de modifier et de maintenir un programme utilisant le deuxième type de noms de variables.
Revenons aux constantes. Nous avons vu qu’une valeur constante peut être écrite telle quelle dans un programme, par exemple 19.6 ou "Bonjour". Mais dans bien des cas, il est préférable de donner un nom symbolique aux constantes également, comme pour les variables. Pourquoi? Prenons l’exemple d’un calcul de prix ttc. À l’heure où j’écris ces lignes, le taux de tva standard est de 19,6%. Si donc je fais tous mes calculs de prix ttc dans mon programme en multipliant les variables désignant le prix hors taxe par 1.196, je serai dans le pétrin le jour où le gouvernement relève ou baisse le taux de la tva. Il me faudra chercher toutes les occurrences du nombre 1.196 dans le programme, vérifier qu’elles correspondent bien à un calcul de tva, et faire les modifications. Il est donc bien plus judicieux de désigner la constante par un nom, comme pour les variables. C’est ce que permet le mot clé final, grâce auquel on écrira :
final double tauxTVA = 1.196;
pour indiquer que tauxTVA est une constante de type réel (cf. § 2.3). Tous les calculs se feront avec cette constante nommée, et quand le taux de tva changera, il y aura une seule ligne à modifier dans le programme De même, en cas de programme multilingue, on pourra par exemple écrire :
final String salutation = "Bonjour";
ou au contraire
final String salutation = "Guten Tag";
ou
final String salutation = "Buenos dias";
2.2 Typage
Le type d’une expression ou d’une variable indique le domaine des valeurs qu’elle peut prendre et les opérations qu’on peut lui appliquer. En Java comme dans la plupart des langages évolués, toute variable doit être déclarée avec un type donné. Ce peut être un des types de base du langage, ou un type construit avec les outils de structuration fournis par le langage. Toute expression valide a aussi un type, et le résultat d’une opération doit a priori être affecté à une variable du type correspondant.
Il est relativement aisé de donner une interprétation ensembliste à la notion de type. Nous aurons l’occasion de le faire plus d’une fois, en particulier quand nous aborderons la notion de classe (cf. § 3.1 et 4.1).
– On dispose d’ensembles de base, auxquels correspondent en Java des types de bases:
– R : double, float
– Z : byte, short, int, long
– Bool : boolean
– caractères : char
– On peut définir de nouveaux ensembles : si on a besoin d’un ensemble de couleurs, on pourraécrire quelque chose du genre class Couleur, comme nous le verrons ultérieurement. Cette capacité à définir de nouveaux ensembles ou types existe dans la grande majorité des langages de programmation.
– On peut définir des produits d’ensemble, comme par exemple Couleur×Bool ou Z × Z × R. Nous verrons que les classes permettent en particulier ce genre de construction.
– On peut définir des fonctions, comme par exemple f : Zp × Z ? Bool, qui s’écrira en Java boolean f(int a, int b).
– Grâce aux listes et autres collections, on peut définir des séquences d’objets.
La plupart des langages évolués offrent les constructions nécessaires pour mettre en œuvre les opérations ensemblistes de base que sont le produit de deux ensembles (et l’accès aux fonctions de projection associées), les fonctions et opérateurs, et la séquence ordonnée ou non d’éléments d’un ensemble.