Exercice : Indiquer les réponses correctes. Correcte ?
Une classe est une instance d'un objet. ✘
Dans une méthode, il est possible d'accéder à l'instance courante à l'aide du mot-clé this. ✔
Le mot-clé private permet de garantir qu'une variable ne pourra être accédée que par toutes les instances de la classe à laquelle elle appartient. ✔
Il est possible de déclarer des variables dans une interface si elles sont déclarées final. ✔
Il est impossible d'appeler un constructeur à partir d'un autre constructeur de la même classe. ✘
Il faut toujours définir un constructeur sans paramètre dans une classe. ✘
Une classe mère ne peut pas définir de membres protected. ✘
Une classe fille a accès à tous les membres de ses super-classes. ✘
En Java, toutes les classes héritent de la classe Object. ✔
Les variables statiques sont accessibles uniquement à partir de méthodes statiques. ✘
Une classe abstraite ne peut hériter d'une autre classe abstraite. ✘
Une classe abstraite doit définir au moins une méthode abstraite. ✘
Une méthode abstraite peut être appelée dans la classe qui la définit. ✔
Les exceptions sont des objets. ✔
Les méthodes de la classe String ne modifient jamais le contenu du receveur. ✔
EXERCICE 2 :
Expliquer la différence entre le concept de liaison dynamique et celui de liaison statique. Illustrer votre propos par deux exemples de code (un pour chaque type de liaison).
La liaison dynamique (ou liaison tardive) et la liaison statique (ou liaison précoce) sont deux concepts liés à la résolution des méthodes en programmation orientée objet.
Liaison dynamique :
La résolution de la méthode se fait pendant l'exécution du programme.
Le choix de la méthode à appeler dépend du type réel de l'objet.
Exemple de code :
class Animal {
void faireSon() {
System.out.println("Certains animaux font du bruit");
}
}
class Chien extends Animal {
void faireSon() {
System.out.println("Le chien aboie");
}
}
public class Genius {
public static void main(String[] args) {
Animal monAnimal = new Chien();
monAnimal.faireSon(); // Appelle la méthode faireSon() de Chien
}
}
Liaison statique :
La résolution de la méthode se fait lors de la compilation.
Le choix de la méthode à appeler dépend du type déclaré de la référence.
Exemple de code :
class Animal {
void faireSon() {
System.out.println("Certains animaux font du bruit");
}
}
class Chien extends Animal {
void faireSon() {
System.out.println("Le chien aboie");
}
}
public class Genius {
public static void main(String[] args) {
Animal monAnimal = new Animal();
monAnimal.faireSon(); // Appelle la méthode faireSon() de Animal
}
}
Dans le premier exemple, avec la liaison dynamique, la méthode appelée dépend du type réel de l'objet, tandis que dans le deuxième exemple, avec la liaison statique, la méthode appelée dépend du type déclaré de la référence.
EXERCICE 3 : Expliquer la difference entre une exception verifiee et une exception non-verifiee. Indiquer dans quel cas il est convient d’utiliser l’une ou l’autre. donne des exemples ( Classe des enitier, EntierException) :
reponse : Les exceptions vérifiées (checked exceptions) et les exceptions non vérifiées (unchecked exceptions) sont deux types d'exceptions en Java, et leur distinction réside dans le fait que le compilateur impose ou non leur gestion explicite.
Exceptions Vérifiées (Checked Exceptions) :
Ce sont des exceptions qui héritent de la classe Exception (mais pas de RuntimeException).
Le compilateur oblige à gérer ces exceptions, soit en utilisant une clause throws dans la signature de la méthode, soit en utilisant un bloc try-catch pour les attraper.
Elles sont généralement utilisées pour des situations où l'application peut anticiper et traiter l'erreur.
Exemple avec une classe d'entier (EntierException est supposée être une exception vérifiée) :
public class EntierException extends Exception {
public EntierException(String message) {
super(message);
}
public static void main(String[] args) {
try {
verifierEntier(10);
verifierEntier(-5); // Cela générera une EntierException
} catch (EntierException e) {
System.out.println("Exception vérifiée : " + e.getMessage());
}
}
public static void verifierEntier(int entier) throws EntierException {
if (entier < 0) {
throw new EntierException("Entier négatif non autorisé");
}
// Autres traitements si l'entier est valide
}
}
Exceptions Non Vérifiées (Unchecked Exceptions) :
Ce sont des exceptions qui héritent de RuntimeException.
Le compilateur n'impose pas explicitement la gestion de ces exceptions.
Elles sont généralement utilisées pour des erreurs de programmation (bug) ou des situations imprévues.
Exemple avec une classe d'entier (ArithmeticException est une exception non vérifiée) :
public class EntierNonVerifie {
public static void main(String[] args) {
int resultat = diviser(10, 0); // Cela générera une ArithmeticException
System.out.println("Résultat : " + resultat);
}
public static int diviser(int a, int b) {
return a / b; // Tentative de division par zéro
}
}
En gros on utilise des exceptions vérifiées lorsque l'on veut forcer la gestion explicite des erreurs dans le code, tandis que les exceptions non vérifiées sont généralement réservées aux erreurs de programmation et aux situations imprévues.
Exercice 4 : Donner deux mesures qu’il est recommande de mettre en place pour preserver l’encapsulation au sein d’une classe. Illustrer vos propos en donnant un exemple de code java. Expliquer quel peut etre l’interet de definir une classe abstraite
Solution :
Pour préserver l'encapsulation au sein d'une classe en Java, il est recommandé de suivre les pratiques suivantes :
Déclarer les variables d'instance comme privées :
En déclarant les variables d'instance comme privées, on limite l'accès direct à ces variables depuis l'extérieur de la classe.
L'accès aux données se fait via des méthodes d'accès (getters et setters), ce qui permet un contrôle total sur la manière dont les données sont manipulées.
Utiliser des méthodes d'accès (getters et setters) :
L'utilisation de méthodes d'accès permet de fournir un moyen contrôlé d'accéder et de modifier les données encapsulées.
Les getters permettent de lire la valeur d'une variable d'instance, tandis que les setters permettent de modifier cette valeur tout en appliquant des vérifications si nécessaire.
Illustrons ces principes avec un exemple de code Java :
public class Genius { // Variables d'instance privées private String nom;
private int age; // Constructeur publicGenius(String nom, int age) { this.nom = nom; this.age = age; }
// Méthodes d'accès pour la variable "nom" public String getNom() { return nom; } public void setNom(String nom) { // On peut ajouter des vérifications ici si nécessaire this.nom = nom; } // Méthodes d'accès pour la variable "age" public int getAge() { return age; } public void setAge(int age) { // On peut ajouter des vérifications ici si nécessaire this.age = age; } // Autres méthodes de la classe... }
Dans cet exemple, les variables nom et age sont privées, et l'accès à ces données se fait à travers les méthodes getNom, setNom, getAge, et setAge. Cela permet de contrôler l'accès et la modification des données encapsulées.
Quant à la définition d'une classe abstraite, voici son intérêt :
Intérêt de définir une classe abstraite :
Une classe abstraite peut contenir à la fois des méthodes abstraites (sans implémentation) et des méthodes concrètes (avec implémentation).
Elle sert souvent de classe de base pour d'autres classes (sous-classes) qui étendent son comportement.
Les méthodes abstraites dans une classe abstraite sont implémentées par les sous-classes, favorisant ainsi la polymorphie et la réutilisation du code.
Illustrons cela avec un exemple simple :
public abstract class Forme {
// Méthode abstraite à implémenter par les sous-classes
public abstract double calculerSurface();
// Méthode concrète partagée par toutes les sous-classes
public void afficherType() {
System.out.println("Type de forme : " + this.getClass().getSimpleName());
}
}
// Exemple de sous-classe
public class Cercle extends Forme {
private double rayon;
public Cercle(double rayon) {
this.rayon = rayon;
}
@Override
public double calculerSurface() {
return Math.PI * Math.pow(rayon, 2);
}
}
// Exemple d'utilisation
public class Main {
public static void main(String[] args) {
Forme cercle = new Cercle(5.0);
cercle.afficherType();
System.out.println("Surface du cercle : " + cercle.calculerSurface());
}
}
Dans cet exemple, la classe abstraite Forme définit une méthode abstraite calculerSurface() que les sous-classes, comme Cercle, doivent implémenter. La classe abstraite fournit également une méthode concrète afficherType(), qui est partagée par toutes les sous-classes. L'utilisation de cette hiérarchie de classes permet une structure plus modulaire et extensible.