L'histoire informatique montre un perpétuel
et cyclique balancement entre abstraction /simplicité et
implémentation détaillée/complexité.
L'API java.io avait comme avantage, par rapport à l'existant,
de réaliser des entrées/sorties sans se souvier
des détails techniques : quel type de flot (fichier, socket,
pipe, ...), gestion des tampons, .....
L'API java.nio définit une hiérarchie de classe
pour gérer finement des tampons.
ajouter/obtenir des
bytes dans un tampon
Source de BufferNio1.java
import java.io.*;
import java.nio.*;
public class BufferNio1
{
static public void main( String args[] ) throws Exception {
ByteBuffer buffer = ByteBuffer.allocate( 10 );
buffer.put( (byte)'a' );
buffer.put( (byte)'1' );
buffer.put( (byte)'b' );
buffer.flip();
System.out.println( (char)buffer.get() );
System.out.println( (char)buffer.get() );
System.out.println( (char)buffer.get() );
}
}
|
EXECUTION
Les lignes de la classe :
- On écrit/ajoute 3 bytes dans un
tampon et on les ré-obtient :
- pour vérifier plus aisément,
ce sont des bytes(octets) obtenus à partir de caractères
de code < 128 qui ne posent pas de problème avec l'encodage
Unicode de Java et l'encodage en byte (8 bits)
- ByteBuffer
- est la classe des tampons de byte/octet
: un tampon est un objet "file" à capacité
limité
- elle n'a aucun constructeur public :
- la méthode static allocate(quantité)
permet d'obtenir un tampon
- la méthode static allocateDirect(quantité)
essaye d'obtenir un tampon plus efficace dans la mémoire
disponible du système d'exploitation sur lequel tourne
l'application Java.
- la méthode put(byte) ajoute/append
un octet dans le tampon
- A chaque écriture/ajout, un "pointeur"
d'écriture avance
- elle peut lever une BufferOverflowException
s'il n'y a plus de place dans le tampon
- la méthode get() lit et
retourne un byte du tampon
- A chaque lecture, un "pointeur"
de lecture avance
- elle peut lever une BufferOUnderflowException
s'il n'y a plus de byte à lire dans le tampon
- la méthode flip() "rembobine"
la tampon après une séquence d'écriture
pour être prêt à une séquence de lecture.
"parcourir"
un tampon IntBuffer
Source de BufferNio2.java
import java.io.*;
import java.nio.*;
public class BufferNio2
{
static public void main( String args[] ) throws Exception {
IntBuffer buffer = IntBuffer.allocate( 10 );
for (int i=0; i<buffer.capacity(); ++i)
buffer.put(i);
buffer.flip();
while (buffer.hasRemaining()) {
int j = buffer.get();
System.out.println( j );
}
}
}
|
EXECUTION
$java BufferNio2
0
1
2
3
4
5
6
7
8
9
|
Les lignes de la classe :
- On utilise içi un tampon d'entiers
.
- On écrit/ajoute directement des
entiers dans le tampon et on les y relit .
- IntBuffer
- est la classe des tampons d'entiers byte/octet
- hérite de la classe ByteBuffer
- la méthode static allocate(quantité)
redéfinit l'allocation du tampon en nombre d'entiers à
accueillir au maximun.
- la méthode put(int) ajoute/append
un entier dans le tampon
- la méthode get() lit et
retourne un entier du tampon
- Buffer
- est la superclasse abstraite de toutes
les classes "tampon"
- la méthode capacity() retourne
la taille maximale allouée du tampon à sa création.
- la méthode hasRemaining()
retourne true si le "pointeur" de séquence de
lecture ou écriture n'est pas arrivé au bout du
tampon
La "mécanique"
des tampons : capacity, limit, position, ...
Source de BufferNio3.java
import java.io.*;
import java.nio.*;
public class BufferNio3
{
static public void main( String args[] ) throws Exception {
DoubleBuffer buffer = DoubleBuffer.allocate( 10 );
System.out.println(" buffer capacity = "+buffer.capacity() );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.put( 12.3 );
buffer.put( 4.5 );
System.out.println("put 12.3 et put 4.5");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.put( 0.678 );
System.out.println("put 0.678");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.flip();
System.out.println("flip");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
System.out.println( "get : "+ buffer.get() );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
System.out.println( "get : "+ buffer.get() );
System.out.println( "get : "+ buffer.get() );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.rewind();
System.out.println("rewind");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
System.out.println( "get : "+ buffer.get() );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
System.out.println( "get : "+ buffer.get() );
System.out.println( "get : "+ buffer.get() );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
}
}
|
EXECUTION
$java BufferNio3
buffer capacity = 10
buffer position = 0 limit = 10
put 12.3 et put 4.5
buffer position = 2 limit = 10
put 0.678
buffer position = 3 limit = 10
flip
buffer position = 0 limit = 3
get : 12.3
buffer position = 1 limit = 3
get : 4.5
get : 0.678
buffer position = 3 limit = 3
rewind
buffer position = 0 limit = 3
get : 12.3
buffer position = 1 limit = 3
get : 4.5
get : 0.678
buffer position = 3 limit = 3
|
Les lignes de la classe :
- la classe DoubleBuffer permet de
manipuler des tampons de double
- la position d'un Buffer est l'index de
l'endroit où sera écrite ou lue la prochaine donnée.
- la méthode position() retourne
cette valeur
- dans une séquence d'écriture,
position "pointe la première case libre/vide"
- dans une séquence de lecture, position
"pointe la case à lire"
- la limite d'un Buffer est la limite maximale
pour le pointeur "position"
- la méthode limit() retourne
cette valeur
- dans une séquence de lecture, limit
"pointe la première case sans donnée"
- dans une séquence d'écriture,
position "pointe juste après la dernière case
libre/vide" :
en général limit = capacity
$java BufferNio3
buffer capacity = 10
buffer position = 0 limit = 10
|

