Les méthodes read() et write() sont les plus primitives. Il est généralement nécessaire, tant par commodité que par efficacité, de recourir à d'autres méthodes.
Par exemple, pour imprimer une représentation textuelle d'une valeur quelconque sur un flot d'octets, on invoque print() et println(), qui ne sont définies que dans PrintStream, qui est une sous-classe de OutputStream. Si l'on veut imprimer ces représentations dans un fichier, on ne peut pas utiliser simplement FileOutputStream car cette classe est dépourvue de ces méthodes d'impression. Comment faire pour disposer à la fois des fonctionnalités de PrintStream et de FileOutputStream ? Il n'est pas possible, en Java, de définir une classe qui soit sous-classe directe de deux classes : le mécanisme d'extension, souvent utilisé pour ajouter des fonctionnalités à une classe est ici insuffisant. On peut par contre procéder par délégation.
On ajoute les fonctionnalités offertes par PrintStream , en faisant d'une instance de FileOutputStream une instance de PrintStream :
PrintStream out = new PrintStream( new FileOutputStream("out.txt"), true); out.println(2); out.println(new Integer(2));
L'argument true permet de vider automatiquement le tampon d'écriture à la fin des lignes.
Ce mécanisme est très courant parmi les classes du paquet java.io : le constructeur prend en argument un flot et lui ajoute des fonctionnalités. Il s'agit d'un pattern dit de décoration , également très employé dans les classes graphiques (par exemple, pour décorer une fenêtre).
Les classes suivantes, sous-classes de FilterOutputStream, elle-même sous-classe de OutputStream, ont toutes un constructeur qui prend en argument un OutputStream et lui ajoutent des fonctionnalités.
L'exemple suivant, qui empile trois constructeurs, permet d'ajouter successivement un tampon d'écriture et les méthodes d'impression textuelle à un flot d'écriture sur fichier :
PrintStream out = new PrintStream( new BufferedOutputStream( new FileOutputStream("out.txt"))); out.println(2); out.println(new Integer(2));