1. Les objets de type char sont en fait des entiers représentables sur seize bits (de 0 à 216−1). Ces entiers sont en quelque sorte les « codes » des caractères selon une convention mondiale.

    Le « -1 » que peut renvoyer read n'est pas un caractère, il indique au contraire qu'il n'y a plus de caractères.

    La méthode read renvoie un entier car c'est un bon moyen de pouvoir renvoyer à la fois les 216 caractères possibles, et « autre chose » dans le cas où le flux est fini.

  2. static void dump(Reader fr) throws IOException { // Hum numéro un int c ; while ((c = fr.read()) != -1) { // Hum numéro deux System.out.print((char)c) ; // Hum numéro trois } }
    Les hums :
    Un
    Le corps de dump peut lever l'exception IOException car cette méthode contient un appel à fr.read. Il faut déclarer ce fait (noter bien throws avec un « s »).
    Deux
    Le test est pour le moins étrange. Il se comprend quand on sait qu'une affectation renvoie la valeur affectée. Ce style est à éviter sauf dans des tournures idiomatiques du langage de programmation.
    int c ; c = fr.read() ; while (c != -1) { System.out.print((char)c) ; // Hum numéro trois c = fr.read() ; } }
    Un autre emploi courant de la valeur de retour de l'affectation est :
    x = y = z = 0 ; // À comprendre comme x = (y = (z = 0)) ;
    Trois
    La variable c contient un int. Si on le passe directement à la méthode print (de l'objet System.out), alors on affiche cet entier. Il faut bien dire que l'entier est ici vu comme un caractère, d'ou la construction (char)c que l'on appelle un type cast (conversion de type).
  3. A priori on peut construire un flux de caractères à partir de tout ce qui peut être vu comme une suite de caractères les principaux exemples sont les fichiers (quoique, les fichiers sont plutôt des suites d'octets) et les chaînes. Mais il y a aussi les tableaux de caractères (anecdotique) et les flux d'octets par exemple, combinables selon un encodage pour former des caractères.

    On pourrait donc penser qu'il existe divers consructeurs de la classe Reader prenant en argument une chaîne, un nom de fichier etc. En fait il n'en est rien, la classe Reader n'a pas de constructeur. Elle sert non pas à créer des objets mais à créer d'autre classes par héritage. Des sous-classes sont définies comme héritant de Reader (et donc, entre autres, comme possédant une méthode read).

    Pour ce qui nous concerne, cela revient un peu à considérer une interface Reader et diverses classes FileReader, StringReader etc. qui l'implémentent (mais le mécanisme est en fait plus puissant).

    À titre d'exemple voici un petit programme qui affiche le contenu d'un fichier dont le nom est donné comme premier argument de la ligne de commande :

    import java.io.* ; class Cat { static void dump(Reader fr) throws IOException { // Hum numéro un int c ; while ((c = fr.read()) != -1) { // Hum numéro deux System.out.print((char)c) ; // Hum numéro trois } } public static void main(String [] argv) { try { Reader fr = new FileReader(argv[0]) ; dump(fr) ; fr.close() ; } catch ( IOException e ) { System.err.println("Erreur d'entrée sortie : " + e.getMessage()) ; } catch (ArrayIndexOutOfBoundsException e) { System.err.println ("Mauvais nombre d'arguments"); } } }

    Notez d'abord le import java.io.* ;. Cette incantation est nécessaire pour accéder à la classe Reader.

    Notez que le flux est crée comme un FileReader et rangé dans une variable de type Reader. C'est possible car, par héritage, un FileReader est au moins un Reader (ici, il a les méthodes qui nous intéressent avec les bonnes signatures).

    Notez aussi la fermeture de fr (par la méthode close). Cela permet de dire au système Java de récupérer les ressources mobilisées lors de la création du flux, et les rend libres pour la création de nouveaux flux (ici c'est un peu inutile). Il faut en effet savoir qu'un programme donné (Unix, ici la machine Java) ne peut posséder qu'un nombre limité de fichiers ouverts simultanéement.