Iut Lannion Département informatique Algorithmique 1ère Année (AP4) 2012-2013 TP 1 L'environnement JAVA Ce premier TP concerne la compilation et l’exécution d’un premier programme écrit en Java et la conception de programmes Java élémentaires. Toute la documentation JAVA se trouve à l’adresse internet http://java.sun.com/j2se/1.5.0/docs/api/ ou bien http://webetu12/DOCAPI/. Vous pouvez saisir le code source d'une application JAVA avec n'importe quel éditeur de texte. La compilation sous l'invite Unix se fait par la commande javac : javac monappli.java Le fichier à compiler doit avoir l'extension .java. Son exécution se fait par la commande java : java monappli Exercice 1 Ecrivez et compilez un programme qui affiche la chaîne de caractères bonjour à l'écran. Quelques explications : 1. Les parties de code situées entre /* et */ sont des commentaires qui ne seront pas compilés. 2. La première instruction public class bonjour déclare que nous créons une classe publique (accessible par tous) qui se nomme bonjour. Cette classe doit être enregistrée dans un fichier de même nom et d'extension .java. Attention, java différencie les majuscules et les minuscules. La classe bonjour est différente de la classe Bonjour, et le fichier bonjour.java n'est pas l'équivalent du fichier Bonjour.java. 3. Java étant un langage orienté objets, tout fichier source définit une classe, c'est à dire un objet possédant des champs et des méthodes. La définition de la classe se fait dans un bloc d'instructions contenu entre 2 accolades. 4. Notre classe bonjour contient une unique méthode annoncée par l'instruction public static void main(String args[]). Il s'agit de la méthode principale d'un programme, celle qui sera exécutée lors du lancement. Le nom et la déclaration de cette méthode ne peuvent pas être modifiés. 5. La définition d'une méthode est une suite d'instructions située entre 2 nouvelles accolades. Dans le cas qui nous intéresse, la méthode main n'a qu'une seule instruction qui provoque l'affichage du message "Bonjour", par l'intermédiaire de la méthode System.out.println. Les lignes contenant une instruction simple comme celle-ci doivent se terminer par un point-virgule. Exercice 2/ Proposez une petite calculatrice se présentant sous la forme d’un menu. Vous utiliserez les méthode de classe de la classe Math pour effectuer les calculs (limitez vous aux calculs d’un cosinus, d’un sinus et d’une tangente : méthodes de classe cos(double a), sin(double a) et tan(double a) dont le résultat est de type primitif double et dont l’argument est la mesure d’un angle en radian). Le menu demande le type d’opération à effectuer (cosinus, sinus ou tangente) puis lit un double au clavier et affiche le résultat de l’opération (tant que l’utilisateur n’a pas quitté le menu). Pour saisir au clavier un type primitif il est nécessaire de mettre la saisie dans une instance de la classe String. Les déclarations sont : String ligne; BufferedReader entree=new BufferedReader (new InputStreamReader (System.in)); Le code pour une saisie est : // saisie d'une ligne System.out.println("Tapez une ligne"); ligne = entree.readLine(); Il faut alors convertir la saisie en type primitif. Voici quelques méthodes nécessaires aux conversions : la méthode de classe parseInt(String str) de la classe Integer a comme argument une instance de la classe String (elle délivre la conversion de cette instance en type primitif int), la méthode de classe valueOf(String str) de la classe Double a comme argument une instance de classe String (elle délivre la conversion de la chaîne en instance de Double), la méthode d’instance doubleValue() délivre la conversion d’une instance de Double en type primitif double, la méthode de classe valueOf(String str) de la classe Float a comme argument une instance de classe String (elle délivre la conversion de la chaîne en instance de Float), la méthode d’instance floatValue() délivre la conversion d’une instance de Float en type primitif float. Saisir des données envoyées par le clavier Si vous voulez lire des données envoyées tout simplement par le clavier, nous vous indiquons deux méthodes illustrées ci-dessous. Les deux programmes d'illustration ont pour objectif de lire des entiers envoyés par l'intermédiaire du clavier, et d'en faire la somme. Leurs cahiers des charges diffèrent uniquement sur la façon d'indiquer la fin de la saisie. Nous notons en rouge les instructions significatives sur le thème de la saisie des données au clavier. Première méthode : utiliser un Bufferedreader On utilise deux classes de java.io, la classe InputStreamReader et la classe BufferedReader. La classe InputStreamReader admet un constructeur InputStreamReader(InputStream), c'est-à-dire un constructeur qui admet en paramètre un flot d'entrée. System.in est une instance de la classe InputStream. Avec une instance de InputStreamReader, on ne peut grosso modo que lire des caractères un à un. La classe BufferedReader a un constructeur qui prend en argument une instance de Reader dont hérite la classe InputStreamReader. Cette classe permet en particulier de lire une ligne d'un texte, mais en revanche, on ne peut pas lui demander de lire un entier, un double etc... On lit ici ce qui est envoyé par le clavier ligne par ligne et on découpe le contenu de chaque ligne avec un StringTokenizer pour récupérer les entiers attendus. L'utilisateur devra taper return deux fois de suite pour interrompre la saisie. Voici le programme que nous proposons. import import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.StringTokenizer; class SaisieClavier { public static void main (String[] argv) throws IOException, NumberFormatException { int somme = 0; String ligne; StringTokenizer st; BufferedReader entree = new BufferedReader (new InputStreamReader(System.in)); ligne = entree.readLine(); while(ligne.length() > 0) { st = new StringTokenizer(ligne); while(st.hasMoreTokens()) somme += Integer.parseInt(st.nextToken()); ligne = entree.readLine(); } System.out.println("La somme vaut : "+somme); } } Voici une execution : $ java SaisieClavier 31 2 Deuxième méthode : utiliser un Scanner Depuis le JDK 5.0 , la saisie à partir de la fenêtre d'exécution est plus simple. La classe java.util.Scanner, qui possède différents constructeurs dont un de type InputStream que nous utilisons dans notre exemple, permet de saisir différents types de données. class SaisieClavierBis { public static void main (String[] arg) { java.util.Scanner entree = new java.util.Scanner(System.in); System.out.println("Donnez votre prénom et votre nom"); String prenom = entree.next(); String nom = entree.next(); System.out.println("Donnez votre âge"); int age = entree.nextInt(); entree.nextLine(); System.out.println("Ecrire votre phrase"); String phrase = entree.nextLine(); System.out.println(prenom + " " + nom + ", " + age + " ans, dit : " + phrase); } } Voici une execution : Donnez votre prénom et votre nom Lou Charon Donnez votre âge 12 Ecrire votre phrase Coucou de Lou Lou Charon, 12 ans, dit : Coucou de Lou Vous pouvez récupérer ce programme. Exercice 3/ Les méthodes que vous avez écrites dans les exercices précédents sont déjà implémentées en Java. Etudiez et testez la class Scanner de la documentation JAVA. Pour saisir au clavier un type primitif il est nécessaire de mettre la saisie dans une instance de la classe String. Les déclarations sont : String ligne; Scanner sc = new Scanner(System.in); Le code pour une saisie est : // saisie d'une ligne System.out.println("Tapez une ligne"); ligne = sc.nextLine(); Au coeur du JDK : la classe Scanner De Olivier Croisier http://thecodersbreakfast.net/index.php?post/2010/03/03/Au-coeur-du-JDK-la-classe-Scanner Une classe injustement méconnue du JDK est Scanner (java.util.Scanner). Elle offre pourtant des fonctionnalités très intéressantes pour parser des chaînes de caractères, et en extraire et convertir les composants. Un Scanner peut se brancher sur à peu près n'importe quelle source : InputStream, Readable (et donc Reader), File... et bien sûr une simple String. Ensuite, deux options s'offrent à vous : utiliser les méthodes de type hasNext...() / next...(), ou alors les méthodes de type find...() / match() / group(). Dans ce billet, nous verrons comment utiliser ces deux jeux d'instructions. Première méthode : hasNext() / next() Voyons une première façon d'utiliser un Scanner pour lire des flux de texte. Elle se décompose en deux étapes : Premièrement, découper la chaîne de caractères en tokens grâce à un délimiteur ; il s'agit par défaut d'un caractère "blanc" (espace, tabulation, retour à la ligne...), mais il est évidemment possible de fournir sa propre expression via la méthode useDelimiter(expression). Ensuite, utiliser les méthodes de type hasNext...() et next...() pour parcourir, récupérer et convertir ces tokens. Les méthodes de type hasNext...() (hasNextInt(), hasNextFloat()...) fonctionnent sur le même principe qu'un Iterator, et indiquent si le prochain token existe et s'il est bien du type spécifié. Une fois cette vérification effectuée, les méthodes de la forme next...() (nextInt(), nextFloat()...) permettent de récupérer ledit token, directement converti dans le type adéquat. A noter qu'existent également les méthodes simples hasNext() et next(), qui permettent de savoir s'il existe un token (de n'importe quel type) et de le récupérer sous forme de String. A l'aide de ces méthodes, il est très facile de parser une chaîne dont vous maîtrisez parfaitement le format, par exemple un fichier .csv : 1. String s = 2. "Dalton;Joe;1.4\n" + 3. "Dalton;Jack;1.6\n" + 4. "Dalton;William;1.8\n" + 5. "Dalton;Averell;2.0"; 6. Scanner scan = new Scanner(s); 7. scan.useDelimiter(";|\n"); 8. scan.useLocale(Locale.US); // Pour les floats 9. 10. 11. 12. while(scan.hasNextLine()) { System.out.printf("%2$s %1$s : %3$.1f m %n", scan.next(), scan.next(), scan.nextFloat()); } Joe Dalton : 1,4 m Jack Dalton : 1,6 m William Dalton : 1,8 m Averell Dalton : 2,0 m Si au contraire le format de la chaîne vous est inconnu, par exemple si elle est saisie par l'utilisateur, il est plus prudent de recourir au pattern suivant, pour éviter les erreurs de conversion de type (InputMismatchException) : 1. Scanner scan = new Scanner(System.in); 2. System.out.println("Entrez un nombre entier à incrémenter :"); 3. // Existe-t-il un token ? 4. while(scan.hasNext()) { 5. // Est-ce un int ? On l'incrémente 6. if (scan.hasNextInt()) { 7. int nb = scan.nextInt(); 8. System.out.println(nb + " +1 = " + ++nb); 9. } 10. // Ce n'est pas un int, affichage d'un message d'erreur. 11. else { 12. System.out.println(scan.next() + " n'est pas un nombre."); 13. } 14. } 15. scan.close(); Notez que la classe Scanner simplifie beaucoup votre code, car vous n'avez plus à convertir manuellement les tokens ni à gérer des exceptions de bas niveau. A titre de comparaison, voici le code effectuant le même traitement sans utiliser la classe Scanner : 1. BufferedReader reader = null; 2. String line = null; 3. try { 4. reader = new BufferedReader(new InputStreamReader(System.in)); 5. while ((line=reader.readLine()) != null) { 6. try { 7. System.out.println(Integer.parseInt(line)+1); 8. } catch (NumberFormatException e) { 9. System.out.println(line + " n'est pas un nombre."); 10. } 11. 12. } } catch (IOException e) { 13. 14. e.printStackTrace(); } finally { 15. if (reader!=null) { 16. try { 17. reader.close(); 18. } catch (IOException e) { 19. } 20. 21. } } Même pour une simple lecture de fichier ligne par ligne, Scanner remplace avantageusement le traditionnel BufferedReader : 1. 2. public static void readFileWithBufferedReader(String fileName) { BufferedReader reader = null; 3. try { 4. reader = new BufferedReader(new FileReader(fileName)); 5. String line = null; 6. while ((line=reader.readLine())!=null) { 7. System.out.println(line); 8. } 9. } catch (FileNotFoundException e) { 10. e.printStackTrace(); 11. } catch (IOException e) { 12. e.printStackTrace(); 13. } finally { 14. if (reader!=null) { 15. try { 16. reader.close(); 17. } catch (IOException e) { 18. } 19. } 20. 21. } } 22. 23. public static void readFileWithScanner(String fileName) { 24. Scanner scan = null; 25. try { 26. scan = new Scanner(new File(fileName)); 27. while(scan.hasNextLine()) { 28. System.out.println(scan.nextLine()); 29. 30. } } catch (FileNotFoundException e) { 31. 32. 33. e.printStackTrace(); } finally { if (scan!=null) scan.close(); 34. 35. } } -------------------------