Si l'on veut lire et écrire, non pas des octets ou des caractères, mais des valeurs d'un type connu, il faut utiliser les classes DataInputStream et DataOutputStream. Ces classes réalisent respectivement les interfaces DataInput et DataOutput qui déclarent des méthodes spécialisées pour divers types de valeurs : les méthodes de lecture readInt, readDouble, readChar, readBoolean, etc., et les méthodes d'écriture write... correspondantes. On bénéficie de ces méthodes supplémentaires en décorant un flot d'octets :
DataOutputStream dos = new DataOutputStream( // décorateur new FileOutputStream("toto")); // délégué ... dos.writeBoolean(true); dos.writeInt(4);
Et inversement, pour la lecture :
DataInputStream dis = new DataInputStream( // décorateur new FileInputStream("toto")); // délégué boolean b = dis.readBoolean(); int n = dis.readInt();
Par exemple, dans le cas d'un tableau d'octets, si l'on sait qu'il contient successivement la représentation d'un booléen et d'un entier, on doit le lire à l'aide des méthodes déclarées dans DataInput. On doit donc ajouter ces fonctionnalités par décoration du flot, à l'aide du constructeur DataInputStream. Par exemple :
byte[] data = ...; DataInputStream dis = new DataInputStream( new ByteArrayInputStream(data)); boolean b = dis.readBoolean(); int n = dis.readInt();
Ceci suppose que ce tableau contienne des données codées de façon compatible avec le décodage réalisé par les méthodes readBoolean, etc. Ce sera le cas, symétriquement, si le tableau d'octets a été obtenu par les classes DataOutputStream et ByteArrayOutputStream :
byte[] data; ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeBoolean(true); dos.writeInt(4); data = baos.toByteArray();