4. Classes abstraites

De la même façon qu'il est possible d'empêcher quelqu'un d'étendre une classe, ou de surcharger une méthode, il est possible de forcer l'extension ou la surcharge, en utilisant le mot-clé abstract. Formellement une classe abstraite n'est pas différente d'une classe normale. Simplement, elle est déclarée en ajoutant le mot-clé abstract, comme dans l'exemple suivant.

Exemple 86. Classe abstraite

public  abstract  class A {  // déclarée dans le fichier A.java  
  
     public  abstract  void ouSuisJe() ;  // pas de corps de méthode  
  
}

On peut déclarer autant de méthodes abstraites que l'on veut dans une classe abstraite, y compris ne pas en déclarer du tout. Une méthode abstraite, comme dans notre exemple, ne comporte qu'une déclaration de signature ; elle n'a pas de corps. On peut alors se poser la question : comment fait-on pour exéctuter une telle méthode ? La réponse est simple : on ne peut pas l'exécuter en l'état. Une classe abstraite ne peut pas être instanciée. Il est nécessaire de créer une classe concrète (c'est-à-dire non abstraite !) qui l'étende, et d'instancier cette classe concrète. Toute classe concrête qui étend une classe abstraite doit fournir un corps de méthode (on parle alors d'implémentation, un beau mot de la langue française...) pour toutes les méthodes abstraites de cette classe abstraite. Dans notre exemple, on peut alors écrire le code de l'exemple suivant.

Exemple 87. Extension d'une classe abstraite - 1

public  class B  extends A {  // déclarée dans le fichier B.java  
  
     public  void ouSuisJe() {  
        System.out.println("J'y suis j'y reste !") ;  
    }  
  
}

D'une façon générale :
  • une classe abstraite peut étendre une autre classe abstraite ;
  • une classe abstraite peut étendre une classe concrète ;
  • une classe concrète qui étend une ou plusieurs classes abstraites (indirectement), doit obligatoirement fournir une implémentation pour toutes les méthodes abstraites existantes.
Voyons ceci sur un exemple.

Exemple 88. Extension d'une classe abstraite - 2

public  abstract  class A {  
     public  abstract  void ouSuisJe() ;  
}  
  
 public  abstract  class B  extends A {  
     public  void ouSuisJe() {  
        System.out.println("Il est passé par ici") ;  
    }  
}  
  
 public  abstract  class C  extends B {  
     public  abstract  void ouVasTu() ;  
}  
  
 public  class D  extends C {  
     public  void ouVasTu() {  
        System.out.println("J'y retourne") ;  
    }  
}

Dans notre exemple :
  • on ne peut pas instancier A, qui est abstraite ;
  • on ne peut pas non plus instancier B, car même si elle n'a pas de méthode abstraite, elle est tout de même déclarée abstraite ;
  • on ne peut pas instancier C ;
  • on peut instancier D, qui possède bien une implémentation pour ouSuisJe() (par extension de B), et pour ouVasTu().
Les classes abstraites sont très utilisées dans l'API standard. La partie graphique (Swing) en possède de nombreuses. On peut énoncer deux principes pour justifier la création d'une classe abstraite :
  • la classe que l'on écrit va être étendue par plusieurs classes ;
  • ces différentes classes vont être utilisées par des méthodes communes, et seront déclarées par le nom de la super classe abstraite.
Si ces deux conditions ne sont pas réunies, alors écrire une classe abstraite ne présente aucun intérêt.
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