forked from fabienfelix/formation-programmation-java
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cours-java.ad
1484 lines (965 loc) · 52.3 KB
/
cours-java.ad
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
= Cours de programmation orientée objet avec Java
:author: Arnaud Tournier
:email: [email protected]
:toc: macro
:toc-title: Cours de programmation orientée objet avec Java
:imagesdir: images-java
:source-highlighter: highlightjs
LTE Consulting (C) - 2016
toc::[]
Ce cours est Open Source. Les sources et ce document sont disponibles en ligne à ces adresse :
- Ce document : http://lteconsulting.fr/java/[lteconsulting.fr/java/]
- Les sources de ce document ainsi que des projets d'exemple : https://github.com/ltearno/formation-programmation-java[github.com/ltearno/formation-programmation-java]
Vous pouvez donc proposer des modifications (les corrections sont bienvenues) en créant des https://help.github.com/articles/using-pull-requests/[pull requests].
== Avant-propos
Ce document n'a pas vocation a être ni trop détaillé ni exhaustif. On renverra le lecteur vers des documents plus riches lorsque cela est nécessaire.
Voici une liste de liens que vous pouvez utiliser pour vous auto-documenter, ou bien simplement pour approfondir les connaissances acquises dans ce cours.
=== Quelques liens utiles
Afin de connaître le Java de façon approfondie, il est nécessaire de se documenter et d'expérimenter. Voilà quelques liens qui vous permettront d'avancer de façon autonome.
Les cours :
- http://docs.oracle.com/javase/tutorial/[Tutoriel officiel Java (en Anglais)]
- http://www.oracle.com/technetwork/java/javase/java-tutorial-downloads-2005894.html[Page de téléchargement des tutoriaux officiels Java (en Anglais)]
- Le fameux http://www.jmdoudoux.fr/accueil_java.htm#dej[cours] de https://www.linkedin.com/in/jmdoudoux[Jean-Michel Doudoux], un des nombreux https://java-champions.java.net/[Java champions].
- Des https://openclassrooms.com/courses/apprenez-a-programmer-en-java[cours] sur open class rooms. C'est un site d'auto-formation, il y a souvent de bons documents.
Les exercices :
- Les http://perso.telecom-paristech.fr/~charon/coursJava/exercices/index.html[cours] d'Irène Charon.
- D'autres exercices sur http://www.cours-gratuit.com/java/[cours-gratuit.com/java/].
Les personnes influentes :
- Rémi Forax,
- Antonio Goncalves (livre Java EE),
- José Paumard avec son blog http://blog.paumard.org/[Java le soir],
- Jean-Michel Doudoux,
- Antoine Sabot-Durand,
- ...
== Enjeux et Etat de l'art
Le système d'information est crucial pour n'importe quelle entreprise. Toutes les informations y sont stockées (clients, factures, ...).
Créer des SI est un métier. Le marché est pour le plus gros organisé avec des _prestataires de service_ (SSII), qui créent et gèrent le SI pour leurs clients. Le point est que concevoir, déployer et gérer un SI est un métier qui exige des compétences fortes.
De nombreuses pratiques et théories existent pour construire et gérer les SI.
Nous allons découvrir la _programmation objet avec Java_ - une de ces méthodes - qui permet la conception et le développement d'applications d'entreprise de grande taille tout en _maîtrisant_ la complexité des problèmes soumis au développeur.
== UML - conception objet
=== Modélisation objet
=== Diagramme de cas d'utilisation
=== Diagramme de classe
=== Diagramme de séquence
== Java - Les fondamentaux
=== Présentation de Java et JVM
=== Principes
=== Programme `HelloWorld`
Créez un projet vide et un fichier `HelloWorld.java` dedans avec le contenu suivant :
[source,language="java"]
----
public class HelloWorld
{
public static void main( String[] args ) <1>
{
System.out.println( "Bonjour tout le monde !" ); <2>
}
}
----
<1> Méthode principale du programme.
<2> Appel qui permet d'afficher "Bonjour tout le monde !" à l'écran.
Compiler ce code avec la commande
`javac HelloWorld.java`
Ceci produit un fichier `HelloWorld.class` contenant le byte-code issue de la compilation du code source.
Exécutez ce programme avec la commande :
`java HelloWorld`
Le programme écrit "Bonjour tout le monde !" sur la console.
=== Variables
Variable:: un emplacement en mémoire avec un _nom_ et un _type_.
==== Déclaration
Ceci déclare une variable nommée `i` de type `int`.
[source,language="java"]
----
int i;
----
==== Affectation
Ici nous affectons la valeur `42` à la variable `i`.
[source,language="java"]
----
int i; <1>
i = 42; <2>
----
<1> Déclaration
<2> Affectation
==== Utilisation
Pour utiliser la valeur contenue dans la variable, il suffit de mentionner son nom :
[source,language="java"]
----
int v = i; <1>
System.out.println( "La valeur de v est : " + v ); <2>
----
<1> Lecture de `i` pour affecter la variable ``v`
<2> Lecture de `v` pour construire la chaine qui va s'afficher dans la console.
=== Les types primitifs
Il existe en Java deux familles de type : les types primitifs et les types objet.
Les types primitifs portent des valeurs, en voici la liste :
- valeur booléenne : `boolean`,
- valeurs entières : `byte`, `short`, `int`, `long`,
- valeurs flottantes : `float` et `short`,
- valeur caractère (unicode, 16 bits) : `char`.
NOTE: Attention, les types primitifs `float` et `double` sont codés en virgule flottante ! Il ne faut **surtout pas les utiliser** pour effectuer des traitements monétaires. Voici http://www.enseignement.polytechnique.fr/informatique/profs/Jean-Jacques.Levy/poly/main1/node7.html[quelques] http://pdp.microjoe.org/tutoriels/85/les-nombres-a-virgule-flottante/[liens] http://www.math.univ-paris13.fr/~delourme/Feuilles_TD_Projets_2013-2014/Chapitre1Japhet.pdf[explicatifs] et http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html[passionnants].
=== Les tableaux
Une ou plusieurs dimension.
Indices démarrent de zéro.
Un tableau stocke la classe des éléments qu'il stocke. Il provoque une exception `java.lang.ArrayStoreException` si un objet du mauvais type est inséré dans le tableau.
Si on essaie d'accéder à une cellule du tableau qui n'existe pas (index négatif ou index trop grand), l'exception `IndexOutOfBoundsExeption` est levée.
==== Déclaration
[source,language="java"]
----
int[] tableauEntiers; <1>
String[] tableauString; <1>
----
<1> Les tableaux sont ici seulement déclarés, il ne sont pas encore _instanciés_ en mémoire. La variable `tableauEntiers` vaut `null` (de même pour `tableauString`).
==== Instantiation
On crée véritablement le tableau en mémoire avec l'instantiation qui s'écrit ainsi :
[source,language="java"]
----
tableauEntiers = new int[50]; <1>
----
<1> Nous demandons la réservation en mémoire d'un espace pouvant stocker 50 valeurs `int`.
==== Utilisation
[source,language="java"]
----
// lecture de la sixième cellule
int valeurCellule = tableauEntiers[5];
// écriture dans la première cellule
tableauEntiers[0] = -2;
----
=== La classe `String`
La classe `String` sert à représenter des chaînes de caractères (qui ont le plus souvent comme objectif d'être présentées à l'utilisateur, à un moment où un autre).
La https://docs.oracle.com/javase/7/docs/api/java/lang/String.html[Javadoc] de la class String est complète pour connaître les méthodes statiques et non statiques de cette classe. Voici néanmoins quelques méthodes de la classe `String` que vous utiliserez souvent :
==== `boolean startsWith( String other )`, `boolean endsWith( String other )`
Permet de savoir si une chaîne commence par une autre (ou termine pour la méthode `endsWith()`).
==== `String[] split( String separateur )`
Permet de découper une chaîne de caractère en morceaux séparés par la chaine contenue dans le paramètre `separateur`.
Exemple :
[source,language="java"]
----
String s = "5;6";
// partage la chaine "5;6" en utilisant ';' comme séparateur
String[] parts = s.split( ";" );
// affiche "2"
System.out.println( parts.length );
// affiche "5"
System.out.println( parts[0] );
// affiche "6"
System.out.println( parts[1] );
NOTE: Attention, le paramètre `separateur` est en fait interprété comme une expression régulière.
----
==== `String trim()`
Renvoie la chaine sans les espaces de début et de fin.
Par exemple appeller cette méthode sur la chaine " Bonjour Madame " retournera la chaine "Bonjour Madame".
==== `String toLowerCase()`
Retourne une chaine dans laquelle les majuscules ont été transformées en minuscules.
==== `String toUpperCase()`
Retourne une chaine dans laquelle les minuscules ont été transformées en majuscules.
==== `boolean isEmpty()`
Retourne `true` si la chaine de caractères est vide, `false` sinon.
==== `int length()`
Retourne la longueur de la chaîne de caractères.
==== `boolean equals( Object other )`, `boolean equalsIgnoreCase( String other )`
Retourne `true` si la chaine passée en paramètre est identique à celle sur laquelle on appelle la méthode. La méthode `equalsIgnoreCase` ignore la casse lors de la comparaison.
==== `int indexOf( String part )`
Cherche dans la chaine si elle contient `part`, auquel cas cette méthode retourne l'index où de trouve `part` dans la chaîne sur laquelle on appelle cette méthode. Elle retourne *-1* si `part` n'est pas trouvée.
==== `substring( int start, int end )`
Retourne une sous chaine de la chaine sur laquelle est appellée la méthode, en commençant à l'index `start` et en terminant à l'index `end` _exclu_.
==== `replace( String motif, String par )`, `replaceAll( String motif, String par )`
Retourne une chaine dans laquelle l'occurence de `motif` a été remplacée par `par`. La méthode `replaceAll` remplace *toutes* les occurences.
NOTE: Attention, le paramètre `motif` est en fait interprété comme une expression régulière.
=== Structures de controle
Instructions de branchement:: `if`, `switch`.
Instructions de bouclage:: `for`, `while`, `do ... while`.
=== Ecrire une classe
==== Un fichier, une classe
Une classe java s'écrit dans un fichier nommé par le même nom (et même casse) auquel on donne l'extension `.java`.
Ce fichier .java sera (_le plus souvent_) situé dans un sous-répertoire du répertoire `src/` du projet, et qui correspondra au chemin jusqu'au _package_ de la classe.
NOTE: Il est néanmoins possible de déclarer plusieurs classes dans un même fichier *.java*. Nous reviendrons sur ces cas plus tard dans le cours (voir les classes imbriquées).
==== Déclaration d'une classe
Pour créer une classe, on utilise le code suivant :
[source,language="java"]
----
public <1> class NomDeLaClasse <2>
{
// Corps de la classe (attributs et méthodes)
}
----
<1> Modificateur de visibilité : `public` ou `protected`.
<2> Nom de la classe en cours de définition.
NOTE: Par convention les noms de classes commencent par une majuscule comme par exemple `NomDeLaClasse`.
==== Fully Qualified Name
Le véritable nom complet d'une classe s'appelle le _Fully Qualified Name_ (FQN). Il est composé du _nom de package_ dans lequel se trouve la classe ainsi que du _nom de la classe_ lui-même.
Ainsi pour une classe déclarée ainsi :
[source,language="java"]
----
package fr.lteconsulting;
class MaClasse
{
// ...
}
----
Le _FQN_ de cette classe est `fr.lteconsulting.MaClasse`.
NOTE: Par convention les noms de package sont tout en minuscule, comme par exemple : `fr.lteconsulting`.
==== Importation avec `import`
Lorsque l'on souhaite utiliser une classe qui n'est pas située dans le même package que celle qui l'utilise, on peut l'importer, de façon à ne pas avoir à rappeler le _FQN_ complet à chaque utilisation.
Ainsi au lieu d'écrire ceci :
[source,language="java"]
----
package fr.lteconsulting;
class MaClasse
{
MaClasse()
{
fr.lteconsulting.outils.MonOutil outil;
outil = new fr.lteconsulting.outils.MonOutil();
}
}
----
On pourra _importer_ la classe `fr.lteconsulting.MaClasse` :
[source,language="java"]
----
package fr.lteconsulting;
import fr.lteconsulting.outils.MonOutil;
class MaClasse
{
MaClasse()
{
MonOutil outil;
outil = new MonOutil();
}
}
----
- écrire attribut
- écrire méthode
- constructeur
-- délégation
-- this et super
=== Classes abstraites
Classe dont on ne peut créer d'instance. Il est possible de déclarer des _méthodes abstraites_ dont le corps (l'implémentation) n'est pas fourni. Ces méthodes seront implémentées plus tard dans la hierarchie des sous classes de la classe abstraite.
=== Classes finales
Avec le mot-clé `final` on peut interdier aux autres classes d'hériter d'un classe.
=== Champs et méthodes statiques
Attribut statique:: un attribut partagé par toutes les instances d'une classe.
Attribut d'instance:: chaque objet a SA propre valeur pour cet attribut
=== Héritage
Mot-clé `extends`.
==== Conversion de type (`instanceof` et transtypage)
Le système de typage de Java n'est pas unifié. Il y a les _types objet_ et les _types primitifs_.
=== La classe `Object`
Toutes les classes ont la classe `Object` comme super-classe si elles n'en n'ont pas déjà une autre.
Les méthodes de la classe `Object` sont donc accessibles sur _tous_ les objets que l'on peut rencontrer.
==== `boolean equals( Object other )` et `int hashCode()`
Ces deux méthodes fonctionnent main dans la main et répondent à ces questions :
- `equals` doit retourner `true` si l'objet passé en paramètre peut être considéré comme identique (au sens de l'application) à l'objet sur lequel cette méthode est appelée.
- `hashCode` doit retourner le même résultat pour deux objets considérés identiques (donc retournant `true` si appelés sur `equals`).
IMPORTANT: Il est important que ces deux méthodes soient implémentées de façon à garder leur résultat cohérents. De nombreuses structures de données dépendent de cette cohérence...
==== `String toString()`
Renvoie une représentation textuelle de l'objet sur lequel on appelle cette méthode.
Il est en général utile de réimplémenter cette méthode de façon à obtenir un résultat _humain_ lorsque l'on combine une chaine de caractère avec un objet.
L'implémentation de cette méthode dans la classe `Object` consiste à indiquer un code correspondant au type de l'objet suivi de l'adresse mémoire à laquelle cet objet est stocké.
==== `Class<?> getClass()`
Renvoie un objet représentant la classe de l'objet sur lequel on appelle cette méthode.
=== Annotations introduction
`@Deprecated`, `@Override`, ...
=== Interfaces
- séparer le contrat de l'implémentation
- écrire une interface
- implémenter une interface
==== Définir une interface
Nous définissons ici l'interface `IMonInterface` :
[source,language="java"]
----
public interface IMonInterface {
// le mot clé public est facultatif car implicite
[public] void afficher();
void autreMéthode();
}
----
Et ensuite cette interface est _implémentée_ par la classe `UneClasse`.
[source,language="java"]
----
public class UneClasse implements IMonInterface {
// attributs, méthodes, ...
@Override
public void afficher() {
// .. <1>
}
}
----
<1> Implémentation de la méthode `afficher()`.
*Exercice* : recoder l'exercice du jeu de cartes, en créant une interface `ICarte`, une classe de base `AbstractBaseCarte` qui contient le champ `protected` `cout`. Seules `Sortilege` et `Creature` héritent de `AbstractBaseCarte`. Les trois classes de cartes implémentent `ICarte`. Ceci nous montre comment bien séparer implémentation et contrat d'utilisation.
==== Exemple
Soit le code suivant :
[source,language="java"]
----
interface IContrat {
void commencer();
void terminer();
}
interface ICommencable {
void commencer();
}
interface IMangeable {
void manger();
}
interface IConsommable extends IMangeable {
void digerer();
}
class Base {
void manger() {
System.out.println( "Manger" );
}
}
abstract class ImplPartielle extends Base implements IContrat {
@Override
public abstract void commencer();
@Override
public void terminer() {
System.exit( 0 );
}
}
class Implementation extends ImplPartielle implements IConsommable, IMangeable {
@Override
public void commencer() {
System.out.println( "Implementation::commencer()" );
}
@Override
public void terminer() {
System.out.println( "Terminer" );
super.terminer();
}
}
----
Voici le diagramme de classe correspondant :
image::exemple-interface-uml.jpg[]
=== Les génériques
Qu'est-ce qui peut être _générique_ en java ?
- les classes,
- les interfaces,
- les méthodes.
Type générique:: La classe/interface/méthode dont l'implémentation est écrite sans connaitre à l'avance l'intégralité des types sur lesquels elle s'appuie.
Paramètre de type:: Le nom que l'on donne au type(s) inconnu(s) au sein de l'implémentation d'un type générique.
Type argument:: Le type (réel) qui remplace le paramètre de type au moment de l'utilisation d'une classe/interface/méthode générique.
Contrainte:: Condition(s) que l'on impose au type argument d'un type générique.
==== Classes génériques
Voici un exemple de définition d'une classe générique `Tableau` travaillant sur un paramètre de type `T` :
[source,language="java"]
----
class Tableau<T> { <1>
private T tableau; <2>
Tableau( int taille ) {
tableau = (T[]) new Object[taille]; <3>
}
T get( int index ) { <4>
return tableau[index];
}
void set( int index, T valeur ) {
tableau[index] = valeur;
}
}
----
<1> Définition du paramètre de type `T`.
<2> L'attribut `tableau` est un tableau de `T`.
<3> Ce transtypage est obligatoire. On verra plus tard comment faire mieux.
<4> On retourne un élément de type `T`.
Voici un exemple de code utilisant la classe générique `Tableau` définie juste au dessus :
[source,language="java"]
----
Tableau<String> tableau;
// Syntaxe jusqu'à Java 6
tableau = new Tableau<String>();
// Syntaxe à partir de Java 7
tableau = new Tableau<>();
tableau.set( 4, "Une chaine de caractère" );
----
==== Méthodes génériques
==== Spécifier des contraintes sur le type générique (ex: `<T extends ICarte>`...)
==== Problème de la covariance
*Exercice* : implémenter une classe `TableauDynamique<T>` permettant de stocker un nombre variable d'éléments de type `T`. La classe aura au moins les méthodes `T get(int index)`, `void set(int index, T value)` et `int size()`.
*Exercice* : utiliser la classe `TableauDynamique<ICarte>` dans le projet de cartes...
*Exercice* : refaire l'alogrithme du tri avec les génériques (interface `Triable` plus `<T extends Triable> T[] trier( T[] data )`). Attention, le tri ne fonctionnera plus sur les types primitifs (auto-boxing).
*Exercice* : si on implémente l'algorithme de tri en prenant des données triables (`<T extends Triable>`), par fois la hierarchie des classes ne permet pas de modifier celle-ci. On définit donc une interface `Comparateur<T>` dont on demande une instance dans la nouvelle méthode `trier` : `<T> T[] trier( T[] data, Comparateur<T> comparateur)`. Vérifier que la méthode `trier(...)` a bien été implémentée deux fois. Bien comprendre que la contrainte de type permet de garantir la correspondance entre les types d'entrée et de sortie.
=== Classes imbriquées
Le langage Java permet de créer des classes à l’intérieur d’une autre classe. On parle de classes imbriquées (_nested class_)
[source,language="java"]
----
class A {
private class B{
}
}
----
Les classes imbriquées sont divisées en deux catégories : _statique_ ou _non statique_. On parle de classes statiques imbriquées (_static nested class_) et de classes internes (_inner class_) pour les classes imbriquées non statiques.
Contrairement aux classes classiques qui ne peuvent être définies qu’avec une visibilité `public` ou `package`, les classes imbriquées peuvent être aussi bien `private`, `public`, `protected` ou `package`.
==== Classes internes
Une classe interne peut avoir accès aux méthodes et aux attributs de la classe englobante même si ces derniers sont déclarés en *private*.
[source,language="java"]
----
public class A {
int a = 1;
B b = new B();
C c = new C();
class B {
void test() {
a = a + 1;
}
}
class C {
void test() {
a = a + 1;
b.test();
}
}
}
----
Une classe interne ne peut pas exister si sa classe englobante n’existe pas. Dans l'exemple suivant il est donc nécessaire d'avoir une instance de A pour créer une instance de B :
[source,language="java"]
----
void uneMethode() {
A a = new A(); <1>
B b = a.new B(); <2>
}
----
<1> Création d'une instance de A.
<2> Création d'une instance de B reliée à a.
==== Classes imbriquées statiques
Une classe imbriquée statique se comporte avec la classe englobante comme n’importe quelle autre classe de haut niveau. Elle *ne peut pas* accéder aux attributs non statiques de la class englobante. Autrement dit, elle ne peut accéder *qu'aux membres statiques* de la classe englobante.
Exemple :
[source,language="java"]
----
public class A {
private static int a = 1;
public static class B {
public void test() {
a = a + 1;
}
}
}
----
On peut instancier la classe englobée sans avoir de référence à une instance de la classe englobante.
[source,language="java"]
----
void uneMethode() {
A.B b = new A.B();
b.test();
System.out.println(A.a);
}
----
==== Cas d'utilisation
Les classes imbriquées sont utiles dans plusieurs cas :
- Si une classe n’est utile qu’à une autre classe il est logique de l’intégrer directement dedans. Ceci aide à la lisibilité et la maintenabilité du code.
- Si une classe a besoin d’accéder aux éléments d’une autre classe que l’on souhaite garder avec une visibilité privée, définir cette classe comme classe interne permet de répondre à cette problématique. Ceci améliore l’encapsulation.
=== Classes anonymes
Une classe anonyme est une classe déclarée et instanciée en même temps sans définir de nom (il sera donné par le compilateur). Cette classe ne peut être utilisée qu’à l’endroit où elle a été définie.
Une classe anonyme a accès à tous les éléments accessibles à l'endroit de sa définition.
[source,language="java"]
----
void uneMethode() {
Plateau plateau = new Plateau( 5, 2 );
Piece piece = new Piece() { <1>
@Override
public char getDisplayChar() {
return 'X';
}
}
plateau.placer( piece, new Coordonnee( 2, 2 ) );
}
----
<1> On voit ici la création implicite d'une classe implémentant l'interface `Piece`. Cette classe n'a pas de nom : elle est anonyme.
=== Les exceptions
Mécanisme de gestion des erreurs en Java.
Une *exception* est une instance de la classe (ou sous-classe) `Exception`. Elle représente une *erreur* survenue pendant le déroulement du programme.
Trois mot-clés permettent de détecter et traiter ces erreurs : `try`, `catch` et `finally`.
Lorsqu'une erreur est provoquée, une exception est levée grâce au mot-clé `throw`.
Il est possible d'indiquer qu'une méthode peut potentiellement lever un type d'exception avec le mot-clé `throws`.
==== La hiérarchie des classes d'Exception
La classe mère de toutes les exceptions est la classe `Throwable`.
image::exceptions.png[]
`java.lang.Error`:: C'est sous cette classe que sont regroupées toutes les exceptions associées à des erreurs irrécupérables (par exemple: `OutOfMemoryError`, `StackOverflowError`, ...). Cette classe est réservée à Java, elle est d'ailleurs déclarée `final`, ce qui empêche d'en hériter.
`java.lang.Exception`:: Cette classe est la classe mère de toutes les exceptions _gérées_ (par exemple `FileNotFoundException`). C'est à dire que ces exceptions doivent être déclarées au niveau des signatures des méthodes pouvant les lever (avec le mot-clé `throws`, voir plus loin dans le cours).
`java.lang.RuntimeException`:: Cette classe est la classe mère des exceptions _non gérées_ (par exemple `NullPointerException`), c'est à dire qui peuvent être levées sans être déclarées au préalable dans la signature de la méthode (voir plus loin dans le cours).
==== La classe `Throwable`
La classe `Throwable` possède les caractéristiques suivantes :
- un constructeur par défaut `Throwable()`,
- un constructeur `Throwable( String message )` qui mémorise un message d'erreur,
- une méthode `String getMessage()` qui permet de retrouver le message passé par le constructeur (`null` sinon),
- une méthode `void printStackTrace()` qui écrit dans la sortie standard la pile d'exécution au moment de la levée de l'exception,
- une méthode `void printStackTrace( PrintStream stream )` qui fait la même chose mais dans le flux indiqué en paramètre.
La classe `Throwable` étant la classe mère de *toutes* les exceptions, toutes les exceptions bénéficient de ces fonctionnalités...
==== Détecter et traiter une exception
Si vous exécutez ce code :
[source,language="java"]
----
public class TestException {
public static void main( String[] args ) {
int i = 3;
int j = 0;
System.out.println("résultat = " + (i / j));
}
}
----
Vous provoquerez la levée de l'exception `ArithmeticException` :
----
Exception in thread "main" java.lang.ArithmeticException: by zero
at tests.TestException.main(TestException.java:6)
----
Ce message signifie que l'exception `ArithmeticException` a été levée mais qu'elle n'a été _gérée_ par aucun code de votre programme. L'effet de ceci est donc l'arrêt complet du programme.
Il est possible de gérer les exceptions levées, de reprendre la main sur l'exécution du programme et ainsi de poursuivre les opérations, même après la levée d'une exception. Ceci se fait avec les mot-clés `try`, `catch` et `finally` :
[source,language="java"]
----
try {
// operation risquées;
} catch (ExceptionInteressante e) {
// traitement de l'exception ExceptionInteressante
} catch (Exception e) {
// traitement du cas d'exception général
} finally {
// traitement exécuté dans tous les cas (exception levée ou pas)
}
----
Les instructions situées dans le bloc `try` sont _surveillées_ :
- Si aucune exception n'est levée pendant l'exécution du bloc `try`, l'exécution se poursuit avec le contenu du bloc `finally`.
- Si une exception est levée pendant l'exécution du bloc `try`, l'exécution de celui-ci s'arrête immédiatement et l'exécution se poursuit avec l'exécution du bloc `catch` correspondant le mieux au type de l'exception levée.
Dans tous les cas (exception levée ou pas), le bloc `finally` est exécuté après les autres (même si on fait `return` dans le bloc `try`).
==== Déclarer la potentielle levée d'une exception
Lorsqu'une méthode peut lever une exception *gérée*, celle-ci doit être déclarée au niveau de la signature de la méthode. L'objectif est multiple : avoir une valeur documentaire, préciser au compilateur que cette méthode pourra lever cette exception et obliger à ce que toute méthode qui l'appelle devra prendre en compte cette exception (traitement ou propagation).
Par exemple, cette méthode doit déclarer `throws IOException` :
[source,language="java"]
----
public static String saisir( String message ) throws IOException <2>
{
System.out.println( message );
System.out.print( "> " );
BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) );
return reader.readLine(); <1>
}
----
<1> La méthode `readLine()` peut provoquer l'exception `IOException` qui est une sous-classe de `Exception` (et non de `RuntimeException`).
<2> Ceci oblige à préciser dans le prototype de `saisir(...)` que cette méthode peut lever `IOException`.
Si l'exception potentiellement est gérée par la méthode, alors il n'est plus nécessaire de la déclarer avec `throws` :
[source,language="java"]
----
public static String saisie( String message )
{
System.out.println( message );
System.out.print( "> " );
BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) );
try <1>
{
return reader.readLine();
}
catch( IOException e ) <2>
{
e.printStackTrace(); <3>
return null; <4>
}
}
----
<1> On surveille l'exécution de la méthode `readLine()`...
<2> On définit le comportement à adopter si l'exception `IOException` est levée.
<3> On affiche la pile d'appel ayant provoqué l'exception.
<4> Mais on décide de retourner `null`...
==== Les exceptions personnalisées
Il est évidemment possible de définir des classes d'exception spécifiques à votre application. Celles-ci doivent être des sous-classes de `RuntimeException` (pour les exceptions non gérées) ou de `Exception`.
Exemple :
[source,language="java"]
----
public class SaisieErroneeException extends Exception { <1>
public SaisieErroneeException() {
}
public SaisieErroneeException( String s ) {
super( s );
}
}
public class TestSaisieErroneeException {
public static void controle( String chaine ) throws SaisieErroneeException {
if( "".equals( chaine ) == true )
throw new SaisieErroneeException("Saisie erronee : chaine vide"); <2>
}
public static void main( String[] args ) {
String chaine1 = "";
try {
controle( chaine1 );
} catch ( SaisieErroneeException e ) { <3>
System.out.println( "Saisie erronee: " + e.getMessage() );
}
}
}
----
<1> Nous sommes en train de définir une exception _gérée_.
<2> Le mot clé `throws` sert à lever une exception.
<3> Le `catch` ici permet le traitement spécifique de l'exception `SaisieErroneeException`. Toute autre classe d'exception ne sera pas _attrapée_.
==== Exercices
Vous pouvez utiliser cet http://perso.telecom-paristech.fr/~charon/coursJava/exercices/fact+Exc.html[exercice] pour vous familiariser avec la syntaxe des exceptions.
=== Conventions de nommage
En Java on respecte des conventions de nommage qui permettent une meilleure _lisibilité_ du code et qui apportent une _cohérence_ utile au travail d'équipe.
CamelCase:: Le _camelCase_ est la manière la plus utilisée pour nommer en Java. _CamelCase_ veut dire que chaque première lettre d'un mot prend une majuscule, que tous les mots sont collés les uns aux autres et petite subtitlité, le premier mot ne prend pas de majuscule. Pourquoi _camelCase_ .
==== Les packages
- entièrement en minuscules,
- les caractères autorisés sont alphanumériques (de a à z, de 0 à 9) et peuvent contenir des points (.),
- tout package doit commencer par `com`, `edu`, `gov`, `mil`, `net`, `org` ou les deux lettres identifiant un pays (`fr` correspond à la France, `en` à l'Angleterre etc).
==== Les classes et interfaces
Les classes et interfaces sont nommées selon la méthode _camelCase_ mais commencent pas une majuscule. Il faut aussi éviter les abréviations et essayer d'utiliser des mots qui décrivent bien la classe tout en étant pas trop long.
Exemple : `FenetrePrincipale`, `SelecteurDeFichiers`, etc.
==== Les méthodes
Les méthodes sont nommées selon la méthode -camelCase- et comportent généralement un verbe d'action. Cela donne par exemple : `fermeLaFenetre()`, `donnerDeLArgent()`, `manger()`, etc.
En plus de cela, il faut savoir que dans une classe on retrouve souvent des méthodes de type _getter_ et _setter_ (accesseur, modificateur), pour récupérer une variable de classe ou la modifier sans toucher directement à la variable. Il se nomme généralement `getNomDeLaVariable()` et `setNomDeLaVariable(Object nomDeLaVariable)`. Il existe également d'autres mot-clés fréquemment utilisés comme _add_ et _remove_ (ajout, supprimer) pour ajouter et supprimer quelque chose : `addQuelqueChose(Object quelqueChose)`, `removeTout()`, etc.
==== Les attributs et variables
Les attributs et variables se nomment également en _camelCase_ et en commençant par une lettre (a-z). Le nom d'une variable devrait être court et clair. Les variables à un seul caractère sont à bannir sauf pour un usage temporaire (`i`, `j`, `k`, `l`, `m`, `n` pour les entiers et `c`, `d `, `e` pour les caractères).
Cela donne par exemple : `nombrePopcorn`, `tailleAppartement`, `i`, etc.
==== Les contantes
Les constantes c'est-à-dire les attributs _static final_ doivent être écrites en majuscules et pour séparer les mots les `_` sont à utiliser.
Cela donne par exemple : `LARGEUR_MAX`, `ID_COURANT`, etc.
=== Autres conventions
==== *On n'utilise JAMAIS le package par défaut.*
==== Nommage explicite
Utilisez un nommage explicite pour vos variables, méthodes et classes.
Par exemple `indexListeProduits` vaut bien mieux que `iLP`. Surtout quand vous retournez dans un projet six mois après avoir créé cette variable !
==== Cohérence du nommage
On respecte une cohérence dans le nommage des classes. Par exemple on crée deux classes servlet qui s'appellent : `ListeProduitsServlet` et `AjouterProduitServlet`. On fera attention à rester cohérent et ne pas faire deux classes : `ListeProduitsServlet` et `ServletAjouterProduit`.
En général, dans une équipe on définit au préalable les conventions de nommage à suivre pour les projets. Il faudra donc se conformer à ces conventions, de façon à conserver la cohérence du code.
==== Organisation en package
Les package permettent d'organiser vos classes, utilisez-les ! Dès qu'un package contient trop de classes avec des fonctionnalités variées on pense à les répartir dans des packages différents.
Ceci vous aidera à trouver facilement vos classes (en fonction des fonctionnalités qu'elles implémentent) et à revenir plus facilement sur le projet plus tard.
==== Pas de Warnings !
Les warnings ne sont certes pas des erreurs mais méritent toutefois d'attirer votre attention. Ils sont des indicateurs donnés par le compilateur pour vous aider à éviter les problèmes.
Il faut donc les éliminer, et un bon projet ne doit avoir _aucun_ warning.
Pour les éliminer, on peut :
- soit corriger effectivement la cause de l'apparition du warning,
- soit, dans le cas ou le compilateur émet un warning (du genre _Unchecked cast warning_) mais que vous savez que votre code est correct, vous utiliserez l'annotation `@SuppressWarning` pour signaler au compilateur que vous êtes au courant du problème. Le warning n'apparaîtra plus et vous pourrez vous focaliser sur les warnings suivants.
==== Les outils
Il existe des outils automatiques permettant d'avoir une idée plus ou moins précise de la qualité du code.
Sonar, ...
=== Les collections
NOTE: Le lecteur avide de précisions supplémentaires pourra lire ce http://www.jmdoudoux.fr/java/dej/chap-collections.htm[document] très complet de JM Doudoux.
Les classes de collection représentent des objets capables de gérer un ensemble d'objets.
Plusieurs familles de collections existent, avec une interface pour représenter chacune de ces familles. Chaque interface possède plusieurs implémentations répondant à des contraintes techniques variées. Toutes ces interface se retrouvent soit filles de l'interface `Collection` soit de `Map`.
En général une classe de collection permet au moins de stocker un ensemble d'objets, avec des méthodes pour ajouter d'autres objets, en retirer et parcourir les objets présents dans la collection.
Deux interfaces sont utilisées pour parcourir les collections : `Iterator` et `ListIterator`.
Une interface et une classe pour permettre le tri de certaines collections : `Comparable` et `Comparator`.
Deux classes comportant de nombreuses méthodes d'_outillage_ : `Arrays` et `Collections`.
Certaines de ces collections proposent des fonctionnalités comme la suppression des doublons ou encore la recherche d'éléments. Ces opérations s'appuient en général sur le concept d'égalité entre objets définie par les méthodes `equals(...)` et `hashCode()`. Il est donc d'autant plus important d'implémenter ces méthodes correctement lorqu'on les surcharge.
==== L'interface `Collection<E>`
Cette interface est l'interface de base pour `List<E>` et `Set<E>`. Voici quelques méthodes importantes :
`boolean add( E element )`:: Ajoute un élément à la collection
`boolean addAll( Collection<? extends E> collection )`:: Ajoute les éléments d'une collection dans une autre.
`void clear()`:: Vide la collection de tous ses éléments.
`boolean contains( Object o )`:: Teste la présence d'un élément particulier dans la collection. Les méthodes `equals` et `hashCode` sont utilisées pour rechercher l'élément dans la collection. On parle donc de la présence d'un objet _identique_ au sens _applicatif_.
`equals( Object other )` et `hashCode()`:: Ces méthodes sont redéfinies dans les collections pour refléter l'égalité de l'ensemble des éléments d'une collection.
`int size()`:: Retourne le nombre d'éléments dans la collection.
`boolean isEmpty()`:: Permet de savoir si une collection est vide. A utiliser de préférence au lieu de tester `if( collection.size() == 0 )`...
`Iterator<E> iterator()`:: Renvoie un `Iterator` permettant de parcourir la collection. Cette méthode est issue de l'interface `Iterable` qui est utilisée par la boucle `for( Element e : iterableObject )`.
==== `List<V>`, `ArrayList<V>`, `LinkedList<V>`
Un classe qui implémente l'interface `List<V>` représente un ensemble ordonné d'objets de type _V_. Le contrat de `List<V>` n'interdit pas les doublons.
`ArrayList<V>`:: Implémentation de l'interface `List<V>` grâce à un tableau redimensionnable. Non thread-safe. Autorise l'ajout d'élément `null`. La Javadoc est disponible https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html[ici].
`LinkedList<V>`:: Implémentation de l'interface `List<V>` utilisant une liste (doublement) chaînée.
Voici quelques méthodes très utilisées de `List<V>` : `add( T element )`, `T get( int index )`, `ListIterator<T> listIterator()`, `T remove( int index )`, `T set( int index, T element )`, `List<T> subList( int indexFrom, int indexTo )` (attention ce n'est pas une copie !).
==== `Set<V>`, `HashSet<V>`, `TreeSet<V>`, `LinkedHashSet<V>`
Ensemble d'objet sans doublons. Ne permet pas l'accès direct à un élément de la collection (les éléments ne sont par ordonnés). Il existe une interface `SortedSet<V>` qui interdit les doublons et pour autant ordonne quand même les éléments.