Les bases #
Commentaires #
Un commentaire peut s’écrire sur une ou plusieurs lignes. N’abusez jamais des commentaires contrairement à ce que l’on vous a peut-être appris par le passé. Un bon commentaire est un commentaire qui n’existe pas. Le commentaire sert à expliquer une partie du code qui n’est pas compréhensible. Si tel est le cas, envisagez de rendre votre code plus lisible et simple à comprendre plutôt que de devoir l’annoter. Un code simple est plus facile à maintenir. On dit généralement qu’un code est écrit une fois et qu’il est lu une vingtaine de fois.
|
|
Exemple de commentaires inutiles:
|
|
La documentation sur les fonctionnalités publiques d’une API est par contre importante.
Convention de nommage pour identifiants #
De la même manière, un identifiant doit être explicite et signifier son intention. La notation se fait en camelCase
(par. ex: value
, globalTax
, veryBigVariableName
, …)
Deux règles d’or pour un identifiant :
- Plus la visibilité d’une variable est grande, plus le nom doit être explicite.
- Un identifiant doit être prononçable
- La déclaration se fait le plus près possible de son utilisation
Exemples de bon ou mauvais identifiants:
|
|
Structure simple d’un programme #
|
|
Compilation et exécution:
|
|
Voici quelques explications sur la syntaxe. Nous aborderons les méthodes statiques et la création de classes dans une prochain chapitre.
- public class HelloWorld: le programme principal est une classe à visibilité publique
- méthode main :
- code du programme principal
- static: n’est pas liée à un objet
- void: ne retourne rien
- String[] args: permet de récupérer les arguments
Java sans faire de programmation orientée objet #
Avant d’aborder le paradigme, nous pouvons présenter comment utiliser des fonctions sans faire de la programmation orientée objet.
Reprenons le même fichier, mais ajoutons des fonctions classiques. Une fonction qui n’est pas rattachée à un objet est dite “statique”.
|
|
Les types primitifs #
Voici un tableau de tous les types primitifs. Un type primitif est stocké sur la pile (référence d’un article: Que sont la pile et le tas )
désignation | type |
---|---|
nombres entiers | byte , short , int , et long |
nombres réels | float et double |
caractère | char |
booléen | boolean |
Un type primitif commence par une minuscule. Chaque type primitif a son équivalent en classe (Byte
, Integer
, Double
…) que nous découvrirons plus tard.
Conseil
Privilégiez toujours les types primitifs. Ils sont stockés sur la pile. Les objets sont stockés sur le tas, ce qui est beaucoup moins performant.
Voici un tableau détaillant la plage de valeur des différents types (source wikibooks ):
Exemples de déclaration de variable avec le int
:
|
|
Seules les lignes 2 et 3 sont recommandées dans ce cours
Les types entiers #
Le tableau ci-dessous indique la taille réservée pour stocker les différents types d’entiers.
type | octets |
---|---|
byte |
1 |
short |
2 |
int |
4 |
long |
8 |
Le nombre de valeurs possibles est donc limité à la taille réservée. Par exemple, le type byte
comprend les valeurs comprises entre -128 et 127.
L’arithmétique est dite modulaire lorsqu’un dépassement de capacité de la valeur maximum génère un débordement vers la valeur minimum. L’ensemble des valeurs forme un cycle. (La classe utilitaire Math
propose plusieurs fonctionnalités pour traiter ce problème).
|
|
Deux types différents peuvent être compatibles. Java propose un mécanisme de conversion implicite s’il ne peut y avoir de perte de précision. Un byte
peut être converti implicitement en short
, mais la conversion implicite inverse est impossible.
Quelques règles de conversions importantes sur les entiers #
- Un littéral entier est de type int.
- Un littéral est une valeur donnée explicitement dans le code, par exemple:
int i = 3; // 3 est un littéral
.
- Un littéral est une valeur donnée explicitement dans le code, par exemple:
- Lors d’une affectation1, le compilateur refuse tout littéral entier dont la valeur n’appartient pas au domaine de définition du type de la variable.
|
|
- Pour convertir un littéral en
long
, il est nécessaire de le post-fixer par l ou L.
|
|
- Promotion numérique: la promotion numérique est une conversion automatique qui a lieu sur des opérateurs de type entier:
- la valeur d’une expression entière (comme l’addition de deux opérandes) est de type int
- dans une expression, les opérandes de types byte ou short sont convertis en int
- corolaire: une division de deux entiers retourne un entier !
|
|
La deuxième règle concerne l’affectation et non l’invocation d’une fonction/méthode. L’extrait ci-dessous n’est pas équivalent :
|
|
|
|
Nous verrons plus tard qu’il est possible de surcharger une fonction ; ce qui permet d’avoir des fonctions qui ont le même nom, mais des types ou un nombre d’arguments qui diffèrent. Dans de tels cas, le compilateur fixe le type pour déterminer la fonction candidate à invoquer.
Les types réels #
Les types réels font partie du standard IEEE 754 floating point . Le tableau ci-dessous indique la taille réservée pour stocker les différents types de réels:
type | octets |
---|---|
float |
4 |
double |
8 |
Quelques règles #
- Littéral réel est de type double
- Le compilateur refuse de l’attribuer à un float
- Conversion d’un double en float: post-fixer le littéral de f ou F
- Pas de promotion numérique: i. une opération sur deux floats retourne un float
Exemple d’assignation de réels:
|
|
Le standard permet les valeurs particulières NaN
, Infinity
et -Infinity
:
|
|
Danger
Etant donné que le nombre de bits est limité pour tenter de représenter toutes les valeurs réelles possibles, certains nombres ne peuvent être constitué. C’est ce que l’on appelle les problèmes de précisions sur les virgules flottantes.
1 2 3 4 5
jshell> 0.1 * 3 == 0.3 $9 ==> false jshell> 0.1 * 3 $10 ==> 0.30000000000000004 // problème de précision
Evitez-les quand vous pouvez et lorsque la précision est importante. Pour représenter des nombres monétaires, vous pouvez par exemple employer des entiers et travailler en centimes. Vous pouvez également utiliser la technique du seuil de comparaison à l’aide de la valeur absolue de leur soustraction:
|
|
Une autre solution consiste à utiliser une précision arbitraire à l’aide de la classe BigDecimal
. Une précision arbitraire permet de représenter des nombres tant que la place mémoire le permet (cf: fr.qwe.wiki
).
|
|
Le type caractère #
Le type char
occupe 2 octets pour stocker un caractère. Il utilise le standard Unicode. Les guillemets simples sont utilisés pour ce type alors que les guillemets doubles sont réservés pour les chaînes de caractères (classe String
). Un caractère est interprété comme un entier non signé. Il respecte donc la promotion numérique.
|
|
Un objet de type String permet d’instancier une chaîne de caractères: String s = "Hello";
. Nous verrons cette classe plus tard.
Le type booléen #
Ce type occupe 1 octet et permet deux valeurs possibles (true
et false
). Il n’y a pas de conversion implicite d’un autre type vers un boolean
comme en C
, JavaScript
ou Python
.
|
|
Compatibilité et équivalence de types primitifs #
Les types sont non équivalents entre eux : leur espace mémoire diffère, il existe des types signés et non signés… Par contre, en Java (contrairement à Rust
ou Ada
), ils sont comparables, voire compatibles. C’est-à-dire qu’il y a une conversion implicite lors d’une expression impliquant des types différents pour autant qu’il n’y ait pas de perte de magnitude (et non une perte de précision). Si une perte de magnitude est possible, le compilateur refuse l’expression. Il est alors obligatoire de réaliser une conversion explicite à l’aide d’un cast au risque de perdre de l’information.
Il n’y a pas de perte de magnitude si la valeur source se situera dans la fourchette de valeurs du type cible. Par contre, une perte de précision est possible! Autrement dit, si le type T1 est compris dans l’ensemble de définition de T2 alors T1 peut être converti implicitement en T2.
Conversions implicites #
Règles de conversions implicites:
- une conversion implicite est légale s’il n’y a pas de perte de précision (conversion non dégradante)
- conversion possible selon la hiérarchie suivante :
|
|
Les conversions implicites short -> char
ou char -> short
sont impossibles: ils ont la même taille, mais le short
est signé, alors que le char
ne l’est pas !
Exemples:
|
|
Perte de précision sans perte de magnitude #
|
|
Récapitulation des conversions implicites #
Voici un résumé des deux types de conversions implicites principales:
- promotion numérique
- concerne les opérateurs (addition, soustraction…) sur les entiers
- les opérandes de type byte, short, char sont convertis en int
- le résultat est toujours de type int
- ajustement de types
- si le type T1 est compris dans l’ensemble de définition de T2 alors T1 peut être converti implicitement en T2
- par ex:
int
verslong
- hiérarchie des conversions
byte
->short
->int
->long
->float
->double
char
->int
->long
->float
->double
Exemple illustrant une expression en raisonnant sur les types:
|
|
Conversions explicites (cast) #
Exemple de conversion explicite
|
|
Quiz #
Quelles sont les valeurs de n et de p ?
|
|
Chaîne de caractères #
La chaîne de caractères String
n’est pas un type primitif, mais une classe. Vous pouvez l’utiliser sans connaître le concept de classes/objets pour l’instant:
|
|
Inférence des types #
L’inférence des types est possible depuis Java 10 avec le mot-clé var.
|
|
Non-attribution de valeur #
Une valeur arbitraire “neutre” est attribuée par défaut si aucune valeur n’est affectée explicitement à une déclaration de variable
0
pour les nombres entiers0.0
pour les nombres réelsfalse
pour les booléens''
pour les caractèresnull
pour les objets
Déclaration de constantes #
Conseil
Une bonne pratique veut que toute référence non modifiée soit une constante. Ou plutôt, que toute référence devrait être constante par défaut, sauf s’il est vraiment nécessaire de la changer.
Une constante se déclare en précédant le type par le mot clé final
. En programmation contemporaine, la constante est très largement utilisée et il ne devient pas nécessaire d’écrire l’identifiant en majuscule si sa visibilité est courte.
|
|
Nous verrons que le mot-clé final
peut être utilisé dans d’autre contexte.
-
attribution d’une valeur à une variable ↩︎