5. Classes enveloppe

5.1. Associer les types de base à des objets

Les classes enveloppe (appelées en anglais classes wrapper ) sont associées à chacun des type de base. Aux types de base boolean, byte, short, char, int, long, float et double, sont donc associées des classes Boolean, Byte, Short, Character, Integer, Long, Float et Double. Ces classes sont construites sur le même modèle. Elles ont toutes un constructeur qui correspond au type de base qu’elle représente. Par exemple, pour la classe Integer :

Exemple 75. Construction d'un Integer

public Integer(int i) ;

Ensuite, les classes qui représentent des nombres étendent la classe Number, qui propose des méthodes byteValue(), shortValue(), intValue(), longValue(), floatValue() et doubleValue(). Ces méthodes permettent de convertir la valeur numérique portée par l’objet en un type de base explicite. Enfin, ces classes possèdent une méthode statique, parse[TypeDeBase](String s), qui permet de décoder la chaîne de caractères passée en argument. Par exemple, si l’on veut lire un nombre flottant en double précision dans une chaîne de caractères, il suffit d’écrire les lignes suivantes.

Exemple 76. Conversion d'une chaîne de caractères en nombre

String s =  "1.2563556e-3" ;  
 double d = Double.parseDouble(s) ;  
Double dObjet = Double.valueOf(s) ;

Notons que ces appels peuvent générer une exception : NumberFormatException. Les classes wrapper possèdent une méthode toString() prenant en paramètre le type wrappé, par exemple Integer.toString(int). Cette méthode statique permet de convertir un nombre en chaîne de caractères de façon très efficace. Une façon communément utilisée pour faire cette conversion est d'écrire :

Exemple 77. Conversion d'un nombre en chaîne de caractères - 1

int i =  12345 ;  
String s =  "" + i ;

Cette méthode fonctionne, mais, elle est assez peu élégante et il faut éviter de l'utiliser. Il est vrai que la notion d'élégance en programmation est un argument plus que douteux, la plupart du temps utilisé pour tenter de rallier tout de même quelqu'un à son opinion lorsque l'on est à bout d'arguments réels... Disons simplement que la conversion suivante est environ 3 fois plus performante que le pattern précédent, cet argument est nettement meilleur.

Exemple 78. Conversion d'un nombre en chaîne de caractères - 2

int i =  12345 ;  
String s = Integer.toString(i) ;

Ce second argument est beaucoup plus percutant ! Tout comme la classe String, les classes enveloppes sont finales : elles ne peuvent pas être étendues. De plus, la valeur portée par un objet wrapper ne peut pas être modifiée une fois l'objet construit. Ce sont donc des objets immutables. Ce point est très important, car il permet de conserver la cohérence du passage de paramètre par valeur. Observons le cas suivant.

Exemple 79. Importance de l'immutabilité des classes wrapper

public  class Main {  
  
     public  static  void incremente(int i) {  
        i++ ;  
    }  
      
     public  static  void main(String[] args) {  
         int i =  1 ;  
        incremente(i) ;  
        System.out.println("i = " + i) ;  
    }  
}

Le résultat est bien sûr le suivant :
 > i = 1
Effectivement, la méthode incrémente(int) ne possède que la valeur du paramètre. Modifier cette valeur n'a donc pas d'influence sur celle possédée par la méthode main(). Imaginons que l'on réécrive cet exemple simple avec des Integer au lieu d' int. S'il était possible de changer la valeur numérique portée par l'objet de type Integer, alors il serait possible à la méthode incremente(Integer) d'effectivement incrémenter l'objet passé en paramètre. La classe Integer étant immutable, ce n'est pas le cas, et le contrat du passage par valeur est toujours respecté.

5.2. Auto-boxing

L'un des apports de Java 5 dans ce domaine est la notion d'auto-boxing, et d'auto-unboxing. En Java 4, on ne peut constuire un objet wrapper qu'explicitement, par appel à son constructeur ou à une de ses méthodes. En Java 5, il es possible d'écrire ce genre de choses :

