Voici un exemple d’un programme Hello world typique écrit en Java :
class HelloWorld { public static void main(String[] args) { System.out.println("Hello world!"); } }
Le fichier source porte presque toujours le nom de la classe avec l'extension ".java" (ici "HelloWorld.java", ce serait même obligatoire si la classe avait l'attribut public dans sa déclaration — la rendant alors accessible à tout autre programme). On peut compiler puis exécuter cet exemple avec les commandes suivantes (sous Linux) :
javac HelloWorld.java export CLASSPATH=. java HelloWorld
La ligne « export CLASSPATH=. » sert à indiquer à Java qu’il doit également chercher les programmes class dans le répertoire courant. Ce chemin peut également être spécifié au lancement du programme par l’option -classpath (ou -cp en abrégé) :
java -cp . HelloWorld
Structures de contrôle
Les collections d'objets
Il est souvent nécessaire de stocker de nombreuses données dans des collections : liste d’achats, notes des élèves, etc. Les collections peuvent être consultées, modifiées, on peut les trier, les recopier, les supprimer etc. Elles peuvent avoir une taille fixe ou variable.
Collections de taille fixe :
- Les tableaux : Pour initialiser un tableau d’entiers de 10 cases on écrit : int[] tab = new int[10]; Les indices du tableau vont dans ce cas de 0 à 9. Plus généralement, un tableau de taille n, ses indices vont de 0 à n-1. Un des plus grands avantages des tableaux est l’accès en temps constant O(1), à chaque case du tableau. Pour accéder ou modifier le i élément du tableau, pour 0≤ i <n, on écrit tab[i]=2; pour attribuer la valeur 2 à une case du tableau. La propriété length (ex : tab.length) fournit le nombre d'éléments du tableau. Si on essaie de modifier un élément qui n’existe pas, par exemple tab [10] = 4; on va obtenir le message d’erreur suivant : IndexOutOfBoundsException car notre tableau tab ne contient que 10 cases (numérotées de 0 à 9 inclus). Un tableau une fois initialisé ne peut plus être redimensionné.
Collections de taille variable :
-
ArrayList : pour initialiser une ArrayList il faut importer la classe java.util.ArrayList ; et écrire liste = new ArrayList(); depuis jdk 1.5 on a la possibilité d’indiquer le type des éléments contenus dans une ArrayList : entiers, string, etc.
-
Cette collection est vide après l’appel du constructeur ;
-
On peut ajouter autant d’éléments que l’on veut.
Pour ajouter un élément on écrit liste.add(Objet);
Pour accéder à un élément de l’ArrayList : liste.get(index);
Pour connaître le nombre d’éléments que contient une liste : liste.size( );
Pour supprimer un élément : liste.remove(index); les éléments qui suivent l’élément supprimés seront décalés à gauche.
Boucles
Bien qu’elles aient toutes un rôle similaire, chaque boucle est pourtant adaptée à une situation :
- Structure tant que (adaptée pour effectuer des opérations tant qu’une condition n’est pas rencontrée) :
while (<expression booléenne>) { instruction(s) }
- Structure faire … tant que (adaptée pour effectuer des opérations qui demandent le même état d’origine à chaque itération) :
do { instruction(s) } while (<expression booléenne>);
- Structure pour (adaptée lorsqu'une collection doit être parcourue en totalité pour traitement) :
for ( ; ; <expression d’incrémentation>) { instruction(s) }
- Structure pour chaque (simplification du for en for each, dans laquelle l'expression doit être un tableau ou une collection ) :
for (type variable : ) { instruction(s) }
- Structure pour (Java 1.5) :
for (<Objet récupérant l’occurrence suivante de la collection> : <collection d’objets>){ instruction(s) }
Structures conditionnelles
Structure si : condition simple
if (<expression booléenne>) { instruction(s) }
Structure si … sinon : condition avec alternative unique
if (<expression booléenne>) { instruction(s) } else { instruction(s) }
Structure si … ou si … ou si … : condition avec alternatives multiples
if (<expression booléenne>) { instruction(s) } else if (<expression booléenne>) { instruction(s) } else if (<expression booléenne>) { instruction(s) } else { instruction(s) }
Structure atteindre … cas x … cas y …" : embranchement vers un bloc d’instructions énuméré
switch (<expression de type numérique>) { case <constante de type numérique>: instruction(s) break; case <constante de type numérique>: instruction(s) break; [...] default: instruction(s) break; }
Remarques : vous pouvez également utiliser la structure de contrôle switch sur une énumération; par ailleurs le switch ne fonctionne pas sur toutes les constantes de type numérique mais seulement sur les entiers. Switch fonctionne également avec des variables de type char.
La commande break sort immédiatement la boucle en cours (for, while, do), et permet de sortir d’une clause contenue dans un switch. Si le break est omis, l'exécution du switch se poursuit de case en case.
Une expression continue termine l’itération en cours et continue à la prochaine. Elle s’écrit comme suit : continue, mais son usage est à proscrire puisqu'elle tant à favoriser un type de programmation non structurée (programmation spaghetti).
L’énoncé return termine une méthode.
Avec return uneValeur, uneValeur sera renvoyée à la méthode appelante.
Opérateur ternaire ? : : Instruction conditionnelle pouvant être employée comme une expression
<expression booléenne> ? :
Traitement des exceptions
try { instruction(s) } catch ( ) { instruction(s) } [...] finally { instruction(s) }
Le bloc de code finally sera exécuté quel que soit le résultat lorsque le programme sortira du bloc try-catch.
Voici un exemple de capture d’une exception :
FileOutputStream fos = null; try { //Chacune de ces deux instructions peut générer une exception // création d'un flux pour écrire dans un fichier fos = new FileOutputStream(...); // écriture de données dans ce flux fos.write(...); } catch (IOException e) { //Gestion de l'erreur de création ou d'écriture dans le flux e.printStackTrace(); } finally{ //Cette section de code est toujours exécutée, qu’il y ait une exception ou pas // fermeture du flux s'il a été ouvert if(fos != null) fos.close(); }
Cet exemple permet d’illustrer le mécanisme des exceptions en Java. Dans le cas d’une erreur d’entrée/sortie dans le bloc try, l’exécution reprend dans le bloc catch correspondant à cette situation (exception de type IOException). Dans ce bloc catch, la variable e référence l’exception qui s’est produite. Ici, nous invoquons la méthode printStackTrace() qui affiche dans la console des informations sur l’exception qui s’est produite : nom, motif, état de la pile d’appels au moment de la levée de l’exception et, éventuellement, numéro de ligne auquel l’erreur s’est produite. Le bloc finally est ensuite exécuté (ici pour refermer les resources utilisées). Il ne s’agit ici que d’un exemple, l’action à mettre en œuvre lorsqu’une exception survient dépend du fonctionnement général de l’application et de la nature de l’exception.
Types génériques
Un type générique est autrement appelé un Template, il prend un ou plusieurs autres types en arguments. Le type passé en paramètre est déterminé lors de l'instanciation. Cela permet notamment dans le cadre des ArrayList d'éviter les transtypages
public class ArrayList { ArrayList al = new ArrayList(); }
Ces types génériques ne sont utilisés qu'à la compilation, et non directement dans le bytecode.
Différences avec le C : les templates en C dupliquent une classe pour chaque type. Java, au contraire, agit au moment de la compilation comme si on avait dupliqué les classes de ces types intrinsèques mais ne traite en réalité qu'avec une seule classe.
Encodage du source
Les spécifications du langage Java précisent qu’il est formé de caractères au format UTF-16, ce qui permet l’utilisation dans le code source de tous les caractères existant dans le monde :
public class hello_world { private String text = "hello world"; }
Pour assurer la portabilité entre plates-formes, les noms de classes devraient néanmoins être formés uniquement de caractères ASCII.