put 12.3 et put 4.5
buffer position = 2 limit = 10
|

put 0.678
buffer position = 3 limit = 10
|

flip
buffer position = 0 limit = 3
|

get : 12.3
buffer position = 1 limit = 3
|

get : 4.5
get : 0.678
buffer position = 3 limit = 3
|

rewind
buffer position = 0 limit = 3
|

get : 12.3
buffer position = 1 limit = 3
|
- la méthode flip(), en général,
"bascule" du mode écriture vers le mode lecture
limit <-- position ;
position <-- 0;
|
- la méthode rewind(), en
général, "recommence" une séquence
de lecture après une séquence de lecture
- les méthodes get() et put()
incrémente position
La "mécanique"
des tampons : clear, dépassement, ...
Source de BufferNio4.java
import java.io.*;
import java.nio.*;
public class BufferNio4
{
static public void main( String args[] ) throws Exception {
CharBuffer buffer = CharBuffer.allocate( 10 );
System.out.println(" buffer capacity = "+buffer.capacity() );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.put( "abc 3.4 e" );
System.out.println(" put( \"abc 3.4 e\")");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.flip();
System.out.println("flip");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
char[] tabChar = new char[5];
buffer.get(tabChar);
String CinqChars = new String(tabChar);
System.out.println( "get(tableau de 5 char) : "+ CinqChars);
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
while (buffer.hasRemaining())
buffer.get();
System.out.println( "buffer.hasRemaining() faux " );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.clear();
System.out.println( "buffer clear " );
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.put( "xyz" );
System.out.println(" put( \"xyz\")");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
buffer.flip();
System.out.println("flip");
System.out.println(" buffer position = "+buffer.position()
+" limit = "+buffer.limit());
System.out.println( "essaye get(tableau de 5 char) ");
buffer.get(tabChar);
}
}
|
EXECUTION
$java BufferNio4
buffer capacity = 10
buffer position = 0 limit = 10
put( "abc 3.4 e")
buffer position = 9 limit = 10
flip
buffer position = 0 limit = 9
get(tableau de 5 char) : abc 3
buffer position = 5 limit = 9
buffer.hasRemaining() faux
buffer position = 9 limit = 9
buffer clear
buffer position = 0 limit = 10
put( "xyz")
buffer position = 3 limit = 10
flip
buffer position = 0 limit = 3
essaye get(tableau de 5 char)
Exception in thread "main" java.nio.BufferUnderflowException
at java.nio.HeapCharBuffer.get(HeapCharBuffer.java:127)
at java.nio.CharBuffer.get(CharBuffer.java:638)
at BufferNio4.main(BufferNio4.java:48)
|
Les lignes de la classe :
- la classe CharBuffer permet de
manipuler des tampons de caractères
- la méthode clear(), en général,
"bascule" vers le mode écriture
limit <-- capacity ;
position <-- 0;
|
- la méthode get() a parmi
ses nombreuses signatures :
- get(char[]) pour lire un tableau de char
à partir du CharBuffer
elle est susceptible de lever une BufferUnderflowException s'il
n'y a plus suffisament de char à lire dans le tampon
- la méthode put(String)
- permet d'écrire plusieurs "char"
dand un CharBuffer
- elle est susceptible de lever une BufferOverflowException
s'il n'y a plus suffisament de place libre dans le tampon
les méthodes
Put<Type> et get<Type>
Source de BufferNio7.java
import java.io.*;
import java.nio.*;
public class BufferNio7
{
static public void main( String args[] ) throws Exception {
ByteBuffer buffer = ByteBuffer.allocate( 100 );
System.out.println("put un char puis int puis long puis double");
buffer.putChar('a');
buffer.putInt(12345);
buffer.putLong(88000000000L );
buffer.putDouble(9.8765);
buffer.flip();
System.out.println( buffer.getChar() );
System.out.println( buffer.getInt() );
System.out.println( buffer.getLong() );
System.out.println( buffer.getDouble() );
buffer.rewind();
System.out.println("entier : "+buffer.getInt() );
System.out.println("char : "+buffer.getChar() );
for (int i=0; i<50; i++)
buffer.getDouble();
}
}
|
EXECUTION
$java BufferNio7
put un char puis int puis long puis double
a
12345
88000000000
9.8765
entier : 1627390000
char : ?
Exception in thread "main" java.nio.BufferOverflowException
at java.nio.Buffer.nextPutIndex(Unknown Source)
at java.nio.HeapByteBuffer.putDouble(Unknown Source)
at BufferNio7.main(BufferNio7.java:22)
|
Les lignes de la classe :
- la classe ByteBuffer posséde des
méthodes de lecture et d'écriture pour tous les
types primitifs
- get<type>()
- put<type>(valeur)
- par contre, il n'y a aucune indication
des types stockés :
- il faut écrire puis lire les mêmes
types !
- dans un buffer, l'encodage d'un type primitif
se fait par défaut en ByteOrder.BIG_ENDIAN (par
le "gros bout") : de l'octet le plus significatif vers
le moins.
- la classe ByteOrder permet de modifier
cet encodage.
- get<type>() est susceptible de lever
une BufferOverflowException s'il n'y a plus suffisament de données
à lire dans le tampon
adressage
absolu et relatif du Buffer
Source de BufferNio8.java
import java.io.*;
import java.nio.*;
public class BufferNio8
{
static public void main( String args[] ) throws Exception {
CharBuffer carBuf = CharBuffer.wrap("abcdefghijklmnopqrstuvwxyz");
System.out.println("wrap : "+carBuf);
String s = carBuf.toString();
System.out.println("toString : "+s);
System.out.println("get(2) : "+(char)(carBuf.get(2)) );
System.out.println("position() : "+carBuf.position() );
System.out.println("get() : "+(char)(carBuf.get()) );
System.out.println("position() : "+carBuf.position() );
System.out.println("get() : "+(char)(carBuf.get()) );
System.out.println("position() : "+carBuf.position() );
System.out.println("charAt(0) : "+carBuf.charAt(0) );
System.out.println("position() : "+carBuf.position() );
System.out.println("toString : "+carBuf.toString());
}
}
|
EXECUTION
$ java BufferNio8
wrap : abcdefghijklmnopqrstuvwxyz
toString : abcdefghijklmnopqrstuvwxyz
get(2) : c
position() : 0
get() : a
position() : 1
get() : b
position() : 2
charAt(0) : c
position() : 2
toString : cdefghijklmnopqrstuvwxyz
|
Les lignes de la classe :
- la méthode static wrap(objet_multiple)
fournit un objet ByteBuffer ou d'une de ses sous-classes :
- le résultat est une instance de
ByteBuffer ou CharBuffer qui utilise l'objet_multiple comme tampon
- ByteBuffer.wrap(byte[])
- ByteBuffer.wrap(byte[], position_debut,
quantité)
- CharBuffer.wrap(String)
- les méthodes get et put utilisent
deux modes d'adressage :
- relatif :
les méthodes simples get et put et leurs équivalents
get<type> et put<type> incrémentent position
de 1
- absolu
:
les méthodes simples get(index) et put(valeur, index)
lisent et écrivent de manière absolue à
l'index indiqué, sans modifier position
- La méthode toString() de
CharBuffer donne sous forme de String ce qui est écrit
dans une séquence d'écriture ou ce qui reste à
lire dans une séquence de lecture.
- la méthode charAt(index)
donne le caractère à cet index
exercices