Exemple 80. Auto-boxing et auto-unboxing

Integer i =  new Integer(1) ;  // instanciation classique d'un Integer  
Integer j =  1 ;               // conversion automatatique -> boxing  
 int k = i ;                   // conversion automatique -> unboxing

Ces deux opérations sont effectuées lors de la compilation. C'est à la compilation que le code nécessaire à ces conversions est ajouté, puis compilé. On ne gagne donc rien en performance lors de l'exécution : l'auto-boxing n'est qu'une facilité d'écriture. On se méfiera lors de la conversion d'un objet wrapper en type de base : si cet objet est null, alors l'infâme NullPointerException sera jetée.
Java langage & API
Retour au blog Java le soir
Cours & Tutoriaux
Table des matières
Introduction : un peu d'histoire
1. Java : genèse d'un nouveau langage
Programmer en Java
1. Un premier exemple
1.1. Aperçu général, cycle de vie
1.2. Un premier programme
1.3. Programmes, applets, servlets, etc...
2. Une première classe
2.1. Écriture d'une classe
2.2. Instanciation de cette classe
3. Types de base, classes et objets
3.1. Types de base
3.2. Classes et objets
Classes Object et String
1. Introduction
2. La classe Object
2.1. La méthode toString()
2.2. La méthode clone()
2.3. La méthode equals()
2.4. La méthode hashCode()
2.5. La méthode finalize()
2.6. La méthode getClass()
3. La classe String
3.1. Introduction
3.2. Construction d'un objet de type String
3.3. Concaténation, StringBuffer et StringBuilder
3.4. Concaténations de chaînes de caractères depuis Java 8
3.5. Concaténations de chaînes de caractères depuis Java 11
3.6. Extraction d'une sous-chaîne de caractères
3.7. Comparaison de deux chaînes de caractères
3.8. Méthodes de comparaisons lexicographiques
3.9. Méthode de recherche de caractères
3.10. Méthode de modification de chaîne
3.11. Méthode de duplication
3.12. Support de l'unicode, internationalisation
Structure d'une classe
1. Introduction
2. Classes
2.1. Classes publiques
2.2. Classes internes
2.3. Classe membre
2.4. Classes locales
2.5. Classes anonymes
2.6. Le mot-clé this
3. Éléments statiques
3.1. Champ statique
3.2. Cas des constantes
3.3. Bloc statique
3.4. Classe membre statique
4. Membres d'une classe, visibilité
4.1. Bloc non statique
4.2. Accès à un membre, visibilité
4.3. Les champs
4.4. Signature d'une méthode
4.5. Les méthodes
4.6. Getters et Setters
5. Constructeur, instanciation
5.1. Chargement d'une classe
5.2. Constructeurs d'une classe
5.3. Instanciation d'un objet
5.4. Destruction d'objets
5.5. Le mot-clé final
6. Énumérations
6.1. Déclaration d'une énumération
6.2. Classe énumération
6.3. Méthode toString()
6.4. Méthode valueOf()
6.5. Méthode values()
6.6. Méthode ordinal()
6.7. Méthode compareTo()
6.8. Constructeurs privés
6.9. Classe utilitaire : EnumSet
Noms, opérateurs, tableaux
1. Introduction
2. Identificateurs, noms et expressions
2.1. Identificateurs et noms
2.2. Expressions
3. Opérateurs, ordre d'exécution
3.1. Ordre d'exécution
3.2. Les opérateurs
3.3. Les opérateurs ++ et --
3.4. Les opérateurs % et /
3.5. Les opérateurs <<, >> et >>>
3.6. L'opérateur instanceof
3.7. Les opérateurs &, | et ^
3.8. Les opérateurs && et ||
3.9. L'opérateur ? ... :
3.10. Les opérateurs d'affectation
4. Tableaux
4.1. Création d'un tableau
4.2. Initialisation d'un tableau
4.3. Utilisation d'un tableau comme un Object
4.4. Tableaux de tableaux
4.5. Copie de tableaux
5. Blocs, boucles et contrôles
5.1. Blocs
5.2. Mots-clés réservés
5.3. Tests : if et switch
5.4. Boucles : for, while, do ... while
5.5. Commandes continue et break
5.6. Commandes return et goto
Nombres, précision, calculs
1. Introduction
2. Calculs
2.1. Précision
2.2. Codage des nombres flottants
2.3. Le mot-clé strictfp
2.4. Conversion de types
3. Dépassements de capacité
3.1. Cas des entiers
3.2. Cas des flottants
3.3. Bibliothèques BigInteger et BigDecimal
4. Fonctions mathématiques
4.1. Fonctions usuelles
4.2. Générateurs aléatoires
5. Classes enveloppe
5.1. Associer les types de base à des objets
5.2. Auto-boxing
Héritage, abstraction, interfaces
1. Introduction
2. Abstraction et encapsulation
2.1. Abstraction
2.2. Encapsulation
3. Héritage
3.1. Définition de l'héritage
3.2. Conséquences pour les membres
3.3. Polymorphisme
3.4. Empêcher l'héritage
4. Classes abstraites
5. Interfaces
5.1. Introduction
5.2. Définition
5.3. Java 8 et les interfaces
5.4. Utilisation des interfaces
5.5. Définition de constantes dans les interfaces
5.6. Utilité des interfaces
Packages
1. Introduction
2. Notion de paquet
2.1. Déclaration d’appartenance à un paquet
2.2. Chargement d’une classe
2.3. Choix de nom
3. Archives, chemin de recherche, classpath
3.1. Archives
3.2. Variable CLASSPATH
3.3. Notion de classloader
3.4. Bilan sur les classes chargées
3.5. Visibilité
3.6. Conseils d'écriture, bibliothèque standard
Exceptions
1. Introduction
2. Erreurs et Exceptions
2.1. Classe Throwable, notion de stack trace
2.2. Classe Error
2.3. Classe RuntimException
2.4. Classe Exception
3. Déclenchement d'une exception
3.1. Exceptions déclenchées par la JVM
3.2. Exceptions déclenchées par l'application
4. Capter une exception
4.1. Traiter une exception localement
4.2. Code de captage
5. Créer ses propres exceptions
Entrées / sorties
1. Introduction
2. Notion de fichier
2.1. Introduction
2.2. La classe File
2.3. Construction d'une instance de File
2.4. Méthodes exposées
3. Flux de sortie
3.1. Introduction, notion de flux
3.2. Écriture de caractères, classe Writer
3.3. Bufferisation, construction d'un flux sur un autre
3.4. Utilisation de PrintWriter
3.5. Écriture d'octets, OuputStream
3.6. Écriture de types primitifs : DataOutputStream
3.7. Écriture d'objets : ObjectOutputStream
4. Flux d'entrée
4.1. Introduction
4.2. Lecture de caractères, classe Reader
4.3. Bufferisation, lecture ligne par ligne
4.4. Lecture d'octets : InputStream
4.5. Lecture de types primitifs : DataInputStream
4.6. Lecture d'objets : ObjectInputStream
5. Lecture et écriture de flux croisés
5.1. Introduction
5.2. Lire des caractères sur un flux binaire : classe InputStreamReader
5.3. Écrire des caractères sur un flux binaire : classe OutputStreamWriter
5.4. Remarque sur les jeux de caractères
6. Serialization d'objets
6.1. Enjeu de la sérialization d'objets
6.2. Serialization d'un objet
6.3. Sérialization d'une grappe d'objets
6.4. Première surcharge : méthode writeObject() readObject()
6.5. Deuxième surcharge : utilisation d'un externalizer
6.6. Troisième surcharge : utilisation d'un objet proxy
7. Flux compressés
7.1. Introduction
7.2. Flux de type gzip
7.3. Flux de type zip